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/RunTime.h"
27#include "MeshLib/MeshEnums.h"
28#include "MeshLib/Properties.h"
29
30// Check if the value can by converted to given type without overflow.
31template <typename VALUE, typename TYPE>
32bool is_safely_convertable(VALUE const& value)
33{
34 bool const result = value <= std::numeric_limits<TYPE>::max();
35 if (!result)
36 {
37 ERR("The value {:d} is too large for conversion.", value);
38 ERR("Maximum available size is {:d}.",
39 std::numeric_limits<TYPE>::max());
40 }
41 return result;
42}
43
44namespace MeshLib
45{
46namespace IO
47{
49 : _mpi_comm(comm)
50{
51 MPI_Comm_size(_mpi_comm, &_mpi_comm_size);
52 MPI_Comm_rank(_mpi_comm, &_mpi_rank);
53
55}
56
61
63{
64 int const count = 2;
65 int blocks[count] = {1, 3};
66 MPI_Datatype types[count] = {MPI_UNSIGNED_LONG, MPI_DOUBLE};
67 MPI_Aint displacements[count] = {0, sizeof(NodeData::index)};
68
69 MPI_Type_create_struct(count, blocks, displacements, types,
71 MPI_Type_commit(&_mpi_node_type);
72}
73
75 const std::string& file_name_base)
76{
77 BaseLib::RunTime timer;
78 timer.start();
79
80 // Always try binary file first
81 std::string const fname_new = file_name_base + "_partitioned_msh_cfg" +
82 std::to_string(_mpi_comm_size) + ".bin";
83
84 if (!BaseLib::IsFileExisting(fname_new)) // binary file does not exist.
85 {
86 std::string const fname_ascii = file_name_base +
87 "_partitioned_msh_cfg" +
88 std::to_string(_mpi_comm_size) + ".msh";
89 if (BaseLib::IsFileExisting(fname_ascii))
90 {
91 ERR("Reading of ASCII mesh file {:s} is not supported since OGS "
92 "version 6.3.3.",
93 fname_ascii);
94 }
95 OGS_FATAL("Required binary file {:s} does not exist.\n", fname_new);
96 }
97
98 INFO("Reading corresponding part of mesh data from binary file {:s} ...",
99 file_name_base);
100
101 MeshLib::NodePartitionedMesh* mesh = readMesh(file_name_base);
102
103 INFO("[time] Reading the mesh took {:f} s.", timer.elapsed());
104
105 MPI_Barrier(_mpi_comm);
106
107 return mesh;
108}
109
110template <typename DATA>
111bool NodePartitionedMeshReader::readDataFromFile(std::string const& filename,
112 MPI_Offset offset,
113 MPI_Datatype type,
114 DATA& data) const
115{
116 // Check container size
117 if (!is_safely_convertable<std::size_t, int>(data.size()))
118 {
119 ERR("The container size is too large for MPI_File_read() call.");
120 return false;
121 }
122
123 // Open file
124 MPI_File file;
125
126 char* filename_char = const_cast<char*>(filename.data());
127 int const file_status = MPI_File_open(
128 _mpi_comm, filename_char, MPI_MODE_RDONLY, MPI_INFO_NULL, &file);
129
130 if (file_status != 0)
131 {
132 ERR("Error opening file {:s}. MPI error code {:d}", filename,
133 file_status);
134 return false;
135 }
136
137 // Read data
138 char file_mode[] = "native";
139 MPI_File_set_view(file, offset, type, type, file_mode, MPI_INFO_NULL);
140 // The static cast is checked above.
141 MPI_File_read(file, data.data(), static_cast<int>(data.size()), type,
142 MPI_STATUS_IGNORE);
143 MPI_File_close(&file);
144
145 return true;
146}
147
149 const std::string& file_name_base)
150{
151 //----------------------------------------------------------------------------------
152 // Read headers
153 const std::string fname_header = file_name_base + "_partitioned_msh_";
154 const std::string fname_num_p_ext = std::to_string(_mpi_comm_size) + ".bin";
155
156 // Read the config meta data from *cfg* file into struct PartitionedMeshInfo
157 // _mesh_info
158 if (!readDataFromFile(
159 fname_header + "cfg" + fname_num_p_ext,
160 static_cast<MPI_Offset>(static_cast<unsigned>(_mpi_rank) *
161 sizeof(_mesh_info)),
162 MPI_LONG, _mesh_info))
163 return nullptr;
164
165 //----------------------------------------------------------------------------------
166 // Read Nodes
167 std::vector<NodeData> nodes(_mesh_info.number_of_nodes);
168
169 if (!readDataFromFile(fname_header + "nod" + fname_num_p_ext,
170 static_cast<MPI_Offset>(_mesh_info.offset[2]),
171 _mpi_node_type, nodes))
172 return nullptr;
173
174 std::vector<MeshLib::Node*> mesh_nodes;
175 std::vector<unsigned long> glb_node_ids;
176 setNodes(nodes, mesh_nodes, glb_node_ids);
177
178 //----------------------------------------------------------------------------------
179 // Read non-ghost elements
180 std::vector<unsigned long> elem_data(_mesh_info.number_of_regular_elements +
181 _mesh_info.offset[0]);
182 if (!readDataFromFile(fname_header + "ele" + fname_num_p_ext,
183 static_cast<MPI_Offset>(_mesh_info.offset[3]),
184 MPI_LONG, elem_data))
185 return nullptr;
186
187 std::vector<MeshLib::Element*> mesh_elems(
190 setElements(mesh_nodes, elem_data, mesh_elems);
191
192 //----------------------------------------------------------------------------------
193 // Read ghost element
194 std::vector<unsigned long> ghost_elem_data(
196
197 if (!readDataFromFile(fname_header + "ele_g" + fname_num_p_ext,
198 static_cast<MPI_Offset>(_mesh_info.offset[4]),
199 MPI_LONG, ghost_elem_data))
200 return nullptr;
201
202 const bool process_ghost = true;
203 setElements(mesh_nodes, ghost_elem_data, mesh_elems, process_ghost);
204
205 //----------------------------------------------------------------------------------
206 // read the properties
207 MeshLib::Properties p(readProperties(file_name_base));
208
209 return newMesh(BaseLib::extractBaseName(file_name_base), mesh_nodes,
210 glb_node_ids, mesh_elems, p);
211}
212
214 const std::string& file_name_base) const
215{
220 return p;
221}
222
224 const std::string& file_name_base, MeshLib::MeshItemType t,
225 MeshLib::Properties& p) const
226{
227 const std::string item_type = MeshLib::toString(t);
228
229 const std::string fname_cfg = file_name_base + "_partitioned_" + item_type +
230 "_properties_cfg" +
231 std::to_string(_mpi_comm_size) + ".bin";
232 std::ifstream is(fname_cfg.c_str(), std::ios::binary | std::ios::in);
233 if (!is)
234 {
235 WARN(
236 "Could not open file '{:s}'.\n"
237 "\tYou can ignore this warning if the mesh does not contain {:s}-"
238 "wise property data.",
239 fname_cfg, item_type.data());
240 return;
241 }
242 std::size_t number_of_properties = 0;
243 is.read(reinterpret_cast<char*>(&number_of_properties),
244 sizeof(std::size_t));
245 std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> vec_pvmd(
246 number_of_properties);
247 for (std::size_t i(0); i < number_of_properties; ++i)
248 {
250 if (!vec_pvmd[i])
251 {
252 OGS_FATAL(
253 "Error in NodePartitionedMeshReader::readProperties: "
254 "Could not read the meta data for the PropertyVector {:d}",
255 i);
256 }
257 }
258 for (std::size_t i(0); i < number_of_properties; ++i)
259 {
261 }
262 auto pos = is.tellg();
263 auto offset =
264 static_cast<long>(pos) +
265 static_cast<long>(_mpi_rank *
267 is.seekg(offset);
268 std::optional<MeshLib::IO::PropertyVectorPartitionMetaData> pvpmd(
270 bool pvpmd_read_ok = static_cast<bool>(pvpmd);
271 bool all_pvpmd_read_ok;
272 MPI_Allreduce(&pvpmd_read_ok, &all_pvpmd_read_ok, 1, MPI_C_BOOL, MPI_LOR,
273 _mpi_comm);
274 if (!all_pvpmd_read_ok)
275 {
276 OGS_FATAL(
277 "Error in NodePartitionedMeshReader::readProperties: "
278 "Could not read the partition meta data for the mpi process {:d}",
279 _mpi_rank);
280 }
281 DBUG("offset in the PropertyVector: {:d}", pvpmd->offset);
282 DBUG("{:d} tuples in partition.", pvpmd->number_of_tuples);
283 is.close();
284
285 const std::string fname_val = file_name_base + "_partitioned_" + item_type +
286 "_properties_val" +
287 std::to_string(_mpi_comm_size) + ".bin";
288 is.open(fname_val.c_str(), std::ios::binary | std::ios::in);
289 if (!is)
290 {
291 ERR("Could not open file '{:s}'\n."
292 "\tYou can ignore this warning if the mesh does not contain {:s}-"
293 "wise property data.",
294 fname_val, item_type.data());
295 }
296
297 readDomainSpecificPartOfPropertyVectors(vec_pvmd, *pvpmd, t, is, p);
298}
299
301 std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> const&
302 vec_pvmd,
305 std::istream& is,
306 MeshLib::Properties& p) const
307{
308 unsigned long global_offset = 0;
309 std::size_t const number_of_properties = vec_pvmd.size();
310 for (std::size_t i(0); i < number_of_properties; ++i)
311 {
312 DBUG("global offset: {:d}, offset within the PropertyVector: {:d}.",
313 global_offset,
314 global_offset + pvpmd.offset * vec_pvmd[i]->number_of_components *
315 vec_pvmd[i]->data_type_size_in_bytes);
316
317 // Special field data such as OGS_VERSION, IntegrationPointMetaData,
318 // etc., which are not "real" integration points, are copied "as is"
319 // (i.e. fully) for every partition.
320 if (vec_pvmd[i]->property_name.find("_ip") == std::string::npos &&
322 {
323 createSpecificPropertyVectorPart<char>(is, *vec_pvmd[i], t,
324 global_offset, p);
325
326 global_offset += vec_pvmd[i]->data_type_size_in_bytes *
327 vec_pvmd[i]->number_of_tuples;
328 continue;
329 }
330
331 if (vec_pvmd[i]->is_int_type)
332 {
333 if (vec_pvmd[i]->is_data_type_signed)
334 {
335 if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(char))
336 {
337 createPropertyVectorPart<char>(is, *vec_pvmd[i], pvpmd, t,
338 global_offset, p);
339 }
340 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(int))
341 {
342 createPropertyVectorPart<int>(is, *vec_pvmd[i], pvpmd, t,
343 global_offset, p);
344 }
345 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(long))
346 createPropertyVectorPart<long>(is, *vec_pvmd[i], pvpmd, t,
347 global_offset, p);
348 else
349 {
350 WARN(
351 "Implementation for reading signed integer property "
352 "vector '{:s}' is not available.",
353 vec_pvmd[i]->property_name);
354 }
355 }
356 else
357 {
358 if (vec_pvmd[i]->data_type_size_in_bytes ==
359 sizeof(unsigned char))
360 {
361 createPropertyVectorPart<unsigned char>(
362 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
363 }
364 else if (vec_pvmd[i]->data_type_size_in_bytes ==
365 sizeof(unsigned int))
366 createPropertyVectorPart<unsigned int>(
367 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
368 else if (vec_pvmd[i]->data_type_size_in_bytes ==
369 sizeof(unsigned long))
370 createPropertyVectorPart<unsigned long>(
371 is, *vec_pvmd[i], pvpmd, t, global_offset, p);
372 else
373 {
374 WARN(
375 "Implementation for reading unsigned property vector "
376 "'{:s}' is not available.",
377 vec_pvmd[i]->property_name);
378 }
379 }
380 }
381 else
382 {
383 if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(float))
384 createPropertyVectorPart<float>(is, *vec_pvmd[i], pvpmd, t,
385 global_offset, p);
386 else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(double))
387 createPropertyVectorPart<double>(is, *vec_pvmd[i], pvpmd, t,
388 global_offset, p);
389 else
390 {
391 WARN(
392 "Implementation for reading floating point property vector "
393 "'{:s}' is not available.",
394 vec_pvmd[i]->property_name);
395 }
396 }
397 global_offset += vec_pvmd[i]->data_type_size_in_bytes *
398 vec_pvmd[i]->number_of_tuples *
399 vec_pvmd[i]->number_of_components;
400 }
401}
402
404 std::string const& mesh_name,
405 std::vector<MeshLib::Node*> const& mesh_nodes,
406 std::vector<unsigned long> const& glb_node_ids,
407 std::vector<MeshLib::Element*> const& mesh_elems,
408 MeshLib::Properties const& properties) const
409{
410 std::vector<std::size_t> gathered_n_regular_base_nodes(_mpi_comm_size);
411
413 1,
414 MPI_UNSIGNED_LONG,
415 gathered_n_regular_base_nodes.data(),
416 1,
417 MPI_UNSIGNED_LONG,
418 _mpi_comm);
419
420 std::vector<std::size_t> n_regular_base_nodes_at_rank;
421 n_regular_base_nodes_at_rank.push_back(0);
422 std::partial_sum(begin(gathered_n_regular_base_nodes),
423 end(gathered_n_regular_base_nodes),
424 back_inserter(n_regular_base_nodes_at_rank));
425
426 std::vector<std::size_t> gathered_n_regular_high_order_nodes(
428 std::size_t const n_regular_high_order_nodes =
431 MPI_Allgather(&n_regular_high_order_nodes,
432 1,
433 MPI_UNSIGNED_LONG,
434 gathered_n_regular_high_order_nodes.data(),
435 1,
436 MPI_UNSIGNED_LONG,
437 _mpi_comm);
438
439 std::vector<std::size_t> n_regular_high_order_nodes_at_rank;
440 n_regular_high_order_nodes_at_rank.push_back(0);
441 std::partial_sum(begin(gathered_n_regular_high_order_nodes),
442 end(gathered_n_regular_high_order_nodes),
443 back_inserter(n_regular_high_order_nodes_at_rank));
444
446 mesh_name, mesh_nodes, glb_node_ids, mesh_elems, properties,
449 std::move(n_regular_base_nodes_at_rank),
450 std::move(n_regular_high_order_nodes_at_rank));
451}
452
454 const std::vector<NodeData>& node_data,
455 std::vector<MeshLib::Node*>& mesh_node,
456 std::vector<unsigned long>& glb_node_ids) const
457{
458 mesh_node.resize(_mesh_info.number_of_nodes);
459 glb_node_ids.resize(_mesh_info.number_of_nodes);
460
461 for (std::size_t i = 0; i < mesh_node.size(); i++)
462 {
463 NodeData const& nd = node_data[i];
464 glb_node_ids[i] = nd.index;
465 mesh_node[i] = new MeshLib::Node(nd.x, nd.y, nd.z, i);
466 }
467}
468
470 const std::vector<MeshLib::Node*>& mesh_nodes,
471 const std::vector<unsigned long>& elem_data,
472 std::vector<MeshLib::Element*>& mesh_elems, const bool ghost) const
473{
474 // Number of elements, either ghost or regular
475 unsigned long const ne = ghost ? _mesh_info.number_of_ghost_elements
477 unsigned long const id_offset_ghost =
479
480 for (unsigned long i = 0; i < ne; i++)
481 {
482 unsigned long id_offset_elem = elem_data[i];
483
484 // Unused for now, keep for elem_data documentation purpose here.
485 {
486 const unsigned mat_idx =
487 static_cast<unsigned>(elem_data[id_offset_elem++]);
488 (void)mat_idx;
489 }
490 const unsigned long e_type = elem_data[id_offset_elem++];
491 unsigned long const nnodes = elem_data[id_offset_elem++];
492
493 MeshLib::Node** elem_nodes = new MeshLib::Node*[nnodes];
494 for (unsigned long k = 0; k < nnodes; k++)
495 elem_nodes[k] = mesh_nodes[elem_data[id_offset_elem++]];
496
497 // The element types below are defined by the MeshLib::CellType.
498 switch (static_cast<CellType>(e_type))
499 {
500 case CellType::POINT1:
501 mesh_elems[i + id_offset_ghost] =
502 new MeshLib::Point(elem_nodes);
503 break;
504 case CellType::LINE2:
505 mesh_elems[i + id_offset_ghost] = new MeshLib::Line(elem_nodes);
506 break;
507 case CellType::LINE3:
508 mesh_elems[i + id_offset_ghost] =
509 new MeshLib::Line3(elem_nodes);
510 break;
511 case CellType::QUAD4:
512 mesh_elems[i + id_offset_ghost] = new MeshLib::Quad(elem_nodes);
513 break;
514 case CellType::QUAD8:
515 mesh_elems[i + id_offset_ghost] =
516 new MeshLib::Quad8(elem_nodes);
517 break;
518 case CellType::QUAD9:
519 mesh_elems[i + id_offset_ghost] =
520 new MeshLib::Quad9(elem_nodes);
521 break;
522 case CellType::HEX8:
523 mesh_elems[i + id_offset_ghost] = new MeshLib::Hex(elem_nodes);
524 break;
525 case CellType::HEX20:
526 mesh_elems[i + id_offset_ghost] =
527 new MeshLib::Hex20(elem_nodes);
528 break;
529 case CellType::HEX27:
530 OGS_FATAL(
531 "NodePartitionedMeshReader: construction of HEX27 element "
532 "with id {:d} is not implemented.",
533 i);
534 break;
535 case CellType::TRI3:
536 mesh_elems[i + id_offset_ghost] = new MeshLib::Tri(elem_nodes);
537 break;
538 case CellType::TRI6:
539 mesh_elems[i + id_offset_ghost] = new MeshLib::Tri6(elem_nodes);
540 break;
541 case CellType::TET4:
542 mesh_elems[i + id_offset_ghost] = new MeshLib::Tet(elem_nodes);
543 break;
544 case CellType::TET10:
545 mesh_elems[i + id_offset_ghost] =
546 new MeshLib::Tet10(elem_nodes);
547 break;
548 case CellType::PRISM6:
549 mesh_elems[i + id_offset_ghost] =
550 new MeshLib::Prism(elem_nodes);
551 break;
553 mesh_elems[i + id_offset_ghost] =
554 new MeshLib::Prism15(elem_nodes);
555 break;
557 mesh_elems[i + id_offset_ghost] =
558 new MeshLib::Pyramid(elem_nodes);
559 break;
561 mesh_elems[i + id_offset_ghost] =
562 new MeshLib::Pyramid13(elem_nodes);
563 break;
565 OGS_FATAL(
566 "NodePartitionedMeshReader: construction of INVALID "
567 "element type with id {:d} is not possible.",
568 i);
569 break;
570 default:
571 OGS_FATAL(
572 "NodePartitionedMeshReader: construction of element type "
573 "{:d} is not implemented.",
574 e_type);
575 }
576 }
577}
578} // namespace IO
579} // 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 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.
int _mpi_comm_size
Number of processes in the communicator: _mpi_comm.
MPI_Comm _mpi_comm
Pointer to MPI communicator.
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.
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
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists.
Definition FileTools.cpp:47
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
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,