OGS
NodePartitionedMeshReader.cpp
Go to the documentation of this file.
1
17
18#include <mpi.h>
19
20#include <fstream>
21#include <numeric>
22
23#include "BaseLib/FileTools.h"
24#include "BaseLib/Logging.h"
25#include "BaseLib/MPI.h"
26#include "BaseLib/RunTime.h"
28#include "MeshLib/MeshEnums.h"
29#include "MeshLib/Properties.h"
30
31// Check if the value can by converted to given type without overflow.
32template <typename VALUE, typename TYPE>
33bool is_safely_convertable(VALUE const& value)
34{
35 bool const result = value <= std::numeric_limits<TYPE>::max();
36 if (!result)
37 {
38 ERR("The value {:d} is too large for conversion.", value);
39 ERR("Maximum available size is {:d}.",
40 std::numeric_limits<TYPE>::max());
41 }
42 return result;
43}
44
45namespace MeshLib
46{
47namespace IO
48{
53
58
60{
61 int const count = 2;
62 int blocks[count] = {1, 3};
63 MPI_Datatype types[count] = {MPI_UNSIGNED_LONG, MPI_DOUBLE};
64 MPI_Aint displacements[count] = {0, sizeof(NodeData::index)};
65
66 MPI_Type_create_struct(count, blocks, displacements, types,
68 MPI_Type_commit(&_mpi_node_type);
69}
70
72 const std::string& file_name_base)
73{
74 BaseLib::RunTime timer;
75 timer.start();
76
77 // Always try binary file first
78 std::string const fname_new = file_name_base + "_partitioned_msh_cfg" +
79 std::to_string(mpi_.size) + ".bin";
80
81 if (!BaseLib::IsFileExisting(fname_new)) // binary file does not exist.
82 {
83 std::string const fname_ascii = file_name_base +
84 "_partitioned_msh_cfg" +
85 std::to_string(mpi_.size) + ".msh";
86 if (BaseLib::IsFileExisting(fname_ascii))
87 {
88 ERR("Reading of ASCII mesh file {:s} is not supported since OGS "
89 "version 6.3.3.",
90 fname_ascii);
91 }
92 OGS_FATAL("Required binary file {:s} does not exist.\n", fname_new);
93 }
94
95 INFO("Reading corresponding part of mesh data from binary file {:s} ...",
96 file_name_base);
97
98 MeshLib::NodePartitionedMesh* mesh = readMesh(file_name_base);
99
100 INFO("[time] Reading the mesh took {:f} s.", timer.elapsed());
101
102 MPI_Barrier(mpi_.communicator);
103
104 return mesh;
105}
106
107template <typename DATA>
108bool NodePartitionedMeshReader::readDataFromFile(std::string const& filename,
109 MPI_Offset offset,
110 MPI_Datatype type,
111 DATA& data) const
112{
113 // Check container size
115 {
116 ERR("The container size is too large for MPI_File_read() call.");
117 return false;
118 }
119
120 // Open file
121 MPI_File file;
122
123 char* filename_char = const_cast<char*>(filename.data());
124 int const file_status =
125 MPI_File_open(mpi_.communicator, filename_char, MPI_MODE_RDONLY,
126 MPI_INFO_NULL, &file);
127
128 if (file_status != 0)
129 {
130 ERR("Error opening file {:s}. MPI error code {:d}", filename,
131 file_status);
132 return false;
133 }
134
135 // Read data
136 char file_mode[] = "native";
137 MPI_File_set_view(file, offset, type, type, file_mode, MPI_INFO_NULL);
138 // The static cast is checked above.
139 MPI_File_read(file, data.data(), static_cast<int>(data.size()), type,
140 MPI_STATUS_IGNORE);
141 MPI_File_close(&file);
142
143 return true;
144}
145
147 const std::string& file_name_base)
148{
149 //----------------------------------------------------------------------------------
150 // Read headers
151 const std::string fname_header = file_name_base + "_partitioned_msh_";
152 const std::string fname_num_p_ext = std::to_string(mpi_.size) + ".bin";
153
154 // Read the config meta data from *cfg* file into struct PartitionedMeshInfo
155 // _mesh_info
156 if (!readDataFromFile(
157 fname_header + "cfg" + fname_num_p_ext,
158 static_cast<MPI_Offset>(static_cast<unsigned>(mpi_.rank) *
159 sizeof(_mesh_info)),
160 MPI_LONG, _mesh_info))
161 return nullptr;
162
163 //----------------------------------------------------------------------------------
164 // Read Nodes
165 std::vector<NodeData> nodes(_mesh_info.number_of_nodes);
166
167 if (!readDataFromFile(fname_header + "nod" + fname_num_p_ext,
168 static_cast<MPI_Offset>(_mesh_info.offset[2]),
169 _mpi_node_type, nodes))
170 return nullptr;
171
172 std::vector<MeshLib::Node*> mesh_nodes;
173 std::vector<unsigned long> glb_node_ids;
174 setNodes(nodes, mesh_nodes, glb_node_ids);
175
176 //----------------------------------------------------------------------------------
177 // Read non-ghost elements
178 std::vector<unsigned long> elem_data(_mesh_info.number_of_regular_elements +
179 _mesh_info.offset[0]);
180 if (!readDataFromFile(fname_header + "ele" + fname_num_p_ext,
181 static_cast<MPI_Offset>(_mesh_info.offset[3]),
182 MPI_LONG, elem_data))
183 return nullptr;
184
185 std::vector<MeshLib::Element*> mesh_elems(
188 setElements(mesh_nodes, elem_data, mesh_elems);
189
190 //----------------------------------------------------------------------------------
191 // Read ghost element
192 std::vector<unsigned long> ghost_elem_data(
194
195 if (!readDataFromFile(fname_header + "ele_g" + fname_num_p_ext,
196 static_cast<MPI_Offset>(_mesh_info.offset[4]),
197 MPI_LONG, ghost_elem_data))
198 return nullptr;
199
200 const bool process_ghost = true;
201 setElements(mesh_nodes, ghost_elem_data, mesh_elems, process_ghost);
202
203 //----------------------------------------------------------------------------------
204 // read the properties
205 MeshLib::Properties p(readProperties(file_name_base));
206
207 return newMesh(BaseLib::extractBaseName(file_name_base), mesh_nodes,
208 glb_node_ids, mesh_elems, p);
209}
210
212 const std::string& file_name_base) const
213{
218 return p;
219}
220
222 const std::string& file_name_base, MeshLib::MeshItemType t,
223 MeshLib::Properties& p) const
224{
225 const std::string item_type = MeshLib::toString(t);
226
227 const std::string fname_cfg = file_name_base + "_partitioned_" + item_type +
228 "_properties_cfg" +
229 std::to_string(mpi_.size) + ".bin";
230 std::ifstream is(fname_cfg.c_str(), std::ios::binary | std::ios::in);
231 if (!is)
232 {
233 WARN(
234 "Could not open file '{:s}'.\n"
235 "\tYou can ignore this warning if the mesh does not contain {:s}-"
236 "wise property data.",
237 fname_cfg, item_type.data());
238 return;
239 }
240 std::size_t number_of_properties = 0;
241 is.read(reinterpret_cast<char*>(&number_of_properties),
242 sizeof(std::size_t));
243 std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> vec_pvmd(
244 number_of_properties);
245 for (std::size_t i(0); i < number_of_properties; ++i)
246 {
248 if (!vec_pvmd[i])
249 {
250 OGS_FATAL(
251 "Error in NodePartitionedMeshReader::readProperties: "
252 "Could not read the meta data for the PropertyVector {:d}",
253 i);
254 }
255 }
256 for (std::size_t i(0); i < number_of_properties; ++i)
257 {
259 }
260 auto pos = is.tellg();
261 auto offset =
262 static_cast<long>(pos) +
263 static_cast<long>(mpi_.rank *
265 is.seekg(offset);
266 std::optional<MeshLib::IO::PropertyVectorPartitionMetaData> pvpmd(
268 bool const all_pvpmd_read_ok =
269 BaseLib::MPI::allreduce(static_cast<bool>(pvpmd), MPI_LOR, mpi_);
270 if (!all_pvpmd_read_ok)
271 {
272 OGS_FATAL(
273 "Error in NodePartitionedMeshReader::readProperties: "
274 "Could not read the partition meta data for the mpi process {:d}",
275 mpi_.rank);
276 }
277 DBUG("offset in the PropertyVector: {:d}", pvpmd->offset);
278 DBUG("{:d} tuples in partition.", pvpmd->number_of_tuples);
279 is.close();
280
281 const std::string fname_val = file_name_base + "_partitioned_" + item_type +
282 "_properties_val" +
283 std::to_string(mpi_.size) + ".bin";
284 is.open(fname_val.c_str(), std::ios::binary | std::ios::in);
285 if (!is)
286 {
287 ERR("Could not open file '{:s}'\n."
288 "\tYou can ignore this warning if the mesh does not contain {:s}-"
289 "wise property data.",
290 fname_val, item_type.data());
291 }
292
293 readDomainSpecificPartOfPropertyVectors(vec_pvmd, *pvpmd, t, is, p);
294}
295
297 std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> const&
298 vec_pvmd,
301 std::istream& is,
302 MeshLib::Properties& p) const
303{
304 unsigned long global_offset = 0;
305 std::size_t const number_of_properties = vec_pvmd.size();
306 for (std::size_t i(0); i < number_of_properties; ++i)
307 {
308 DBUG("global offset: {:d}, offset within the PropertyVector: {:d}.",
309 global_offset,
310 global_offset + pvpmd.offset * vec_pvmd[i]->number_of_components *
311 vec_pvmd[i]->data_type_size_in_bytes);
312
313 // Special field data such as OGS_VERSION, IntegrationPointMetaData,
314 // etc., which are not "real" integration points, are copied "as is"
315 // (i.e. fully) for every partition.
316 if (vec_pvmd[i]->property_name.find("_ip") == std::string::npos &&
318 {
319 createSpecificPropertyVectorPart<char>(is, *vec_pvmd[i], t,
320 global_offset, p);
321
322 global_offset += vec_pvmd[i]->data_type_size_in_bytes *
323 vec_pvmd[i]->number_of_tuples;
324 continue;
325 }
326
327 if (vec_pvmd[i]->is_int_type)
328 {
329 if (vec_pvmd[i]->is_data_type_signed)
330 {
331 if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(char))
332 {
333 createPropertyVectorPart<char>(is, *vec_pvmd[i], pvpmd, t,
334 global_offset, p);
335 }
336 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(int))
337 {
338 createPropertyVectorPart<int>(is, *vec_pvmd[i], pvpmd, t,
339 global_offset, p);
340 }
341 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(long))
342 createPropertyVectorPart<long>(is, *vec_pvmd[i], pvpmd, t,
343 global_offset, p);
344 else
345 {
346 WARN(
347 "Implementation for reading signed integer property "
348 "vector '{:s}' is not available.",
349 vec_pvmd[i]->property_name);
350 }
351 }
352 else
353 {
354 if (vec_pvmd[i]->data_type_size_in_bytes ==
355 sizeof(unsigned char))
356 {
358 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
359 }
360 else if (vec_pvmd[i]->data_type_size_in_bytes ==
361 sizeof(unsigned int))
363 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
364 else if (vec_pvmd[i]->data_type_size_in_bytes ==
365 sizeof(unsigned long))
367 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
368 else
369 {
370 WARN(
371 "Implementation for reading unsigned property vector "
372 "'{:s}' is not available.",
373 vec_pvmd[i]->property_name);
374 }
375 }
376 }
377 else
378 {
379 if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(float))
380 createPropertyVectorPart<float>(is, *vec_pvmd[i], pvpmd, t,
381 global_offset, p);
382 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(double))
383 createPropertyVectorPart<double>(is, *vec_pvmd[i], pvpmd, t,
384 global_offset, p);
385 else
386 {
387 WARN(
388 "Implementation for reading floating point property vector "
389 "'{:s}' is not available.",
390 vec_pvmd[i]->property_name);
391 }
392 }
393 global_offset += vec_pvmd[i]->data_type_size_in_bytes *
394 vec_pvmd[i]->number_of_tuples *
395 vec_pvmd[i]->number_of_components;
396 }
397}
398
400 std::string const& mesh_name,
401 std::vector<MeshLib::Node*> const& mesh_nodes,
402 std::vector<unsigned long> const& glb_node_ids,
403 std::vector<MeshLib::Element*> const& mesh_elems,
404 MeshLib::Properties const& properties) const
405{
406 std::vector<std::size_t> const gathered_n_regular_base_nodes =
408
409 std::vector<std::size_t> n_regular_base_nodes_at_rank =
410 BaseLib::sizesToOffsets(gathered_n_regular_base_nodes);
411
412 std::size_t const n_regular_high_order_nodes =
415 std::vector<std::size_t> const gathered_n_regular_high_order_nodes =
416 BaseLib::MPI::allgather(n_regular_high_order_nodes, mpi_);
417
418 std::vector<std::size_t> n_regular_high_order_nodes_at_rank =
419 BaseLib::sizesToOffsets(gathered_n_regular_high_order_nodes);
420
422 mesh_name, mesh_nodes, glb_node_ids, mesh_elems, properties,
425 std::move(n_regular_base_nodes_at_rank),
426 std::move(n_regular_high_order_nodes_at_rank));
427}
428
430 const std::vector<NodeData>& node_data,
431 std::vector<MeshLib::Node*>& mesh_node,
432 std::vector<unsigned long>& glb_node_ids) const
433{
434 mesh_node.resize(_mesh_info.number_of_nodes);
435 glb_node_ids.resize(_mesh_info.number_of_nodes);
436
437 for (std::size_t i = 0; i < mesh_node.size(); i++)
438 {
439 NodeData const& nd = node_data[i];
440 glb_node_ids[i] = nd.index;
441 mesh_node[i] = new MeshLib::Node(nd.x, nd.y, nd.z, i);
442 }
443}
444
446 const std::vector<MeshLib::Node*>& mesh_nodes,
447 const std::vector<unsigned long>& elem_data,
448 std::vector<MeshLib::Element*>& mesh_elems, const bool ghost) const
449{
450 // Number of elements, either ghost or regular
451 unsigned long const ne = ghost ? _mesh_info.number_of_ghost_elements
453 unsigned long const id_offset_ghost =
455
456 for (unsigned long i = 0; i < ne; i++)
457 {
458 unsigned long id_offset_elem = elem_data[i];
459
460 // Unused for now, keep for elem_data documentation purpose here.
461 {
462 const unsigned mat_idx =
463 static_cast<unsigned>(elem_data[id_offset_elem++]);
464 (void)mat_idx;
465 }
466 const unsigned long e_type = elem_data[id_offset_elem++];
467 unsigned long const nnodes = elem_data[id_offset_elem++];
468
469 MeshLib::Node** elem_nodes = new MeshLib::Node*[nnodes];
470 for (unsigned long k = 0; k < nnodes; k++)
471 elem_nodes[k] = mesh_nodes[elem_data[id_offset_elem++]];
472
473 // The element types below are defined by the MeshLib::CellType.
474 switch (static_cast<CellType>(e_type))
475 {
476 case CellType::POINT1:
477 mesh_elems[i + id_offset_ghost] =
478 new MeshLib::Point(elem_nodes);
479 break;
480 case CellType::LINE2:
481 mesh_elems[i + id_offset_ghost] = new MeshLib::Line(elem_nodes);
482 break;
483 case CellType::LINE3:
484 mesh_elems[i + id_offset_ghost] =
485 new MeshLib::Line3(elem_nodes);
486 break;
487 case CellType::QUAD4:
488 mesh_elems[i + id_offset_ghost] = new MeshLib::Quad(elem_nodes);
489 break;
490 case CellType::QUAD8:
491 mesh_elems[i + id_offset_ghost] =
492 new MeshLib::Quad8(elem_nodes);
493 break;
494 case CellType::QUAD9:
495 mesh_elems[i + id_offset_ghost] =
496 new MeshLib::Quad9(elem_nodes);
497 break;
498 case CellType::HEX8:
499 mesh_elems[i + id_offset_ghost] = new MeshLib::Hex(elem_nodes);
500 break;
501 case CellType::HEX20:
502 mesh_elems[i + id_offset_ghost] =
503 new MeshLib::Hex20(elem_nodes);
504 break;
505 case CellType::HEX27:
506 OGS_FATAL(
507 "NodePartitionedMeshReader: construction of HEX27 element "
508 "with id {:d} is not implemented.",
509 i);
510 break;
511 case CellType::TRI3:
512 mesh_elems[i + id_offset_ghost] = new MeshLib::Tri(elem_nodes);
513 break;
514 case CellType::TRI6:
515 mesh_elems[i + id_offset_ghost] = new MeshLib::Tri6(elem_nodes);
516 break;
517 case CellType::TET4:
518 mesh_elems[i + id_offset_ghost] = new MeshLib::Tet(elem_nodes);
519 break;
520 case CellType::TET10:
521 mesh_elems[i + id_offset_ghost] =
522 new MeshLib::Tet10(elem_nodes);
523 break;
524 case CellType::PRISM6:
525 mesh_elems[i + id_offset_ghost] =
526 new MeshLib::Prism(elem_nodes);
527 break;
529 mesh_elems[i + id_offset_ghost] =
530 new MeshLib::Prism15(elem_nodes);
531 break;
533 mesh_elems[i + id_offset_ghost] =
534 new MeshLib::Pyramid(elem_nodes);
535 break;
537 mesh_elems[i + id_offset_ghost] =
538 new MeshLib::Pyramid13(elem_nodes);
539 break;
541 OGS_FATAL(
542 "NodePartitionedMeshReader: construction of INVALID "
543 "element type with id {:d} is not possible.",
544 i);
545 break;
546 default:
547 OGS_FATAL(
548 "NodePartitionedMeshReader: construction of element type "
549 "{:d} is not implemented.",
550 e_type);
551 }
552 }
553}
554} // namespace IO
555} // namespace MeshLib
#define OGS_FATAL(...)
Definition Error.h:26
Filename manipulation routines.
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:35
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:30
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Definition of mesh-related Enumerations.
Definition of the class Properties that implements a container of properties.
bool is_safely_convertable(VALUE const &value)
Declare a class to read node-wise partitioned mesh with MPI functions.
Definition of the RunTime class.
Count the running time.
Definition RunTime.h:29
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:42
void start()
Start the timer.
Definition RunTime.h:32
void createPropertyVectorPart(std::istream &is, MeshLib::IO::PropertyVectorMetaData const &pvmd, MeshLib::IO::PropertyVectorPartitionMetaData const &pvpmd, MeshLib::MeshItemType t, unsigned long global_offset, MeshLib::Properties &p) const
void setElements(const std::vector< MeshLib::Node * > &mesh_nodes, const std::vector< unsigned long > &elem_data, std::vector< MeshLib::Element * > &mesh_elems, const bool ghost=false) const
Set mesh elements from a temporary array containing node data read from file.
bool readDataFromFile(std::string const &filename, MPI_Offset offset, MPI_Datatype type, DATA &data) const
MeshLib::NodePartitionedMesh * readMesh(const std::string &file_name_base)
Create a NodePartitionedMesh object, read binary mesh data in the manner of parallel,...
MeshLib::NodePartitionedMesh * read(const std::string &file_name_base)
Create a NodePartitionedMesh object, read data to it, and return a pointer to it. Data files are in b...
void setNodes(const std::vector< NodeData > &node_data, std::vector< MeshLib::Node * > &mesh_node, std::vector< unsigned long > &glb_node_ids) const
Set mesh nodes from a temporary array containing node data read from file.
void createSpecificPropertyVectorPart(std::istream &is, MeshLib::IO::PropertyVectorMetaData const &pvmd, MeshLib::MeshItemType t, unsigned long global_offset, MeshLib::Properties &p) const
MPI_Datatype _mpi_node_type
MPI data type for struct NodeData.
void registerNodeDataMpiType()
Define MPI data type for NodeData struct.
struct MeshLib::IO::NodePartitionedMeshReader::PartitionedMeshInfo _mesh_info
MeshLib::Properties readProperties(const std::string &file_name_base) const
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, MeshLib::Properties const &properties) const
Create a new mesh of NodePartitionedMesh after reading and processing the data.
BaseLib::MPI::Mpi mpi_
Pointer to MPI communicator, the rank and the size.
void readDomainSpecificPartOfPropertyVectors(std::vector< std::optional< MeshLib::IO::PropertyVectorMetaData > > const &vec_pvmd, MeshLib::IO::PropertyVectorPartitionMetaData const &pvpmd, MeshLib::MeshItemType t, std::istream &is, MeshLib::Properties &p) const
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition Properties.h:36
static T allreduce(T const &value, MPI_Op const &mpi_op, Mpi const &mpi)
Definition MPI.h:128
static std::vector< T > allgather(T const &value, Mpi const &mpi)
Definition MPI.h:100
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists.
Definition FileTools.cpp:50
std::vector< ranges::range_value_t< R > > sizesToOffsets(R const &sizes)
Definition Algorithm.h:283
std::string extractBaseName(std::string const &pathname)
void writePropertyVectorMetaData(std::ostream &os, PropertyVectorMetaData const &pvmd)
std::optional< PropertyVectorMetaData > readPropertyVectorMetaData(std::istream &is)
std::optional< PropertyVectorPartitionMetaData > readPropertyVectorPartitionMetaData(std::istream &is)
TemplateElement< MeshLib::QuadRule9 > Quad9
Definition Quad.h:30
TemplateElement< MeshLib::TetRule10 > Tet10
Definition Tet.h:26
CellType
Types of mesh elements supported by OpenGeoSys.
Definition MeshEnums.h:43
TemplateElement< MeshLib::HexRule20 > Hex20
Definition Hex.h:26
TemplateElement< MeshLib::TetRule4 > Tet
Definition Tet.h:25
TemplateElement< MeshLib::LineRule2 > Line
Definition Line.h:25
TemplateElement< MeshLib::QuadRule8 > Quad8
Definition Quad.h:29
TemplateElement< MeshLib::PyramidRule13 > Pyramid13
Definition Pyramid.h:26
MeshItemType
Definition Location.h:21
TemplateElement< MeshLib::QuadRule4 > Quad
Definition Quad.h:28
TemplateElement< MeshLib::TriRule3 > Tri
Definition Tri.h:26
TemplateElement< MeshLib::PyramidRule5 > Pyramid
Definition Pyramid.h:25
TemplateElement< MeshLib::PrismRule6 > Prism
Definition Prism.h:25
TemplateElement< PointRule1 > Point
Definition Point.h:20
TemplateElement< MeshLib::PrismRule15 > Prism15
Definition Prism.h:26
TemplateElement< MeshLib::TriRule6 > Tri6
Definition Tri.h:27
static constexpr char const * toString(const MeshItemType t)
Returns a char array for a specific MeshItemType.
Definition Location.h:28
TemplateElement< MeshLib::LineRule3 > Line3
Definition Line.h:26
TemplateElement< MeshLib::HexRule8 > Hex
Definition Hex.h:25
MPI_Comm communicator
Definition MPI.h:60
struct NodeData used for parallel reading and also partitioning
Definition NodeData.h:18
std::size_t index
Global node index.
Definition NodeData.h:26
unsigned long number_of_nodes
0: Number of all nodes of a partition,
unsigned long number_of_global_nodes
7: Number of all nodes of global mesh,