diff --git a/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.cpp b/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.cpp index 7555f4b8f5cba0046de1d62e65b287a69ac31047..9fc76aac8b3bd320a5dd5a88ba8a98dc582a036a 100644 --- a/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.cpp +++ b/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.cpp @@ -17,6 +17,7 @@ #include <limits> #include <iomanip> #include <cstdio> // for binary output +#include <numeric> #include <logog/include/logog.hpp> @@ -24,7 +25,6 @@ #include "MeshLib/IO/VtkIO/VtuInterface.h" -#include "MeshLib/Node.h" #include "MeshLib/Elements/Element.h" namespace ApplicationUtils @@ -84,106 +84,173 @@ void NodeWiseMeshPartitioner::readMetisData(const std::string& file_name_base) std::remove(fname_eparts.c_str()); } -void NodeWiseMeshPartitioner::partitionByMETIS( - const bool is_mixed_high_order_linear_elems) +void NodeWiseMeshPartitioner::findNonGhostNodesInPartition( + std::size_t const part_id, + const bool is_mixed_high_order_linear_elems, + std::vector<MeshLib::Node*>& extra_nodes) { std::vector<MeshLib::Node*> const& nodes = _mesh->getNodes(); - for (std::size_t part_id = 0; part_id < _partitions.size(); part_id++) + auto& partition = _partitions[part_id]; + // -- Extra nodes for high order elements + for (std::size_t i = 0; i < _mesh->getNumberOfNodes(); i++) { - auto& partition = _partitions[part_id]; + if (_nodes_partition_ids[i] == part_id) + { + splitOfHigherOrderNode(nodes, is_mixed_high_order_linear_elems, + i, partition.nodes, extra_nodes); + } + } + partition.number_of_non_ghost_base_nodes = partition.nodes.size(); + partition.number_of_non_ghost_nodes = + partition.number_of_non_ghost_base_nodes + extra_nodes.size(); +} - INFO("Processing partition: %d", part_id); +void NodeWiseMeshPartitioner::findElementsInPartition( + std::size_t const part_id, const bool is_mixed_high_order_linear_elems) +{ + auto& partition = _partitions[part_id]; + std::vector<MeshLib::Element*> const& elements = _mesh->getElements(); + std::vector<bool> _is_regular_element(elements.size(), false); + + for (std::size_t elem_id = 0; elem_id < elements.size(); elem_id++) + { + const auto* elem = elements[elem_id]; + if (_is_regular_element[elem_id]) + continue; - // Find non-ghost nodes in this partition - // -- Extra nodes for high order elements - std::vector<MeshLib::Node*> extra_nodes; - for (std::size_t i = 0; i < _mesh->getNumberOfNodes(); i++) + std::size_t non_ghost_node_number = 0; + for (unsigned i = 0; i < elem->getNumberOfNodes(); i++) { - if (_nodes_partition_ids[i] == part_id) + if (_nodes_partition_ids[elem->getNodeIndex(i)] == part_id) { - if (is_mixed_high_order_linear_elems) - { // TODO: Test it once there is a case - if (i < _mesh->getNumberOfBaseNodes()) - partition.nodes.push_back(nodes[i]); - else - extra_nodes.push_back(nodes[i]); - } - else - { - partition.nodes.push_back(nodes[i]); - } + non_ghost_node_number++; } } - partition.number_of_non_ghost_base_nodes = partition.nodes.size(); - partition.number_of_non_ghost_nodes = - partition.number_of_non_ghost_base_nodes + extra_nodes.size(); - // Find elements that belong to this partition - std::vector<MeshLib::Element*> const& elements = _mesh->getElements(); - for (std::size_t elem_id = 0; elem_id < elements.size(); elem_id++) - { - const auto* elem = elements[elem_id]; - if (_elements_status[elem_id]) - continue; + if (non_ghost_node_number == 0) + continue; - std::size_t non_ghost_node_number = 0; - for (unsigned i = 0; i < elem->getNumberOfNodes(); i++) - { - if (_nodes_partition_ids[elem->getNodeIndex(i)] == part_id) - { - non_ghost_node_number++; - } - } + if (non_ghost_node_number == elem->getNumberOfNodes()) + { + partition.regular_elements.push_back(elem); + _is_regular_element[elem_id] = true; + } + else + { + partition.ghost_elements.push_back(elem); + } + } +} - if (non_ghost_node_number == 0) +void NodeWiseMeshPartitioner::findGhostNodesInPartition( + std::size_t const part_id, + const bool is_mixed_high_order_linear_elems, + std::vector<MeshLib::Node*>& extra_nodes) +{ + auto& partition = _partitions[part_id]; + std::vector<MeshLib::Node*> const& nodes = _mesh->getNodes(); + std::vector<bool> nodes_reserved(_mesh->getNumberOfNodes(), false); + for (const auto* ghost_elem : partition.ghost_elements) + { + for (unsigned i = 0; i < ghost_elem->getNumberOfNodes(); i++) + { + const unsigned node_id = ghost_elem->getNodeIndex(i); + if (nodes_reserved[node_id]) continue; - if (non_ghost_node_number == elem->getNumberOfNodes()) - { - partition.regular_elements.push_back(elem); - _elements_status[elem_id] = true; - } - else + if (_nodes_partition_ids[node_id] != part_id) { - partition.ghost_elements.push_back(elem); + splitOfHigherOrderNode(nodes, is_mixed_high_order_linear_elems, + node_id, partition.nodes, extra_nodes); + nodes_reserved[node_id] = true; } } + } +} - // Find the ghost nodes of this partition - std::vector<bool> nodes_reserved(_mesh->getNumberOfNodes(), false); - for (const auto* ghost_elem : partition.ghost_elements) - { - for (unsigned i = 0; i < ghost_elem->getNumberOfNodes(); i++) - { - const unsigned node_id = ghost_elem->getNodeIndex(i); - if (nodes_reserved[node_id]) - continue; - - if (_nodes_partition_ids[node_id] != part_id) - { - if (is_mixed_high_order_linear_elems) - { - if (node_id < _mesh->getNumberOfBaseNodes()) - partition.nodes.push_back(nodes[node_id]); - else - extra_nodes.push_back(nodes[node_id]); - } - else - { - partition.nodes.push_back(nodes[node_id]); - } - nodes_reserved[node_id] = true; - } - } - } - partition.number_of_base_nodes = partition.nodes.size(); +void NodeWiseMeshPartitioner::splitOfHigherOrderNode( + std::vector<MeshLib::Node*> const& nodes, + bool const is_mixed_high_order_linear_elems, + unsigned const node_id, + std::vector<MeshLib::Node*>& base_nodes, + std::vector<MeshLib::Node*>& extra_nodes) +{ + if (is_mixed_high_order_linear_elems) + { + if (node_id < _mesh->getNumberOfBaseNodes()) + base_nodes.push_back(nodes[node_id]); + else + extra_nodes.push_back(nodes[node_id]); + } + else + { + base_nodes.push_back(nodes[node_id]); + } +} - if (is_mixed_high_order_linear_elems) - partition.nodes.insert(partition.nodes.end(), extra_nodes.begin(), - extra_nodes.end()); +void NodeWiseMeshPartitioner::processPartition(std::size_t const part_id, + const bool is_mixed_high_order_linear_elems) +{ + std::vector<MeshLib::Node*> extra_nodes; + findNonGhostNodesInPartition(part_id, is_mixed_high_order_linear_elems, + extra_nodes); + + findElementsInPartition(part_id, is_mixed_high_order_linear_elems); + findGhostNodesInPartition(part_id, is_mixed_high_order_linear_elems, + extra_nodes); + auto& partition = _partitions[part_id]; + partition.number_of_base_nodes = partition.nodes.size(); + + if (is_mixed_high_order_linear_elems) + partition.nodes.insert(partition.nodes.end(), extra_nodes.begin(), + extra_nodes.end()); +} + +void NodeWiseMeshPartitioner::processProperties() +{ + std::size_t const total_number_of_tuples = + std::accumulate(std::begin(_partitions), std::end(_partitions), 0, + [](std::size_t const sum, Partition const& p) { + return sum + p.nodes.size(); + }); + + INFO("total number of tuples after partitioning: %d ", + total_number_of_tuples); + // 1 create new PV + // 2 resize the PV with total_number_of_tuples + // 3 copy the values according to the partition info + auto const& original_properties(_mesh->getProperties()); + auto property_names = original_properties.getPropertyVectorNames(); + for (auto const& name : property_names) + { + bool success = + copyPropertyVector<double>(name, total_number_of_tuples) || + copyPropertyVector<float>(name, total_number_of_tuples) || + copyPropertyVector<int>(name, total_number_of_tuples) || + copyPropertyVector<long>(name, total_number_of_tuples) || + copyPropertyVector<unsigned>(name, total_number_of_tuples) || + copyPropertyVector<unsigned long>(name, total_number_of_tuples) || + copyPropertyVector<std::size_t>(name, total_number_of_tuples); + if (!success) + WARN( + "processProperties: Could not create partitioned " + "PropertyVector '%s'.", + name.c_str()); + } +} + +void NodeWiseMeshPartitioner::partitionByMETIS( + const bool is_mixed_high_order_linear_elems) +{ + for (std::size_t part_id = 0; part_id < _partitions.size(); part_id++) + { + INFO("Processing partition: %d", part_id); + processPartition(part_id, is_mixed_high_order_linear_elems); } renumberNodeIndices(is_mixed_high_order_linear_elems); + + processProperties(); } void NodeWiseMeshPartitioner::renumberNodeIndices( @@ -262,6 +329,57 @@ NodeWiseMeshPartitioner::getNumberOfIntegerVariablesOfElements( return nmb_element_idxs; } +void NodeWiseMeshPartitioner::writePropertiesBinary( + const std::string& file_name_base) const +{ + auto const& property_names(_partitioned_properties.getPropertyVectorNames()); + if (property_names.empty()) + return; + const std::string fname_cfg = file_name_base + + "_partitioned_properties_cfg" + + std::to_string(_npartitions) + ".bin"; + std::ofstream out(fname_cfg.c_str(), std::ios::binary | std::ios::out); + + const std::string fname_val = file_name_base + + "_partitioned_properties_val" + + std::to_string(_npartitions) + ".bin"; + std::ofstream out_val(fname_val.c_str(), std::ios::binary | std::ios::out); + + std::size_t number_of_properties(property_names.size()); + out.write(reinterpret_cast<char*>(&number_of_properties), + sizeof(number_of_properties)); + for (auto const& name : property_names) + { + bool success = + writePropertyVectorBinary<double>(name, out_val, out) || + writePropertyVectorBinary<float>(name, out_val, out) || + writePropertyVectorBinary<int>(name, out_val, out) || + writePropertyVectorBinary<long>(name, out_val, out) || + writePropertyVectorBinary<unsigned>(name, out_val, out) || + writePropertyVectorBinary<unsigned long>(name, out_val, out) || + writePropertyVectorBinary<std::size_t>(name, out_val, out); + if (!success) + OGS_FATAL( + "writePropertiesBinary: Could not write PropertyVector '%s'.", + name.c_str()); + } + unsigned long offset = 0; + for (const auto& partition : _partitions) + { + MeshLib::IO::PropertyVectorPartitionMetaData pvpmd; + pvpmd.offset = offset; + pvpmd.number_of_tuples = partition.nodes.size(); + INFO( + "Write meta data for PropertyVector: global offset %d, number " + "of tuples %d", + pvpmd.offset, pvpmd.number_of_tuples); + MeshLib::IO::writePropertyVectorPartitionMetaData(out, pvpmd); + offset += pvpmd.number_of_tuples; + } + out.close(); + out_val.close(); +} + std::tuple<std::vector<NodeWiseMeshPartitioner::IntegerType>, std::vector<NodeWiseMeshPartitioner::IntegerType>> NodeWiseMeshPartitioner::writeConfigDataBinary( @@ -392,6 +510,7 @@ void NodeWiseMeshPartitioner::writeNodesBinary(const std::string& file_name_base const std::string fname = file_name_base + "_partitioned_msh_nod" + std::to_string(_npartitions) + ".bin"; FILE* of_bin_nod = fopen(fname.c_str(), "wb"); + for (const auto& partition : _partitions) { std::vector<NodeStruct> nodes_buffer; @@ -415,6 +534,7 @@ void NodeWiseMeshPartitioner::writeNodesBinary(const std::string& file_name_base void NodeWiseMeshPartitioner::writeBinary(const std::string& file_name_base) { + writePropertiesBinary(file_name_base); const auto elem_integers = writeConfigDataBinary(file_name_base); const std::vector<IntegerType>& num_elem_integers diff --git a/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.h b/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.h index 40f3fc3b1adee9db02003e607d30a32a23bec3c8..db7fb985b15ea307c4ce53cce4c7c3d9dbcd2ded 100644 --- a/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.h +++ b/Applications/Utils/ModelPreparation/PartitionMesh/NodeWiseMeshPartitioner.h @@ -21,6 +21,8 @@ #include <fstream> #include "MeshLib/Mesh.h" +#include "MeshLib/Node.h" +#include "MeshLib/IO/MPI_IO/PropertyVectorMetaData.h" namespace ApplicationUtils { @@ -51,10 +53,10 @@ public: std::unique_ptr<MeshLib::Mesh>&& mesh) : _npartitions(num_partitions), _partitions(num_partitions), + _partitioned_properties(), _mesh(std::move(mesh)), _nodes_global_ids(_mesh->getNumberOfNodes()), - _nodes_partition_ids(_mesh->getNumberOfNodes()), - _elements_status(_mesh->getNumberOfElements(), false) + _nodes_partition_ids(_mesh->getNumberOfNodes()) { } @@ -87,6 +89,9 @@ private: /// Data for all partitions. std::vector<Partition> _partitions; + /// Properties where values at ghost nodes and extra nodes are inserted. + MeshLib::Properties _partitioned_properties; + /// Pointer to a mesh object. std::unique_ptr<MeshLib::Mesh> _mesh; @@ -96,9 +101,6 @@ private: /// Partition IDs of each nodes. std::vector<std::size_t> _nodes_partition_ids; - /// Flags to indicate that whether elements are processed or not. - std::vector<bool> _elements_status; - // Renumber the global indices of nodes, /// \param is_mixed_high_order_linear_elems Flag to indicate whether the /// elements of a mesh can be used for both linear and high order @@ -128,6 +130,106 @@ private: std::vector<IntegerType>& elem_info, IntegerType& counter); + void writePropertiesBinary(std::string const& file_name_base) const; + + /// 1 copy pointers to nodes belonging to the partition part_id + /// 2 collect non-linear element nodes belonging to the partition part_id in + /// extra_nodes + void findNonGhostNodesInPartition( + std::size_t const part_id, + const bool is_mixed_high_order_linear_elems, + std::vector<MeshLib::Node*>& extra_nodes); + + /// 1 find elements belonging to the partition part_id: + /// fills vector partition.regular_elements + /// 2 find ghost elements belonging to the partition part_id + /// fills vector partition.ghost_elements + void findElementsInPartition(std::size_t const part_id, + const bool is_mixed_high_order_linear_elems); + + /// Prerequisite: the ghost elements has to be found (using + /// findElementsInPartition). + /// Finds ghost nodes and non-linear element ghost nodes by walking over + /// ghost elements. + void findGhostNodesInPartition(std::size_t const part_id, + const bool is_mixed_high_order_linear_elems, + std::vector<MeshLib::Node*>& extra_nodes); + + void splitOfHigherOrderNode(std::vector<MeshLib::Node*> const& nodes, + bool const is_mixed_high_order_linear_elems, + unsigned const node_id, + std::vector<MeshLib::Node*>& base_nodes, + std::vector<MeshLib::Node*>& extra_nodes); + + void processPartition(std::size_t const part_id, + const bool is_mixed_high_order_linear_elems); + + void processProperties(); + + template <typename T> + bool copyPropertyVector(std::string const& name, + std::size_t const total_number_of_tuples) + { + auto const& original_properties(_mesh->getProperties()); + if (!original_properties.existsPropertyVector<T>(name)) + return false; + + auto const& pv(original_properties.getPropertyVector<T>(name)); + auto partitioned_pv = + _partitioned_properties.createNewPropertyVector<T>( + name, pv->getMeshItemType(), pv->getNumberOfComponents()); + partitioned_pv->resize(total_number_of_tuples * + pv->getNumberOfComponents()); + std::size_t position_offset(0); + for (auto p : _partitions) + { + for (std::size_t i = 0; i < p.nodes.size(); ++i) + { + const auto global_id = p.nodes[i]->getID(); + (*partitioned_pv)[position_offset + i] = (*pv)[global_id]; + } + position_offset += p.nodes.size(); + } + return true; + } + + template <typename T> + void writePropertyVectorValuesBinary( + std::ostream& os, MeshLib::PropertyVector<T> const& pv) const + { + std::size_t number_of_components(pv.getNumberOfComponents()); + std::size_t number_of_tuples(pv.getNumberOfTuples()); + std::vector<T> property_vector_buffer; + property_vector_buffer.resize(number_of_tuples * number_of_components); + for (std::size_t i = 0; i < pv.getNumberOfTuples(); ++i) + { + for (std::size_t c(0); c < number_of_components; ++c) + property_vector_buffer[i * number_of_components + c] = + pv.getComponent(i, c); + } + os.write(reinterpret_cast<char*>(property_vector_buffer.data()), + number_of_components * number_of_tuples * sizeof(T)); + } + + template <typename T> + bool writePropertyVectorBinary(std::string const& name, + std::ostream& out_val, + std::ostream& out_meta) const + { + if (!_partitioned_properties.existsPropertyVector<T>(name)) + return false; + + MeshLib::IO::PropertyVectorMetaData pvmd; + pvmd.property_name = name; + auto* pv = _partitioned_properties.getPropertyVector<T>(name); + pvmd.fillPropertyVectorMetaDataTypeInfo<T>(); + pvmd.number_of_components = pv->getNumberOfComponents(); + pvmd.number_of_tuples = pv->getNumberOfTuples(); + writePropertyVectorValuesBinary(out_val, *pv); + MeshLib::IO::writePropertyVectorMetaDataBinary(out_meta, pvmd); + return true; + } + /*! \brief Write the configuration data of the partition data in binary files. @@ -137,8 +239,8 @@ private: element 2: The numbers of all ghost element integer variables of each partitions. */ - std::tuple< std::vector<IntegerType>, std::vector<IntegerType>> - writeConfigDataBinary(const std::string& file_name_base); + std::tuple<std::vector<IntegerType>, std::vector<IntegerType>> + writeConfigDataBinary(const std::string& file_name_base); /*! \brief Write the element integer variables of all partitions diff --git a/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.cpp b/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.cpp index 8e3d2f0f7a6611cbf28504525767d42231feaa16..c4f24d992af8ee220d7efa68a0ead8fb02db5eae 100644 --- a/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.cpp +++ b/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.cpp @@ -16,6 +16,10 @@ #include <logog/include/logog.hpp> +#ifdef USE_PETSC +#include <mpi.h> +#endif + #include "BaseLib/FileTools.h" #include "BaseLib/RunTime.h" @@ -184,8 +188,125 @@ MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::readBinary( setElements(mesh_nodes, ghost_elem_data, mesh_elems, process_ghost); //---------------------------------------------------------------------------------- - return newMesh(BaseLib::extractBaseName(file_name_base), - mesh_nodes, glb_node_ids, mesh_elems); + // read the properties + MeshLib::Properties p(readPropertiesBinary(file_name_base)); + + return newMesh(BaseLib::extractBaseName(file_name_base), mesh_nodes, + glb_node_ids, mesh_elems, p); +} + +MeshLib::Properties NodePartitionedMeshReader::readPropertiesBinary( + const std::string& file_name_base) const +{ + const std::string fname_cfg = file_name_base + + "_partitioned_properties_cfg" + + std::to_string(_mpi_comm_size) + ".bin"; + std::ifstream is(fname_cfg.c_str(), std::ios::binary | std::ios::in); + if (!is) + { + WARN("Could not open file '%s' in binary mode.", fname_cfg.c_str()); + return MeshLib::Properties(); + } + std::size_t number_of_properties = 0; + is.read(reinterpret_cast<char*>(&number_of_properties), sizeof(std::size_t)); + std::vector<boost::optional<MeshLib::IO::PropertyVectorMetaData>> vec_pvmd( + number_of_properties); + for (std::size_t i(0); i < number_of_properties; ++i) + { + vec_pvmd[i] = MeshLib::IO::readPropertyVectorMetaData(is); + if (!vec_pvmd[i]) + { + OGS_FATAL( + "Error in NodePartitionedMeshReader::readPropertiesBinary: " + "Could not read the meta data for the PropertyVector %d", + i); + } + } + for (std::size_t i(0); i < number_of_properties; ++i) + { + DBUG("[%d] +++++++++++++", _mpi_rank); + MeshLib::IO::writePropertyVectorMetaData(*(vec_pvmd[i])); + DBUG("[%d] +++++++++++++", _mpi_rank); + } + auto pos = is.tellg(); + auto offset = + pos + + static_cast<long>(_mpi_rank * + sizeof(MeshLib::IO::PropertyVectorPartitionMetaData)); + is.seekg(offset); + boost::optional<MeshLib::IO::PropertyVectorPartitionMetaData> pvpmd( + MeshLib::IO::readPropertyVectorPartitionMetaData(is)); + bool pvpmd_read_ok = static_cast<bool>(pvpmd); + bool all_pvpmd_read_ok; + MPI_Allreduce(&pvpmd_read_ok, &all_pvpmd_read_ok, 1, MPI_C_BOOL, MPI_LOR, + _mpi_comm); + if (!all_pvpmd_read_ok) + { + OGS_FATAL( + "Error in NodePartitionedMeshReader::readPropertiesBinary: " + "Could not read the partition meta data for the mpi process %d", + _mpi_rank); + } + DBUG("[%d] offset in the PropertyVector: %d", _mpi_rank, pvpmd->offset); + DBUG("[%d] %d tuples in partition.", _mpi_rank, pvpmd->number_of_tuples); + is.close(); + + const std::string fname_val = file_name_base + "_partitioned_properties_val" + + std::to_string(_mpi_comm_size) + ".bin"; + is.open(fname_val.c_str(), std::ios::binary | std::ios::in); + if (!is) + { + ERR("Could not open file '%s' in binary mode.", fname_val.c_str()); + } + + MeshLib::Properties p; + + // Read the specific parts of the PropertyVector values for this process. + unsigned long global_offset = 0; + for (std::size_t i(0); i < number_of_properties; ++i) + { + INFO("[%d] global offset: %d, offset within the PropertyVector: %d.", + _mpi_rank, global_offset, + global_offset + + pvpmd->offset * vec_pvmd[i]->data_type_size_in_bytes); + if (vec_pvmd[i]->is_int_type) + { + if (vec_pvmd[i]->is_data_type_signed) + { + if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(int)) + createPropertyVectorPart<int>(is, *vec_pvmd[i], *pvpmd, + global_offset, p); + if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(long)) + createPropertyVectorPart<long>(is, *vec_pvmd[i], *pvpmd, + global_offset, p); + } + else + { + if (vec_pvmd[i]->data_type_size_in_bytes == + sizeof(unsigned int)) + createPropertyVectorPart<unsigned int>( + is, *vec_pvmd[i], *pvpmd, global_offset, p); + if (vec_pvmd[i]->data_type_size_in_bytes == + sizeof(unsigned long)) + createPropertyVectorPart<unsigned long>( + is, *vec_pvmd[i], *pvpmd, global_offset, p); + } + } + else + { + if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(float)) + createPropertyVectorPart<float>(is, *vec_pvmd[i], *pvpmd, + global_offset, p); + if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(double)) + createPropertyVectorPart<double>(is, *vec_pvmd[i], *pvpmd, + global_offset, p); + } + global_offset += vec_pvmd[i]->data_type_size_in_bytes * + vec_pvmd[i]->number_of_tuples * + vec_pvmd[i]->number_of_components; + } + + return p; } bool NodePartitionedMeshReader::openASCIIFiles(std::string const& file_name_base, @@ -369,28 +490,31 @@ MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::readASCII( MPI_Bcast(_mesh_info.data(), static_cast<int>(_mesh_info.size()), MPI_LONG, 0, _mpi_comm); - //---------------------------------------------------------------------------------- + //--------------------------------------------------------------------- // Read Nodes if (!readCastNodesASCII(is_node, i, mesh_nodes, glb_node_ids)) break; - //---------------------------------------------------------------------------------- + //--------------------------------------------------------------------- // Read elements if (!readCastElemsASCII(is_elem, i, _mesh_info.regular_elements + _mesh_info.offset[0], false, mesh_nodes, mesh_elems)) break; - //------------------------------------------------------------------------- + //--------------------------------------------------------------------- // Ghost elements if (!readCastElemsASCII(is_elem, i, _mesh_info.ghost_elements + _mesh_info.offset[1], true, mesh_nodes, mesh_elems)) break; - if(_mpi_rank == i) + if(_mpi_rank == i) { + // reading ascii properties is not implemented + MeshLib::Properties properties; np_mesh = newMesh(BaseLib::extractBaseName(file_name_base), - mesh_nodes, glb_node_ids, mesh_elems); + mesh_nodes, glb_node_ids, mesh_elems, properties); + } } if(_mpi_rank == 0) @@ -405,17 +529,17 @@ MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::readASCII( return np_mesh; } -MeshLib::NodePartitionedMesh* -NodePartitionedMeshReader::newMesh( +MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::newMesh( std::string const& mesh_name, std::vector<MeshLib::Node*> const& mesh_nodes, std::vector<unsigned long> const& glb_node_ids, - std::vector<MeshLib::Element*> const& mesh_elems) const + std::vector<MeshLib::Element*> const& mesh_elems, + MeshLib::Properties const& properties) const { return new MeshLib::NodePartitionedMesh( mesh_name + std::to_string(_mpi_comm_size), mesh_nodes, glb_node_ids, mesh_elems, - MeshLib::Properties(), + properties, _mesh_info.global_base_nodes, _mesh_info.global_nodes, _mesh_info.base_nodes, diff --git a/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.h b/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.h index 26e4a9f2488938d08d69b2813271c446b294499b..6fbfc2d7832b5b7b87d3c3da8ff4813984a3b287 100644 --- a/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.h +++ b/MeshLib/IO/MPI_IO/NodePartitionedMeshReader.h @@ -20,11 +20,14 @@ #include <mpi.h> #include "MeshLib/NodePartitionedMesh.h" +#include "MeshLib/Properties.h" +#include "MeshLib/IO/MPI_IO/PropertyVectorMetaData.h" namespace MeshLib { class Node; class Element; +class Properties; namespace IO { @@ -100,17 +103,21 @@ private: } _mesh_info; /*! - \brief Create a new mesh of NodePartitionedMesh after reading and processing the data. + \brief Create a new mesh of NodePartitionedMesh after reading and + processing the data. \param mesh_name Name assigned to the new mesh. \param mesh_nodes Node data. \param glb_node_ids Global IDs of nodes. \param mesh_elems Element data. - \return True on success and false otherwise. + \param properties Collection of PropertyVector's assigned to the mesh. + \return Returns a pointer to a NodePartitionedMesh */ - MeshLib::NodePartitionedMesh* newMesh(std::string const& mesh_name, + MeshLib::NodePartitionedMesh* newMesh( + std::string const& mesh_name, std::vector<MeshLib::Node*> const& mesh_nodes, std::vector<unsigned long> const& glb_node_ids, - std::vector<MeshLib::Element*> const& mesh_elems) const; + std::vector<MeshLib::Element*> const& mesh_elems, + MeshLib::Properties const& properties) const; /*! \brief Parallel reading of a binary file via MPI_File_read, and it is called by readBinary @@ -158,18 +165,49 @@ private: */ MeshLib::NodePartitionedMesh* readBinary(const std::string &file_name_base); + MeshLib::Properties readPropertiesBinary(const std::string& file_name_base) const; + + template <typename T> + void createPropertyVectorPart( + std::istream& is, MeshLib::IO::PropertyVectorMetaData const& pvmd, + MeshLib::IO::PropertyVectorPartitionMetaData const& pvpmd, + unsigned long global_offset, MeshLib::Properties& p) const + { + MeshLib::PropertyVector<T>* pv = + p.createNewPropertyVector<T>(pvmd.property_name, + MeshLib::MeshItemType::Node, + pvmd.number_of_components); + pv->resize(pvpmd.number_of_tuples * pvmd.number_of_components); + // jump to the place for reading the specific part of the + // PropertyVector + is.seekg(global_offset + pvpmd.offset * sizeof(T)); + // read the values + unsigned long const number_of_bytes = pvmd.data_type_size_in_bytes * + pvpmd.number_of_tuples * + pvmd.number_of_components; + if (!is.read(reinterpret_cast<char*>(pv->data()), number_of_bytes)) + OGS_FATAL( + "Error in NodePartitionedMeshReader::readPropertiesBinary: " + "Could not read part %d of the PropertyVector.", + _mpi_rank); + } + /*! \brief Open ASCII files of node partitioned mesh data. - \param file_name_base Name of file to be read, which must be a name with the + \param file_name_base Name of file to be read, which must be a name + with the path to the file and without file extension. - \param is_cfg Input stream for the file contains configuration data. + \param is_cfg Input stream for the file contains + configuration data. \param is_node Input stream for the file contains node data. - \param is_elem Input stream for the file contains element data. + \param is_elem Input stream for the file contains element + data. \return Return true if all files are good. */ - bool openASCIIFiles(std::string const& file_name_base,std::ifstream& is_cfg, - std::ifstream& is_node, std::ifstream& is_elem) const; + bool openASCIIFiles(std::string const& file_name_base, + std::ifstream& is_cfg, std::ifstream& is_node, + std::ifstream& is_elem) const; /*! \brief Read mesh nodes from an ASCII file and cast to the corresponding rank. diff --git a/MeshLib/IO/MPI_IO/PropertyVectorMetaData.h b/MeshLib/IO/MPI_IO/PropertyVectorMetaData.h new file mode 100644 index 0000000000000000000000000000000000000000..0d1e45fd6bd5123be04d88858aaf4695b3e6795b --- /dev/null +++ b/MeshLib/IO/MPI_IO/PropertyVectorMetaData.h @@ -0,0 +1,144 @@ +/** + * \file + * + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#pragma once + +#include <string> +#include <boost/optional.hpp> + +namespace MeshLib +{ +namespace IO +{ + +struct PropertyVectorMetaData +{ + std::string property_name; + /// is_int_type is true if the type of the components is an integer type, if + /// it is a floating point number type the is_int_type is false + bool is_int_type; + /// if the component type is an integer number the flag is_data_type_signed + /// signals if it has a sign or not + bool is_data_type_signed; + unsigned long data_type_size_in_bytes; + unsigned long number_of_components; + unsigned long number_of_tuples; + + template <typename T> + void fillPropertyVectorMetaDataTypeInfo() + { + is_int_type = std::numeric_limits<T>::is_integer; + is_data_type_signed = std::numeric_limits<T>::is_signed; + data_type_size_in_bytes = sizeof(T); + } +}; + +inline void writePropertyVectorMetaDataBinary( + std::ostream& os, PropertyVectorMetaData const& pvmd) +{ + std::string::size_type s(pvmd.property_name.length()); + os.write(reinterpret_cast<char*>(&s), sizeof(std::string::size_type)); + + os.write( + const_cast<char*>( + const_cast<PropertyVectorMetaData&>(pvmd).property_name.data()), + s); + os.write(reinterpret_cast<char*>( + &const_cast<PropertyVectorMetaData&>(pvmd).is_int_type), + sizeof(bool)); + os.write(reinterpret_cast<char*>(&const_cast<PropertyVectorMetaData&>( + pvmd).is_data_type_signed), + sizeof(bool)); + os.write(reinterpret_cast<char*>(&const_cast<PropertyVectorMetaData&>( + pvmd).data_type_size_in_bytes), + sizeof(unsigned long)); + os.write(reinterpret_cast<char*>(&const_cast<PropertyVectorMetaData&>( + pvmd).number_of_components), + sizeof(unsigned long)); + os.write(reinterpret_cast<char*>( + &const_cast<PropertyVectorMetaData&>(pvmd).number_of_tuples), + sizeof(unsigned long)); +} + +inline void writePropertyVectorMetaData(PropertyVectorMetaData const& pvmd) +{ + DBUG("size of name: %d", pvmd.property_name.length()); + DBUG("name: '%s'", pvmd.property_name.c_str()); + DBUG("is_int_data_type: %d", pvmd.is_int_type); + DBUG("is_data_type_signed: %d", pvmd.is_data_type_signed); + DBUG("data_type_size_in_bytes: %d", pvmd.data_type_size_in_bytes); + DBUG("number of components: i%d", pvmd.number_of_components); + DBUG("number of tuples: %d", pvmd.number_of_tuples); +} + +inline boost::optional<PropertyVectorMetaData> readPropertyVectorMetaData( + std::istream& is) +{ + // read the size of the name of the PropertyVector + std::string::size_type s = 0; + if (!is.read(reinterpret_cast<char*>(&s), sizeof(std::string::size_type))) + return boost::optional<PropertyVectorMetaData>(); + + PropertyVectorMetaData pvmd; + char *dummy = new char[s]; + if (!is.read(dummy, s)) + return boost::none; + pvmd.property_name = std::string(dummy, s); + delete [] dummy; + + if(!is.read(reinterpret_cast<char*>(&pvmd.is_int_type), sizeof(bool))) + return boost::none; + if(!is.read(reinterpret_cast<char*>(&pvmd.is_data_type_signed), sizeof(bool))) + return boost::none; + if(!is.read(reinterpret_cast<char*>(&pvmd.data_type_size_in_bytes), + sizeof(unsigned long))) + return boost::none; + if(!is.read(reinterpret_cast<char*>(&pvmd.number_of_components), + sizeof(unsigned long))) + return boost::none; + if(!is.read(reinterpret_cast<char*>(&pvmd.number_of_tuples), + sizeof(unsigned long))) + return boost::none; + return boost::optional<PropertyVectorMetaData>(pvmd); +} + +struct PropertyVectorPartitionMetaData +{ + unsigned long offset; + unsigned long number_of_tuples; +}; + +inline void writePropertyVectorPartitionMetaData( + std::ostream& os, PropertyVectorPartitionMetaData const& pvpmd) +{ + os.write(reinterpret_cast<char*>( + &const_cast<PropertyVectorPartitionMetaData&>(pvpmd) + .offset), + sizeof(unsigned long)); + os.write(reinterpret_cast<char*>( + &const_cast<PropertyVectorPartitionMetaData&>(pvpmd) + .number_of_tuples), + sizeof(unsigned long)); +} + +inline boost::optional<PropertyVectorPartitionMetaData> +readPropertyVectorPartitionMetaData(std::istream& is) +{ + PropertyVectorPartitionMetaData pvpmd; + if (!is.read(reinterpret_cast<char*>(&pvpmd.offset), + sizeof(unsigned long))) + return boost::optional<PropertyVectorPartitionMetaData>(); + if (!is.read(reinterpret_cast<char*>(&pvpmd.number_of_tuples), + sizeof(unsigned long))) + return boost::optional<PropertyVectorPartitionMetaData>(); + return boost::optional<PropertyVectorPartitionMetaData>(pvpmd); +} +} +}