diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 76581725b80642815fbac8ad9c8f88649a9a1d4c..7f7464879ba6c24ee855d47fe6792bef0a9bb8c8 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -689,6 +689,19 @@ SchemeModule::registerOperators() const void SchemeModule::registerCheckpointResume() const { + CheckpointResumeRepository::instance() + .addCheckpointResume(ast_node_data_type_from<std::shared_ptr<const DiscreteFunctionVariant>>, + std::function([](const std::string& symbol_name, const EmbeddedData& embedded_data, + HighFive::File& file, HighFive::Group& checkpoint_group, + HighFive::Group& symbol_table_group) { + writeDiscreteFunctionVariant(symbol_name, embedded_data, file, checkpoint_group, + symbol_table_group); + }), + std::function([](const std::string& symbol_name, + const HighFive::Group& symbol_table_group) -> EmbeddedData { + return readDiscreteFunctionVariant(symbol_name, symbol_table_group); + })); + CheckpointResumeRepository::instance() .addCheckpointResume(ast_node_data_type_from<std::shared_ptr<const IBoundaryConditionDescriptor>>, std::function([](const std::string& symbol_name, const EmbeddedData& embedded_data, diff --git a/src/utils/checkpointing/CheckpointUtils.cpp b/src/utils/checkpointing/CheckpointUtils.cpp index afb542d7a9477d876751566396eb69b1e6b0839d..519b5a29cf805d4bb701ebb47cbefc892632d1d7 100644 --- a/src/utils/checkpointing/CheckpointUtils.cpp +++ b/src/utils/checkpointing/CheckpointUtils.cpp @@ -18,6 +18,7 @@ #include <mesh/NumberedZoneDescriptor.hpp> #include <scheme/DiscreteFunctionP0.hpp> #include <scheme/DiscreteFunctionP0Vector.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> #include <scheme/IBoundaryConditionDescriptor.hpp> #include <utils/checkpointing/DiscreteFunctionTypeHFType.hpp> #include <utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp> @@ -409,20 +410,8 @@ writeIZoneDescriptor(const std::string& symbol_name, } void -writeMesh(const std::string& symbol_name, - const EmbeddedData& embedded_data, - HighFive::File& file, - HighFive::Group& checkpoint_group, - HighFive::Group& symbol_table_group) +writeMesh(std::shared_ptr<const MeshVariant> mesh_v, HighFive::File& file, HighFive::Group& checkpoint_group) { - HighFive::Group variable_group = symbol_table_group.createGroup("embedded/" + symbol_name); - - std::shared_ptr<const MeshVariant> mesh_v = - dynamic_cast<const DataHandler<const MeshVariant>&>(embedded_data.get()).data_ptr(); - - variable_group.createAttribute("type", dataTypeName(ast_node_data_type_from<decltype(mesh_v)>)); - variable_group.createAttribute("id", mesh_v->id()); - std::string mesh_group_name = "mesh/" + std::to_string(mesh_v->id()); if (not checkpoint_group.exist(mesh_group_name)) { bool linked = false; @@ -463,6 +452,61 @@ writeMesh(const std::string& symbol_name, mesh_v->variant()); } +void +writeMesh(const std::string& symbol_name, + const EmbeddedData& embedded_data, + HighFive::File& file, + HighFive::Group& checkpoint_group, + HighFive::Group& symbol_table_group) +{ + HighFive::Group variable_group = symbol_table_group.createGroup("embedded/" + symbol_name); + + std::shared_ptr<const MeshVariant> mesh_v = + dynamic_cast<const DataHandler<const MeshVariant>&>(embedded_data.get()).data_ptr(); + + variable_group.createAttribute("type", dataTypeName(ast_node_data_type_from<decltype(mesh_v)>)); + variable_group.createAttribute("id", mesh_v->id()); + + writeMesh(mesh_v, file, checkpoint_group); +} + +void +writeDiscreteFunctionVariant(const std::string& symbol_name, + const EmbeddedData& embedded_data, + HighFive::File& file, + HighFive::Group& checkpoint_group, + HighFive::Group& symbol_table_group) +{ + HighFive::Group variable_group = symbol_table_group.createGroup("embedded/" + symbol_name); + + std::shared_ptr<const DiscreteFunctionVariant> discrete_function_v = + dynamic_cast<const DataHandler<const DiscreteFunctionVariant>&>(embedded_data.get()).data_ptr(); + + variable_group.createAttribute("type", dataTypeName(ast_node_data_type_from<decltype(discrete_function_v)>)); + + std::visit( + [&](auto&& discrete_function) { + auto mesh_v = discrete_function.meshVariant(); + using DFType = std::decay_t<decltype(discrete_function)>; + variable_group.createAttribute("Vh_type", discrete_function.descriptor().type()); + + variable_group.createAttribute("mesh_id", mesh_v->id()); + writeMesh(mesh_v, file, checkpoint_group); + if constexpr (is_discrete_function_P0_v<DFType>) { + using data_type = std::decay_t<typename DFType::data_type>; + // variable_group.createAttribute("Vh_type", "P0"); + variable_group.createAttribute("data_type", dataTypeName(ast_node_data_type_from<data_type>)); + write(variable_group, "values", discrete_function.cellValues()); + } else if constexpr (is_discrete_function_P0_vector_v<DFType>) { + using data_type = std::decay_t<typename DFType::data_type>; + // variable_group.createAttribute("Vh_type", "P0Vector"); + variable_group.createAttribute("data_type", dataTypeName(ast_node_data_type_from<data_type>)); + // write(variable_group, "values", discrete_function.cellArrays()); + } + }, + discrete_function_v->discreteFunction()); +} + void writeOStream(const std::string& symbol_name, const EmbeddedData& embedded_data, diff --git a/src/utils/checkpointing/CheckpointUtils.hpp b/src/utils/checkpointing/CheckpointUtils.hpp index 0870184203c36bce615818d138cf29ff3e215ed3..5a5fe066d9bd457cb603ae8c3f4af9cf800e6dea 100644 --- a/src/utils/checkpointing/CheckpointUtils.hpp +++ b/src/utils/checkpointing/CheckpointUtils.hpp @@ -42,6 +42,12 @@ write(HighFive::Group& group, write(group, name, item_value.arrayView()); } +void writeDiscreteFunctionVariant(const std::string& symbol_name, + const EmbeddedData& embedded_data, + HighFive::File& file, + HighFive::Group& checkpoint_group, + HighFive::Group& symbol_table_group); + void writeIBoundaryConditionDescriptor(const std::string& symbol_name, const EmbeddedData& embedded_data, HighFive::File& file, diff --git a/src/utils/checkpointing/ResumeUtils.cpp b/src/utils/checkpointing/ResumeUtils.cpp index 125fe1f9c753f8038e013a12ab334d560d24a147..367372bd4ba3469847f6db88b60320279ee223d1 100644 --- a/src/utils/checkpointing/ResumeUtils.cpp +++ b/src/utils/checkpointing/ResumeUtils.cpp @@ -12,9 +12,21 @@ #include <mesh/NumberedBoundaryDescriptor.hpp> #include <mesh/NumberedInterfaceDescriptor.hpp> #include <mesh/NumberedZoneDescriptor.hpp> +#include <scheme/AxisBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> #include <scheme/DiscreteFunctionDescriptorP0.hpp> #include <scheme/DiscreteFunctionDescriptorP0Vector.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/ExternalBoundaryConditionDescriptor.hpp> +#include <scheme/FixedBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/FreeBoundaryConditionDescriptor.hpp> #include <scheme/IBoundaryConditionDescriptor.hpp> +#include <scheme/InflowBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/OutflowBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> #include <utils/checkpointing/DiscreteFunctionTypeHFType.hpp> #include <utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp> #include <utils/checkpointing/IBoundaryDescriptorHFType.hpp> @@ -25,16 +37,19 @@ #include <utils/checkpointing/QuadratureTypeHFType.hpp> #include <utils/checkpointing/ResumingData.hpp> -#include <scheme/AxisBoundaryConditionDescriptor.hpp> -#include <scheme/DirichletBoundaryConditionDescriptor.hpp> -#include <scheme/ExternalBoundaryConditionDescriptor.hpp> -#include <scheme/FixedBoundaryConditionDescriptor.hpp> -#include <scheme/FourierBoundaryConditionDescriptor.hpp> -#include <scheme/FreeBoundaryConditionDescriptor.hpp> -#include <scheme/InflowBoundaryConditionDescriptor.hpp> -#include <scheme/NeumannBoundaryConditionDescriptor.hpp> -#include <scheme/OutflowBoundaryConditionDescriptor.hpp> -#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +template <typename T, ItemType item_type> +ItemValue<T, item_type> +readItemValue(const HighFive::Group& group, const std::string& name, const IConnectivity& connectivity) +{ + return {connectivity, readArray<T>(group, name)}; +} + +template <typename T, ItemType item_type> +ItemArray<T, item_type> +readItemArray(const HighFive::Group& group, const std::string& name, const IConnectivity& connectivity) +{ + return {connectivity, readTable<T>(group, name)}; +} std::shared_ptr<const IBoundaryDescriptor> readIBoundaryDescriptor(const HighFive::Group& iboundarydescriptor_group) @@ -269,6 +284,81 @@ readMesh(const std::string& symbol_name, const HighFive::Group& symbol_table_gro return {std::make_shared<DataHandler<const MeshVariant>>(ResumingData::instance().meshVariant(mesh_id))}; } +EmbeddedData +readDiscreteFunctionVariant(const std::string& symbol_name, const HighFive::Group& symbol_table_group) +{ + const HighFive::Group discrete_function_group = symbol_table_group.getGroup("embedded/" + symbol_name); + DiscreteFunctionType type = discrete_function_group.getAttribute("Vh_type").read<DiscreteFunctionType>(); + size_t mesh_id = discrete_function_group.getAttribute("mesh_id").read<size_t>(); + + std::shared_ptr<const MeshVariant> mesh_v = ResumingData::instance().meshVariant(mesh_id); + + const std::string data_type = discrete_function_group.getAttribute("data_type").read<std::string>(); + + std::shared_ptr<DiscreteFunctionVariant> p_discrete_function; + switch (type) { + case DiscreteFunctionType::P0: { + if (data_type == dataTypeName(ast_node_data_type_from<double>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const double>(mesh_v, + readItemValue<double, ItemType::cell>(discrete_function_group, "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyVector<1>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyVector<1>>(mesh_v, + readItemValue<TinyVector<1>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyVector<2>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyVector<2>>(mesh_v, + readItemValue<TinyVector<2>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyVector<3>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyVector<3>>(mesh_v, + readItemValue<TinyVector<3>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyMatrix<1>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyMatrix<1>>(mesh_v, + readItemValue<TinyMatrix<1>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyMatrix<2>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyMatrix<2>>(mesh_v, + readItemValue<TinyMatrix<2>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else if (data_type == dataTypeName(ast_node_data_type_from<TinyMatrix<3>>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyMatrix<3>>(mesh_v, + readItemValue<TinyMatrix<3>, ItemType::cell>(discrete_function_group, + "values", + mesh_v->connectivity()))); + } else { + throw UnexpectedError("unexpected discrete function data type: " + data_type); + } + break; + } + case DiscreteFunctionType::P0Vector: { + if (data_type == dataTypeName(ast_node_data_type_from<double>)) { + p_discrete_function = std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0Vector<const double>(mesh_v, + readItemArray<double, ItemType::cell>(discrete_function_group, "values", + mesh_v->connectivity()))); + } else { + throw UnexpectedError("unexpected discrete function vector data type: " + data_type); + } + break; + } + } + return {std::make_shared<DataHandler<const DiscreteFunctionVariant>>(p_discrete_function)}; +} + EmbeddedData readOStream(const std::string& symbol_name, const HighFive::Group& symbol_table_group) { diff --git a/src/utils/checkpointing/ResumeUtils.hpp b/src/utils/checkpointing/ResumeUtils.hpp index c66f48207eaf073f5b5ab09f66c5715837f0de82..1ade7973f9c09d3c9bfca4c374958f947a96e2fb 100644 --- a/src/utils/checkpointing/ResumeUtils.hpp +++ b/src/utils/checkpointing/ResumeUtils.hpp @@ -5,11 +5,12 @@ #include <language/utils/SymbolTable.hpp> #include <mesh/CellType.hpp> +#include <mesh/ItemArray.hpp> #include <mesh/ItemValue.hpp> template <typename DataType> PUGS_INLINE Array<DataType> -read(const HighFive::Group& group, const std::string& name) +readArray(const HighFive::Group& group, const std::string& name) { using data_type = std::remove_const_t<DataType>; @@ -28,6 +29,28 @@ read(const HighFive::Group& group, const std::string& name) return array; } +template <typename DataType> +PUGS_INLINE Table<DataType> +readTable(const HighFive::Group& group, const std::string& name) +{ + using data_type = std::remove_const_t<DataType>; + + auto dataset = group.getDataSet(name); + Table<DataType> table(dataset.getDimensions()[0], dataset.getDimensions()[1]); + if constexpr (std::is_same_v<CellType, data_type>) { + dataset.template read<short>(reinterpret_cast<short*>(&(table(0, 0)))); + } else if constexpr ((std::is_same_v<CellId, data_type>) or (std::is_same_v<FaceId, data_type>) or + (std::is_same_v<EdgeId, data_type>) or (std::is_same_v<NodeId, data_type>)) { + using base_type = typename data_type::base_type; + dataset.template read<base_type>(reinterpret_cast<base_type*>(&(table(0, 0)))); + } else { + dataset.template read<data_type>(&(table(0, 0))); + } + + return table; +} + +EmbeddedData readDiscreteFunctionVariant(const std::string& symbol_name, const HighFive::Group& symbol_table_group); EmbeddedData readIBoundaryConditionDescriptor(const std::string& symbol_name, const HighFive::Group& symbol_table_group); EmbeddedData readIBoundaryDescriptor(const std::string& symbol_name, const HighFive::Group& symbol_table_group); diff --git a/src/utils/checkpointing/ResumingData.cpp b/src/utils/checkpointing/ResumingData.cpp index 5743e98fd04185018b67a2f40ffb6f85caa7b70d..86dd4428919dce9e6cc6b1dfbf1dd735b527bb89 100644 --- a/src/utils/checkpointing/ResumingData.cpp +++ b/src/utils/checkpointing/ResumingData.cpp @@ -31,59 +31,59 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) } ConnectivityDescriptor descriptor; - descriptor.setCellTypeVector(read<CellType>(connectivity_data, "cell_type")); - descriptor.setCellNumberVector(read<int>(connectivity_data, "cell_numbers")); - descriptor.setNodeNumberVector(read<int>(connectivity_data, "node_numbers")); + descriptor.setCellTypeVector(readArray<CellType>(connectivity_data, "cell_type")); + descriptor.setCellNumberVector(readArray<int>(connectivity_data, "cell_numbers")); + descriptor.setNodeNumberVector(readArray<int>(connectivity_data, "node_numbers")); - descriptor.setCellOwnerVector(read<int>(connectivity_data, "cell_owner")); - descriptor.setNodeOwnerVector(read<int>(connectivity_data, "node_owner")); + descriptor.setCellOwnerVector(readArray<int>(connectivity_data, "cell_owner")); + descriptor.setNodeOwnerVector(readArray<int>(connectivity_data, "node_owner")); using index_type = typename ConnectivityMatrix::IndexType; descriptor.setCellToNodeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "cell_to_node_matrix_rowsMap"), - read<index_type>(connectivity_data, "cell_to_node_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "cell_to_node_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "cell_to_node_matrix_values")}); if (dimension > 1) { - descriptor.setFaceNumberVector(read<int>(connectivity_data, "face_numbers")); - descriptor.setFaceOwnerVector(read<int>(connectivity_data, "face_owner")); + descriptor.setFaceNumberVector(readArray<int>(connectivity_data, "face_numbers")); + descriptor.setFaceOwnerVector(readArray<int>(connectivity_data, "face_owner")); descriptor.setCellToFaceMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "cell_to_face_matrix_rowsMap"), - read<index_type>(connectivity_data, "cell_to_face_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "cell_to_face_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "cell_to_face_matrix_values")}); descriptor.setFaceToNodeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "face_to_node_matrix_rowsMap"), - read<index_type>(connectivity_data, "face_to_node_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "face_to_node_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "face_to_node_matrix_values")}); descriptor.setNodeToFaceMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "node_to_face_matrix_rowsMap"), - read<index_type>(connectivity_data, "node_to_face_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "node_to_face_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "node_to_face_matrix_values")}); - descriptor.setCellFaceIsReversed(read<bool>(connectivity_data, "cell_face_is_reversed")); + descriptor.setCellFaceIsReversed(readArray<bool>(connectivity_data, "cell_face_is_reversed")); } if (dimension > 2) { - descriptor.setEdgeNumberVector(read<int>(connectivity_data, "edge_numbers")); - descriptor.setEdgeOwnerVector(read<int>(connectivity_data, "edge_owner")); + descriptor.setEdgeNumberVector(readArray<int>(connectivity_data, "edge_numbers")); + descriptor.setEdgeOwnerVector(readArray<int>(connectivity_data, "edge_owner")); descriptor.setCellToEdgeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "cell_to_edge_matrix_rowsMap"), - read<index_type>(connectivity_data, "cell_to_edge_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "cell_to_edge_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "cell_to_edge_matrix_values")}); descriptor.setFaceToEdgeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "face_to_edge_matrix_rowsMap"), - read<index_type>(connectivity_data, "face_to_edge_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "face_to_edge_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "face_to_edge_matrix_values")}); descriptor.setEdgeToNodeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "edge_to_node_matrix_rowsMap"), - read<index_type>(connectivity_data, "edge_to_node_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "edge_to_node_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "edge_to_node_matrix_values")}); descriptor.setNodeToEdgeMatrix( - ConnectivityMatrix{read<index_type>(connectivity_data, "node_to_edge_matrix_rowsMap"), - read<index_type>(connectivity_data, "node_to_edge_matrix_values")}); + ConnectivityMatrix{readArray<index_type>(connectivity_data, "node_to_edge_matrix_rowsMap"), + readArray<index_type>(connectivity_data, "node_to_edge_matrix_values")}); - descriptor.setFaceEdgeIsReversed(read<bool>(connectivity_data, "face_edge_is_reversed")); + descriptor.setFaceEdgeIsReversed(readArray<bool>(connectivity_data, "face_edge_is_reversed")); } if (connectivity_data.exist("item_ref_list")) { @@ -102,16 +102,16 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) if (item_type_name == "cell") { descriptor.addRefItemList( - RefItemList<ItemType::cell>{ref_id, read<CellId>(item_ref_list_data, "list"), ref_item_list_type}); + RefItemList<ItemType::cell>{ref_id, readArray<CellId>(item_ref_list_data, "list"), ref_item_list_type}); } else if (item_type_name == "face") { descriptor.addRefItemList( - RefItemList<ItemType::face>{ref_id, read<FaceId>(item_ref_list_data, "list"), ref_item_list_type}); + RefItemList<ItemType::face>{ref_id, readArray<FaceId>(item_ref_list_data, "list"), ref_item_list_type}); } else if (item_type_name == "edge") { descriptor.addRefItemList( - RefItemList<ItemType::edge>{ref_id, read<EdgeId>(item_ref_list_data, "list"), ref_item_list_type}); + RefItemList<ItemType::edge>{ref_id, readArray<EdgeId>(item_ref_list_data, "list"), ref_item_list_type}); } else if (item_type_name == "node") { descriptor.addRefItemList( - RefItemList<ItemType::node>{ref_id, read<NodeId>(item_ref_list_data, "list"), ref_item_list_type}); + RefItemList<ItemType::node>{ref_id, readArray<NodeId>(item_ref_list_data, "list"), ref_item_list_type}); } else { throw UnexpectedError("invalid item type: " + item_type_name); } @@ -172,7 +172,7 @@ ResumingData::_readPolygonalMesh(const HighFive::Group& mesh_data) std::shared_ptr<const Connectivity<Dimension>> connectivity = std::dynamic_pointer_cast<const Connectivity<Dimension>>(i_connectivity); - Array<const TinyVector<Dimension>> xr_array = read<TinyVector<Dimension>>(mesh_data, "xr"); + Array<const TinyVector<Dimension>> xr_array = readArray<TinyVector<Dimension>>(mesh_data, "xr"); NodeValue<const TinyVector<Dimension>> xr{*connectivity, xr_array}; std::shared_ptr mesh = std::make_shared<const Mesh<Dimension>>(connectivity, xr);