OGS
NodeWiseMeshPartitioner.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <limits>
7#include <numeric>
8#include <range/v3/algorithm/transform.hpp>
9#include <range/v3/range/conversion.hpp>
10#include <unordered_map>
11
12#include "BaseLib/Error.h"
13#include "BaseLib/FileTools.h"
14#include "BaseLib/Logging.h"
15#include "BaseLib/RunTime.h"
17#include "MeshLib/IO/NodeData.h"
19#include "MeshLib/MeshEnums.h"
22
23namespace ApplicationUtils
24{
26 MeshLib::MeshItemType const item_type) const
27{
28 switch (item_type)
29 {
31 return nodes.size();
33 return elements.size();
36 default:
37 OGS_FATAL("Unsupported MeshItemType {:s}.",
38 MeshLib::toString(item_type));
39 }
40}
41
43 std::ostream& os, std::vector<std::size_t> const& global_node_ids) const
44{
45 std::vector<MeshLib::IO::NodeData> nodes_buffer;
46 nodes_buffer.reserve(nodes.size());
47
48 for (const auto* node : nodes)
49 {
50 double const* coords = node->data();
51 nodes_buffer.emplace_back(global_node_ids[node->getID()], coords[0],
52 coords[1], coords[2]);
53 }
54 return os.write(reinterpret_cast<const char*>(nodes_buffer.data()),
55 sizeof(MeshLib::IO::NodeData) * nodes_buffer.size());
56}
57
63 std::vector<const MeshLib::Element*> const& elements)
64{
65 return 3 * elements.size() +
66 std::accumulate(begin(elements), end(elements), 0,
67 [](auto const nnodes, auto const* e)
68 { return nnodes + e->getNumberOfNodes(); });
69}
70
71std::ostream& Partition::writeConfig(std::ostream& os) const
72{
73 long const data[] = {
74 static_cast<long>(nodes.size()),
75 static_cast<long>(number_of_base_nodes),
76 static_cast<long>(elements.size()),
77 // Ghost elements are no longer used; kept as zero for a
78 // format-compatible binary layout.
79 0,
80 static_cast<long>(number_of_regular_base_nodes),
81 static_cast<long>(number_of_regular_nodes),
82 static_cast<long>(number_of_mesh_base_nodes),
83 static_cast<long>(number_of_mesh_all_nodes),
85 // Integer-variable count of the (empty) ghost element section.
86 0,
87 };
88
89 return os.write(reinterpret_cast<const char*>(data), sizeof(data));
90}
91
92std::size_t partitionLookup(std::size_t const& node_id,
93 std::vector<std::size_t> const& partition_ids,
94 std::span<std::size_t const> const node_id_mapping)
95{
96 return partition_ids[node_id_mapping[node_id]];
97}
98
99std::pair<std::vector<MeshLib::Node const*>, std::vector<MeshLib::Node const*>>
100splitIntoBaseAndHigherOrderNodes(std::vector<MeshLib::Node const*> const& nodes,
101 MeshLib::Mesh const& mesh)
102{
103 // Space for resulting vectors.
104 std::vector<MeshLib::Node const*> base_nodes;
105 // if linear mesh, then one reallocation, no realloc for higher order
106 // elements meshes.
107 base_nodes.reserve(nodes.size() / 2);
108 std::vector<MeshLib::Node const*> higher_order_nodes;
109 // if linear mesh, then wasted space, good estimate for quadratic
110 // order mesh, and realloc needed for higher order element meshes.
111 higher_order_nodes.reserve(nodes.size() / 2);
112
113 // Split the nodes into base nodes and extra nodes.
114 std::partition_copy(
115 begin(nodes), end(nodes), std::back_inserter(base_nodes),
116 std::back_inserter(higher_order_nodes),
117 [&](MeshLib::Node const* const n)
118 { return isBaseNode(*n, mesh.getElementsConnectedToNode(*n)); });
119
120 return {base_nodes, higher_order_nodes};
121}
122
126std::tuple<std::vector<MeshLib::Node*>, std::vector<MeshLib::Node*>>
127findGhostNodesInPartition(std::size_t const part_id,
128 std::vector<MeshLib::Node*> const& nodes,
129 std::vector<MeshLib::Element const*> const& elements,
130 std::vector<std::size_t> const& partition_ids,
131 MeshLib::Mesh const& mesh,
132 std::span<std::size_t const> const node_id_mapping)
133{
134 std::vector<MeshLib::Node*> base_ghost_nodes;
135 std::vector<MeshLib::Node*> higher_order_ghost_nodes;
136
137 std::vector<bool> is_ghost_node(nodes.size(), false);
138 for (const auto* elem : elements)
139 {
140 for (unsigned i = 0; i < elem->getNumberOfNodes(); i++)
141 {
142 auto const& n = elem->getNode(i);
143 auto const node_id = n->getID();
144 if (is_ghost_node[node_id])
145 {
146 continue;
147 }
148
149 if (partitionLookup(node_id, partition_ids, node_id_mapping) !=
150 part_id)
151 {
152 if (isBaseNode(*n, mesh.getElementsConnectedToNode(*n)))
153 {
154 base_ghost_nodes.push_back(nodes[node_id]);
155 }
156 else
157 {
158 higher_order_ghost_nodes.push_back(nodes[node_id]);
159 }
160 is_ghost_node[node_id] = true;
161 }
162 }
163 }
164 return std::tuple<std::vector<MeshLib::Node*>, std::vector<MeshLib::Node*>>{
165 base_ghost_nodes, higher_order_ghost_nodes};
166}
167
170template <typename T>
172 Partition const& p,
173 std::size_t const offset,
175 MeshLib::PropertyVector<T>& partitioned_pv)
176{
177 auto const& nodes = p.nodes;
178 auto const nnodes = nodes.size();
179 auto const n_components = pv.getNumberOfGlobalComponents();
180 for (std::size_t i = 0; i < nnodes; ++i)
181 {
182 const auto global_id = nodes[i]->getID();
183 std::copy_n(&pv[n_components * global_id], n_components,
184 &partitioned_pv[offset + n_components * i]);
185 }
186 return n_components * nnodes;
187}
188
191template <typename T>
193 Partition const& p,
194 std::size_t const offset,
196 MeshLib::PropertyVector<T>& partitioned_pv)
197{
198 std::size_t const n_elements(p.elements.size());
199 auto const n_components = pv.getNumberOfGlobalComponents();
200 for (std::size_t i = 0; i < n_elements; ++i)
201 {
202 const auto id = p.elements[i]->getID();
203 std::copy_n(&pv[n_components * id], n_components,
204 &partitioned_pv[offset + n_components * i]);
205 }
206 return n_components * n_elements;
207}
208
212template <typename T>
214 MeshLib::Properties const& properties,
215 Partition const& p,
216 std::size_t const id_offset_partition,
217 std::vector<std::size_t> const& element_ip_data_offsets,
219 MeshLib::PropertyVector<T>& partitioned_pv)
220{
221 // Special field data such as OGS_VERSION, IntegrationPointMetaData,
222 // etc., which are not "real" integration points, are copied "as is"
223 // (i.e. fully) for every partition.
224 if (pv.getPropertyName().find("_ip") == std::string::npos)
225 {
226 std::copy_n(&pv[0], pv.size(), &partitioned_pv[id_offset_partition]);
227 return pv.size();
228 }
229
230 auto const n_components = pv.getNumberOfGlobalComponents();
231
232 std::size_t id_offset = 0;
233
234 auto const ip_meta_data = MeshLib::getIntegrationPointMetaDataSingleField(
236 auto copyFieldData =
237 [&](std::vector<const MeshLib::Element*> const& elements)
238 {
239 for (auto const element : elements)
240 {
241 int const number_of_element_field_data =
243 *element) *
244 n_components;
245 // The original element ID is not changed.
246 auto const element_id = element->getID();
247 int const begin_pos = element_ip_data_offsets[element_id];
248 int const end_pos = element_ip_data_offsets[element_id + 1];
249
250 std::copy(pv.begin() + begin_pos, pv.begin() + end_pos,
251 &partitioned_pv[id_offset + id_offset_partition]);
252 id_offset += number_of_element_field_data;
253 }
254 };
255
256 copyFieldData(p.elements);
257
258 return id_offset;
259}
260
262 std::vector<Partition>& partitions)
263{
264 auto const& opt_ip_meta_data_all =
266 for (auto const& [name, property] : properties)
267 {
268 auto const item_type = property->getMeshItemType();
269
271 {
272 continue;
273 }
274
275 // For special field data such as OGS_VERSION, IntegrationPointMetaData,
276 // etc., which are not "real" integration points:
277 if (property->getPropertyName().find("_ip") == std::string::npos)
278 {
279 continue;
280 }
281
282 auto const& ip_meta_data =
284 opt_ip_meta_data_all, property->getPropertyName());
285 auto countIntegrationPoints =
286 [&](std::vector<const MeshLib::Element*> const& elements)
287 {
288 std::size_t counter = 0;
289 for (auto const element : elements)
290 {
291 int const number_of_integration_points =
293 ip_meta_data, *element);
294 counter += number_of_integration_points;
295 }
296 return counter;
297 };
298
299 for (auto& p : partitions)
300 {
301 p.number_of_integration_points = countIntegrationPoints(p.elements);
302 }
303 return;
304 }
305}
306
307template <typename T>
309 std::vector<MeshLib::Element*> const& global_mesh_elements,
310 MeshLib::Properties& partitioned_properties,
311 MeshLib::Properties const& properties,
312 std::vector<Partition> const& partitions,
313 MeshLib::PropertyVector<T> const* const pv,
314 std::map<MeshLib::MeshItemType, std::size_t> const& total_number_of_tuples)
315{
316 if (pv == nullptr)
317 {
318 return false;
319 }
320 auto const item_type = pv->getMeshItemType();
321
322 std::size_t partitioned_pv_size = total_number_of_tuples.at(item_type) *
324
325 std::vector<std::size_t> element_ip_data_offsets;
327 {
328 // Special field data such as OGS_VERSION, IntegrationPointMetaData,
329 // etc., which are not "real" integration points, are copied "as is"
330 // (i.e. fully) for every partition.
331 if (pv->getPropertyName().find("_ip") == std::string::npos)
332 {
333 partitioned_pv_size = pv->size() * partitions.size();
334 }
335
336 element_ip_data_offsets =
338 global_mesh_elements, *pv, properties);
339 }
340
341 auto partitioned_pv = partitioned_properties.createNewPropertyVector<T>(
342 pv->getPropertyName(), pv->getMeshItemType(),
344 if (partitioned_pv == nullptr)
345 {
346 OGS_FATAL(
347 "Could not create partitioned property vector {:s} for {} data "
348 "array.",
350 }
351 partitioned_pv->resize(partitioned_pv_size);
352
353 auto copy_property_vector_values =
354 [&](Partition const& p, std::size_t offset)
355 {
357 {
358 return copyFieldPropertyDataToPartitions(properties, p, offset,
359 element_ip_data_offsets,
360 *pv, *partitioned_pv);
361 }
362
363 if (item_type == MeshLib::MeshItemType::Node)
364 {
365 return copyNodePropertyVectorValues(p, offset, *pv,
366 *partitioned_pv);
367 }
368 if (item_type == MeshLib::MeshItemType::Cell)
369 {
370 return copyCellPropertyVectorValues(p, offset, *pv,
371 *partitioned_pv);
372 }
373
374 OGS_FATAL(
375 "Copying of property vector values for mesh item type {:s} is not "
376 "implemented.",
377 toString(item_type));
378 };
379
380 std::size_t position_offset(0);
381 for (auto p : partitions)
382 {
383 position_offset += copy_property_vector_values(p, position_offset);
384 }
385 return true;
386}
387
388void addVtkGhostTypeProperty(MeshLib::Properties& partitioned_properties,
389 std::vector<Partition> const& partitions,
390 std::size_t const total_number_of_cells)
391{
392 auto* vtk_ghost_type =
393 partitioned_properties.createNewPropertyVector<unsigned char>(
395 total_number_of_cells, 1);
396 if (vtk_ghost_type == nullptr)
397 {
398 OGS_FATAL("Could not create '{}' cell data array.",
400 }
401
402 if (vtk_ghost_type->size() != total_number_of_cells)
403 {
404 OGS_FATAL("Size mismatch: '{}' has size {}, expected {}.",
405 MeshLib::vtkGhostTypeString, vtk_ghost_type->size(),
406 total_number_of_cells);
407 }
408
409 // A cell that crosses partition boundaries is contained in every partition
410 // it touches. The first occurrence in partition iteration order is kept as
411 // a regular cell; every later occurrence is flagged as a duplicate so that
412 // merging tools (e.g. PVTU2VTU) keep a single copy.
413 // Cell ids are dense in [0, number of global cells), and the total number
414 // of cells over all partitions counts every shared cell at least once, so
415 // it is a valid upper bound for the largest cell id.
416 std::vector<bool> cell_was_visited(total_number_of_cells, false);
417 std::size_t offset = 0;
418 for (auto const& partition : partitions)
419 {
420 for (std::size_t i = 0; i < partition.elements.size(); ++i)
421 {
422 auto const cell_id = partition.elements[i]->getID();
423 if (cell_was_visited[cell_id])
424 {
425 (*vtk_ghost_type)[offset + i] |=
426 vtkDataSetAttributes::DUPLICATECELL;
427 }
428 else
429 {
430 cell_was_visited[cell_id] = true;
431 }
432 }
433 offset += partition.elements.size();
434 }
435}
436
439 std::unique_ptr<MeshLib::Mesh> const& mesh,
440 std::vector<Partition>& partitions)
441{
442 using namespace MeshLib;
443
444 MeshLib::Properties const& properties = mesh->getProperties();
445
446 // Count the number of integration point data of all partitions:
447 setIntegrationPointNumberOfPartition(properties, partitions);
448
449 Properties partitioned_properties;
450 auto count_tuples = [&](MeshItemType const mesh_item_type)
451 {
452 return std::accumulate(
453 begin(partitions), end(partitions), 0,
454 [&](std::size_t const sum, Partition const& p)
455 { return sum + p.numberOfMeshItems(mesh_item_type); });
456 };
457
458 std::map<MeshItemType, std::size_t> const total_number_of_tuples = {
459 {MeshItemType::Cell, count_tuples(MeshItemType::Cell)},
460 {MeshItemType::Node, count_tuples(MeshItemType::Node)},
462 count_tuples(MeshItemType::IntegrationPoint)}};
463
464 DBUG(
465 "total number of tuples after partitioning defined for cells is {:d} "
466 "and for nodes {:d} and for integration points {:d}.",
467 total_number_of_tuples.at(MeshItemType::Cell),
468 total_number_of_tuples.at(MeshItemType::Node),
469 total_number_of_tuples.at(MeshItemType::IntegrationPoint));
470
471 // 1 create new PV
472 // 2 resize the PV with total_number_of_tuples
473 // 3 copy the values according to the partition info
475 properties,
476 [&](auto type, auto const property)
477 {
479 mesh->getElements(), partitioned_properties, properties,
480 partitions,
481 dynamic_cast<PropertyVector<decltype(type)> const*>(property),
482 total_number_of_tuples);
483 });
484
485 addVtkGhostTypeProperty(partitioned_properties,
486 partitions,
487 total_number_of_tuples.at(MeshItemType::Cell));
488
489 return partitioned_properties;
490}
491
493 std::vector<MeshLib::Element*> const& global_mesh_elements,
494 MeshLib::Properties const& properties)
495{
496 auto const& opt_ip_meta_data_all =
498 for (auto const& [name, property] : properties)
499 {
500 auto const item_type = property->getMeshItemType();
501
503 {
504 continue;
505 }
506
507 // For special field data such as OGS_VERSION, IntegrationPointMetaData,
508 // etc., which are not "real" integration points:
509 if (property->getPropertyName().find("_ip") == std::string::npos)
510 {
511 continue;
512 }
513
514 std::size_t number_of_total_integration_points = 0;
515 auto const ip_meta_data =
517 opt_ip_meta_data_all, property->getPropertyName());
518 for (auto const element : global_mesh_elements)
519 {
520 int const number_of_integration_points =
522 *element);
523 number_of_total_integration_points += number_of_integration_points;
524 }
525
526 const auto pv =
527 dynamic_cast<MeshLib::PropertyVector<double> const*>(property);
528 std::size_t const component_number = pv->getNumberOfGlobalComponents();
529 if (pv->size() != number_of_total_integration_points * component_number)
530 {
531 OGS_FATAL(
532 "The property vector's size {:d} for integration point data "
533 "{:s} does not match its actual size {:d}. The field data in "
534 "the vtu file are wrong.",
535 pv->size(), name,
536 number_of_total_integration_points * component_number);
537 }
538 }
539}
540
541std::vector<std::vector<std::size_t>> computePartitionIDPerElement(
542 std::vector<std::size_t> const& node_partition_map,
543 std::vector<MeshLib::Element*> const& elements,
544 std::span<std::size_t const> const bulk_node_ids)
545{
546 auto node_partition_ids = ranges::views::transform(
547 [&](MeshLib::Element const* const element)
548 {
549 auto node_lookup = ranges::views::transform(
550 [&](std::size_t const i)
551 { return node_partition_map[bulk_node_ids[i]]; });
552
553 return element->nodes() | MeshLib::views::ids | node_lookup |
554 ranges::to<std::vector>;
555 });
556
557 return elements | node_partition_ids | ranges::to<std::vector>;
558}
559
561 std::vector<Partition>& partitions,
562 std::vector<std::size_t> const& nodes_partition_ids,
563 std::vector<MeshLib::Node*> const& nodes,
564 std::span<std::size_t const> const bulk_node_ids)
565{
566 for (auto const* const node : nodes)
567 {
568 partitions[nodes_partition_ids[bulk_node_ids[node->getID()]]]
569 .nodes.push_back(node);
570 }
571}
572
574 MeshLib::Mesh const& mesh)
575{
576 std::vector<MeshLib::Node const*> higher_order_nodes;
577 // after splitIntoBaseAndHigherOrderNodes() partition.nodes contains only
578 // base nodes
579 std::tie(partition.nodes, higher_order_nodes) =
581 partition.number_of_regular_base_nodes = partition.nodes.size();
582 std::copy(begin(higher_order_nodes), end(higher_order_nodes),
583 std::back_inserter(partition.nodes));
584 partition.number_of_regular_nodes = partition.nodes.size();
585}
586
588 std::vector<Partition>& partitions, MeshLib::Mesh const& mesh)
589{
590 for (auto& partition : partitions)
591 {
593 }
594}
595
596void setNumberOfNodesInPartitions(std::vector<Partition>& partitions,
597 MeshLib::Mesh const& mesh)
598{
599 auto const number_of_mesh_base_nodes = mesh.computeNumberOfBaseNodes();
600 auto const number_of_mesh_all_nodes = mesh.getNumberOfNodes();
601 for (auto& partition : partitions)
602 {
603 partition.number_of_regular_nodes = partition.nodes.size();
604 partition.number_of_mesh_base_nodes = number_of_mesh_base_nodes;
605 partition.number_of_mesh_all_nodes = number_of_mesh_all_nodes;
606 }
607}
608
610 std::vector<Partition>& partitions,
611 MeshLib::Mesh const& mesh,
612 std::vector<std::vector<std::size_t>> const& partition_ids_per_element)
613{
614 for (auto const& element : mesh.getElements())
615 {
616 auto const element_id = element->getID();
617 auto node_partition_ids = partition_ids_per_element[element_id];
618 // make partition ids unique
619 std::sort(node_partition_ids.begin(), node_partition_ids.end());
620 auto last =
621 std::unique(node_partition_ids.begin(), node_partition_ids.end());
622 node_partition_ids.erase(last, node_partition_ids.end());
623
624 // Add the element to every partition it touches. Elements whose nodes
625 // span multiple partitions are simply contained in each of these
626 // partitions; they are no longer distinguished as ghost elements.
627 for (auto const partition_id : node_partition_ids)
628 {
629 partitions[partition_id].elements.push_back(element);
630 }
631 }
632}
633
634// determine and append ghost nodes to partition.nodes in the following order
635// [base nodes, higher order nodes, base ghost nodes, higher order ghost
636// nodes]
638 std::vector<Partition>& partitions, MeshLib::Mesh const& mesh,
639 std::vector<std::size_t> const& nodes_partition_ids,
640 std::span<std::size_t const> const node_id_mapping)
641{
642 for (std::size_t part_id = 0; part_id < partitions.size(); part_id++)
643 {
644 auto& partition = partitions[part_id];
645 std::vector<MeshLib::Node*> base_ghost_nodes;
646 std::vector<MeshLib::Node*> higher_order_ghost_nodes;
647 std::tie(base_ghost_nodes, higher_order_ghost_nodes) =
648 findGhostNodesInPartition(part_id, mesh.getNodes(),
649 partition.elements, nodes_partition_ids,
650 mesh, node_id_mapping);
651
652 std::copy(begin(base_ghost_nodes), end(base_ghost_nodes),
653 std::back_inserter(partition.nodes));
654
655 partition.number_of_base_nodes =
656 partition.number_of_regular_base_nodes + base_ghost_nodes.size();
657
658 std::copy(begin(higher_order_ghost_nodes),
659 end(higher_order_ghost_nodes),
660 std::back_inserter(partition.nodes));
661 }
662}
663
664void partitionMesh(std::vector<Partition>& partitions,
665 MeshLib::Mesh const& mesh,
666 std::vector<std::size_t> const& nodes_partition_ids,
667 std::span<std::size_t const> const bulk_node_ids)
668{
669 BaseLib::RunTime run_timer;
670 run_timer.start();
671 auto const partition_ids_per_element = computePartitionIDPerElement(
672 nodes_partition_ids, mesh.getElements(), bulk_node_ids);
673 INFO("partitionMesh(): Partition IDs per element computed in {:g} s",
674 run_timer.elapsed());
675
676 run_timer.start();
677 distributeNodesToPartitions(partitions, nodes_partition_ids,
678 mesh.getNodes(), bulk_node_ids);
679 INFO("partitionMesh(): distribute nodes to partitions took {:g} s",
680 run_timer.elapsed());
681
682 run_timer.start();
684 INFO(
685 "partitionMesh(): sorting [base nodes | higher order nodes] took {:g} "
686 "s",
687 run_timer.elapsed());
688
689 run_timer.start();
690 setNumberOfNodesInPartitions(partitions, mesh);
691 INFO(
692 "partitionMesh(): setting number of nodes and of all mesh base nodes "
693 "took {:g} s",
694 run_timer.elapsed());
695
696 run_timer.start();
697 distributeElementsIntoPartitions(partitions, mesh,
698 partition_ids_per_element);
699 INFO("partitionMesh(): distribute elements into partitions took {:g} s",
700 run_timer.elapsed());
701
702 run_timer.start();
704 partitions, mesh, nodes_partition_ids, bulk_node_ids);
705 INFO("partitionMesh(): determine / append ghost nodes took {:g} s",
706 run_timer.elapsed());
707}
708
710{
711 std::vector<std::size_t> bulk_node_ids(_mesh->getNumberOfNodes());
712 std::iota(bulk_node_ids.begin(), bulk_node_ids.end(), 0);
713
715
717
718 // In case the field data in the vtu file are manually added, e.g. by using
719 // some tools, the size of the field property vector has to be checked.
720 checkFieldPropertyVectorSize(_mesh->getElements(), _mesh->getProperties());
721
723
725}
726
728 std::vector<Partition> const& partitions,
729 MeshLib::Properties& partitioned_properties)
730{
731 auto const bulk_node_ids_string =
733 if (partitioned_properties.hasPropertyVector(bulk_node_ids_string))
734 {
736 partitioned_properties.getPropertyVector<std::size_t>(
737 bulk_node_ids_string, MeshLib::MeshItemType::Node, 1),
738 partitions);
739 }
740 auto const bulk_element_ids_string =
742 if (partitioned_properties.hasPropertyVector<std::size_t>(
743 static_cast<std::string>(bulk_element_ids_string),
745 {
747 partitioned_properties.getPropertyVector<std::size_t>(
748 bulk_element_ids_string, MeshLib::MeshItemType::Cell, 1),
749 partitions);
750 }
751}
752
754 MeshLib::PropertyVector<std::size_t>* const bulk_node_ids_pv,
755 std::vector<Partition> const& local_partitions) const
756{
757 if (bulk_node_ids_pv == nullptr)
758 {
759 return;
760 }
761
762 auto& bulk_node_ids = *bulk_node_ids_pv;
763
764 std::size_t offset = 0; // offset in property vector for current partition
765
766 assert(_partitions.size() == local_partitions.size());
767 int const n_partitions = static_cast<int>(_partitions.size());
768 for (int partition_id = 0; partition_id < n_partitions; ++partition_id)
769 {
770 auto const& bulk_partition = _partitions[partition_id];
771 auto const& local_partition = local_partitions[partition_id];
772
773 // Create global-to-local node id mapping for the bulk partition.
774 auto const& bulk_nodes = bulk_partition.nodes;
775 auto const n_bulk_nodes = bulk_nodes.size();
776 std::map<std::size_t, std::size_t> global_to_local;
777 for (std::size_t local_node_id = 0; local_node_id < n_bulk_nodes;
778 ++local_node_id)
779 {
780 global_to_local[bulk_nodes[local_node_id]->getID()] = local_node_id;
781 }
782
783 auto const& local_nodes = local_partition.nodes;
784 auto const n_local_nodes = local_nodes.size();
785 for (std::size_t local_node_id = 0; local_node_id < n_local_nodes;
786 ++local_node_id)
787 {
788 bulk_node_ids[offset + local_node_id] =
789 global_to_local[bulk_node_ids[offset + local_node_id]];
790 }
791 offset += n_local_nodes;
792 }
793}
794
796 MeshLib::PropertyVector<std::size_t>* const bulk_element_ids_pv,
797 std::vector<Partition> const& local_partitions) const
798{
799 if (bulk_element_ids_pv == nullptr)
800 {
801 return;
802 }
803
804 auto& bulk_element_ids = *bulk_element_ids_pv;
805
806 std::size_t offset = 0; // offset in property vector for current partition
807
808 assert(_partitions.size() == local_partitions.size());
809 int const n_partitions = static_cast<int>(_partitions.size());
810 for (int partition_id = 0; partition_id < n_partitions; ++partition_id)
811 {
812 auto const& bulk_partition = _partitions[partition_id];
813 auto const& local_partition = local_partitions[partition_id];
814
815 // Create global-to-local element id mapping for the bulk partition.
816 std::map<std::size_t, std::size_t> global_to_local;
817 auto map_elements =
818 [&global_to_local](
819 std::vector<MeshLib::Element const*> const& elements,
820 std::size_t const offset)
821 {
822 auto const n_elements = elements.size();
823 for (std::size_t e = 0; e < n_elements; ++e)
824 {
825 global_to_local[elements[e]->getID()] = offset + e;
826 }
827 };
828
829 map_elements(bulk_partition.elements, 0);
830
831 // Renumber the local bulk_element_ids map.
832 auto renumber_elements =
833 [&bulk_element_ids, &global_to_local](
834 std::vector<MeshLib::Element const*> const& elements,
835 std::size_t const offset)
836 {
837 auto const n_elements = elements.size();
838 for (std::size_t e = 0; e < n_elements; ++e)
839 {
840 bulk_element_ids[offset + e] =
841 global_to_local[bulk_element_ids[offset + e]];
842 }
843 return n_elements;
844 };
845
846 offset += renumber_elements(local_partition.elements, offset);
847 }
848}
849
851 MeshLib::Mesh const& mesh) const
852{
853 auto const bulk_node_ids_string =
855 auto const& bulk_node_ids =
856 mesh.getProperties().getPropertyVector<std::size_t>(
857 bulk_node_ids_string, MeshLib::MeshItemType::Node, 1);
858
859 std::vector<Partition> partitions(_partitions.size());
860
861 partitionMesh(partitions, mesh, _nodes_partition_ids, *bulk_node_ids);
862
863 return partitions;
864}
865
867{
868 std::size_t node_global_id_offset = 0;
869 // Renumber the global indices.
870 for (auto& partition : _partitions)
871 {
872 for (std::size_t i = 0; i < partition.number_of_regular_nodes; i++)
873 {
874 _nodes_global_ids[partition.nodes[i]->getID()] =
875 node_global_id_offset++;
876 }
877 }
878}
879
880template <typename T>
881void writePropertyVectorValues(std::ostream& os,
883{
884 os.write(reinterpret_cast<const char*>(pv.data()), pv.size() * sizeof(T));
885}
886
887template <typename T>
889 MeshLib::MeshItemType const mesh_item_type,
890 std::ostream& out_val, std::ostream& out_meta)
891{
892 if (pv == nullptr)
893 {
894 return false;
895 }
896 // skip property of different mesh item type. Return true, because this
897 // operation was successful.
898 if (pv->getMeshItemType() != mesh_item_type)
899 {
900 return true;
901 }
902
904 pvmd.property_name = pv->getPropertyName();
908 writePropertyVectorValues(out_val, *pv);
910 return true;
911}
912
913void writeProperties(const std::string& file_name_base,
914 MeshLib::Properties const& partitioned_properties,
915 std::vector<Partition> const& partitions,
916 MeshLib::MeshItemType const mesh_item_type)
917{
918 auto const number_of_properties =
919 partitioned_properties.size(mesh_item_type);
920 if (number_of_properties == 0)
921 {
922 return;
923 }
924
925 auto const file_name_infix = toString(mesh_item_type);
926
927 auto const file_name_cfg = file_name_base + "_partitioned_" +
928 file_name_infix + "_properties_cfg" +
929 std::to_string(partitions.size()) + ".bin";
930 std::ofstream out(file_name_cfg, std::ios::binary);
931 if (!out)
932 {
933 OGS_FATAL("Could not open file '{:s}' for output.", file_name_cfg);
934 }
935
936 auto const file_name_val = file_name_base + "_partitioned_" +
937 file_name_infix + "_properties_val" +
938 std::to_string(partitions.size()) + ".bin";
939 std::ofstream out_val(file_name_val, std::ios::binary);
940 if (!out_val)
941 {
942 OGS_FATAL("Could not open file '{:s}' for output.", file_name_val);
943 }
944
945 BaseLib::writeValueBinary(out, number_of_properties);
946
948 partitioned_properties,
949 [&](auto type, auto const& property)
950 {
952 dynamic_cast<MeshLib::PropertyVector<decltype(type)> const*>(
953 property),
954 mesh_item_type, out_val, out);
955 });
956
957 unsigned long offset = 0;
958 for (const auto& partition : partitions)
959 {
961 offset, static_cast<unsigned long>(
962 partition.numberOfMeshItems(mesh_item_type))};
963 DBUG(
964 "Write meta data for node-based PropertyVector: global offset "
965 "{:d}, number of tuples {:d}",
966 pvpmd.offset, pvpmd.number_of_tuples);
968 offset += pvpmd.number_of_tuples;
969 }
970}
971
973{
977
978 std::ostream& writeConfig(std::ostream& os) const;
979};
980
981std::ostream& ConfigOffsets::writeConfig(std::ostream& os) const
982{
983 os.write(reinterpret_cast<const char*>(this), sizeof(ConfigOffsets));
984
985 static long reserved = 0; // Value reserved in the binary format, not used
986 // in the partitioning process.
987 return os.write(reinterpret_cast<const char*>(&reserved), sizeof(long));
988}
989
991{
992 long node;
994};
995
997{
998 return {static_cast<long>(partition.nodes.size()),
999 static_cast<long>(
1000 partition.elements.size() +
1002}
1003
1005 PartitionOffsets const& offsets)
1006{
1007 return {
1008 static_cast<long>(oldConfig.node_rank_offset +
1009 offsets.node * sizeof(MeshLib::IO::NodeData)),
1010 // Offset the ending entry of the element integer variables of
1011 // the elements of this partition in the vector of elem_info.
1012 static_cast<long>(oldConfig.element_rank_offset +
1013 offsets.elements * sizeof(long)),
1014
1015 // The ghost element section is empty, so this offset never advances.
1016 // It is still part of the binary format and therefore written as 0.
1017 0L};
1018}
1019
1023std::vector<long> writeConfigData(const std::string& file_name_base,
1024 std::vector<Partition> const& partitions)
1025{
1026 auto const file_name_cfg = file_name_base + "_partitioned_msh_cfg" +
1027 std::to_string(partitions.size()) + ".bin";
1028 std::ofstream of_bin_cfg(file_name_cfg, std::ios::binary);
1029 if (!of_bin_cfg)
1030 {
1031 OGS_FATAL("Could not open file '{:s}' for output.", file_name_cfg);
1032 }
1033
1034 std::vector<long> partitions_element_offsets;
1035 partitions_element_offsets.reserve(partitions.size());
1036
1037 ConfigOffsets config_offsets = {0, 0, 0}; // 0 for first partition.
1038 for (const auto& partition : partitions)
1039 {
1040 partition.writeConfig(of_bin_cfg);
1041
1042 config_offsets.writeConfig(of_bin_cfg);
1043 auto const& partition_offsets = computePartitionOffsets(partition);
1044 config_offsets =
1045 incrementConfigOffsets(config_offsets, partition_offsets);
1046
1047 partitions_element_offsets.push_back(partition_offsets.elements);
1048 }
1049
1050 return partitions_element_offsets;
1051}
1052
1061 const MeshLib::Element& elem,
1062 const std::unordered_map<std::size_t, long>& local_node_ids,
1063 std::vector<long>& elem_info,
1064 long& counter)
1065{
1066 constexpr unsigned mat_id =
1067 0; // TODO: Material ID to be set from the mesh data
1068 const long nn = elem.getNumberOfNodes();
1069 elem_info[counter++] = mat_id;
1070 elem_info[counter++] = static_cast<long>(elem.getCellType());
1071 elem_info[counter++] = nn;
1072
1073 for (long i = 0; i < nn; i++)
1074 {
1075 auto const& n = *elem.getNode(i);
1076 elem_info[counter++] = local_node_ids.at(n.getID());
1077 }
1078}
1079
1081std::unordered_map<std::size_t, long> enumerateLocalNodeIds(
1082 std::vector<MeshLib::Node const*> const& nodes)
1083{
1084 std::unordered_map<std::size_t, long> local_ids;
1085 local_ids.reserve(nodes.size());
1086
1087 long local_node_id = 0;
1088 for (const auto* node : nodes)
1089 {
1090 local_ids[node->getID()] = local_node_id++;
1091 }
1092 return local_ids;
1093}
1094
1101void writeElements(std::string const& file_name_base,
1102 std::vector<Partition> const& partitions,
1103 std::vector<long> const& element_offsets)
1104{
1105 const std::string npartitions_str = std::to_string(partitions.size());
1106
1107 auto const file_name_ele =
1108 file_name_base + "_partitioned_msh_ele" + npartitions_str + ".bin";
1109 std::ofstream element_info_os(file_name_ele, std::ios::binary);
1110 if (!element_info_os)
1111 {
1112 OGS_FATAL("Could not open file '{:s}' for output.", file_name_ele);
1113 }
1114
1115 // Ghost elements are no longer used. The ghost element file is still
1116 // created, but left empty, to keep the binary file format compatible.
1117 auto const file_name_ele_g =
1118 file_name_base + "_partitioned_msh_ele_g" + npartitions_str + ".bin";
1119 std::ofstream ghost_element_info_os(file_name_ele_g, std::ios::binary);
1120 if (!ghost_element_info_os)
1121 {
1122 OGS_FATAL("Could not open file '{:s}' for output.", file_name_ele_g);
1123 }
1124
1125 for (std::size_t i = 0; i < partitions.size(); i++)
1126 {
1127 const auto& partition = partitions[i];
1128 auto const local_node_ids = enumerateLocalNodeIds(partition.nodes);
1129
1130 auto writeElementData =
1131 [&local_node_ids](
1132 std::vector<MeshLib::Element const*> const& elements,
1133 long const element_offsets,
1134 std::ofstream& output_stream)
1135 {
1136 long counter = elements.size();
1137 std::vector<long> ele_info(element_offsets);
1138
1139 for (std::size_t j = 0; j < elements.size(); j++)
1140 {
1141 const auto* elem = elements[j];
1142 ele_info[j] = counter;
1143 getElementIntegerVariables(*elem, local_node_ids, ele_info,
1144 counter);
1145 }
1146 // Write vector data of elements
1147 output_stream.write(reinterpret_cast<const char*>(ele_info.data()),
1148 ele_info.size() * sizeof(long));
1149 };
1150
1151 writeElementData(partition.elements, element_offsets[i],
1152 element_info_os);
1153 }
1154}
1155
1160void writeNodes(const std::string& file_name_base,
1161 std::vector<Partition> const& partitions,
1162 std::vector<std::size_t> const& global_node_ids)
1163{
1164 auto const file_name = file_name_base + "_partitioned_msh_nod" +
1165 std::to_string(partitions.size()) + ".bin";
1166 std::ofstream os(file_name, std::ios::binary);
1167 if (!os)
1168 {
1169 OGS_FATAL("Could not open file '{:s}' for output.", file_name);
1170 }
1171
1172 for (const auto& partition : partitions)
1173 {
1174 partition.writeNodes(os, global_node_ids);
1175 }
1176}
1177
1178void NodeWiseMeshPartitioner::write(const std::string& file_name_base)
1179{
1186
1187 auto const element_offsets = writeConfigData(file_name_base, _partitions);
1188 writeElements(file_name_base, _partitions, element_offsets);
1189
1190 writeNodes(file_name_base, _partitions, _nodes_global_ids);
1191}
1192
1194 std::string const& output_filename_base,
1195 std::vector<Partition> const& partitions,
1196 MeshLib::Properties const& partitioned_properties) const
1197{
1198 writeNodes(output_filename_base, partitions, _nodes_global_ids);
1199
1200 auto const element_offsets =
1201 writeConfigData(output_filename_base, partitions);
1202 writeElements(output_filename_base, partitions, element_offsets);
1203
1204 writeProperties(output_filename_base, partitioned_properties, partitions,
1206 writeProperties(output_filename_base, partitioned_properties, partitions,
1208}
1209} // namespace ApplicationUtils
#define OGS_FATAL(...)
Definition Error.h:10
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void renumberBulkElementIdsProperty(MeshLib::PropertyVector< std::size_t > *const bulk_element_ids_pv, std::vector< Partition > const &local_partitions) const
std::vector< Partition > partitionOtherMesh(MeshLib::Mesh const &mesh) const
void write(const std::string &file_name_base)
void renumberBulkIdsProperty(std::vector< Partition > const &partitions, MeshLib::Properties &partitioned_properties)
void renumberBulkNodeIdsProperty(MeshLib::PropertyVector< std::size_t > *const bulk_node_ids, std::vector< Partition > const &local_partitions) const
std::unique_ptr< MeshLib::Mesh > _mesh
Pointer to a mesh object.
MeshLib::Properties _partitioned_properties
Properties where values at ghost nodes and extra nodes are inserted.
std::vector< std::size_t > _nodes_global_ids
Global IDs of all nodes after partitioning.
std::vector< Partition > _partitions
Data for all partitions.
std::vector< std::size_t > _nodes_partition_ids
Partition IDs of each nodes.
void writeOtherMesh(std::string const &output_filename_base, std::vector< Partition > const &partitions, MeshLib::Properties const &partitioned_properties) const
Count the running time.
Definition RunTime.h:18
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:31
void start()
Start the timer.
Definition RunTime.h:21
virtual CellType getCellType() const =0
virtual unsigned getNumberOfNodes() const =0
virtual const Node * getNode(unsigned idx) const =0
constexpr std::span< Node *const > nodes() const
Span of element's nodes, their pointers actually.
Definition Element.h:63
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:98
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:101
std::size_t computeNumberOfBaseNodes() const
Get the number of base nodes.
Definition Mesh.cpp:230
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:92
std::vector< Element const * > const & getElementsConnectedToNode(std::size_t node_id) const
Definition Mesh.cpp:248
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
bool hasPropertyVector(std::string_view name) const
std::map< std::string, PropertyVectorBase * >::size_type size() const
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
PropertyVector< T > const * getPropertyVector(std::string_view name) const
MeshItemType getMeshItemType() const
int getNumberOfGlobalComponents() const
std::string const & getPropertyName() const
constexpr std::size_t getNumberOfTuples() const
constexpr PROP_VAL_TYPE * begin()
constexpr std::size_t size() const
constexpr const PROP_VAL_TYPE * data() const
std::size_t copyCellPropertyVectorValues(Partition const &p, std::size_t const offset, MeshLib::PropertyVector< T > const &pv, MeshLib::PropertyVector< T > &partitioned_pv)
std::unordered_map< std::size_t, long > enumerateLocalNodeIds(std::vector< MeshLib::Node const * > const &nodes)
Generates a mapping of given node ids to a new local (renumbered) node ids.
void addVtkGhostTypeProperty(MeshLib::Properties &partitioned_properties, std::vector< Partition > const &partitions, std::size_t const total_number_of_cells)
bool writePropertyVector(MeshLib::PropertyVector< T > const *const pv, MeshLib::MeshItemType const mesh_item_type, std::ostream &out_val, std::ostream &out_meta)
std::tuple< std::vector< MeshLib::Node * >, std::vector< MeshLib::Node * > > findGhostNodesInPartition(std::size_t const part_id, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element const * > const &elements, std::vector< std::size_t > const &partition_ids, MeshLib::Mesh const &mesh, std::span< std::size_t const > const node_id_mapping)
void checkFieldPropertyVectorSize(std::vector< MeshLib::Element * > const &global_mesh_elements, MeshLib::Properties const &properties)
void writeNodes(const std::string &file_name_base, std::vector< Partition > const &partitions, std::vector< std::size_t > const &global_node_ids)
ConfigOffsets incrementConfigOffsets(ConfigOffsets const &oldConfig, PartitionOffsets const &offsets)
std::vector< std::vector< std::size_t > > computePartitionIDPerElement(std::vector< std::size_t > const &node_partition_map, std::vector< MeshLib::Element * > const &elements, std::span< std::size_t const > const bulk_node_ids)
void setIntegrationPointNumberOfPartition(MeshLib::Properties const &properties, std::vector< Partition > &partitions)
std::vector< long > writeConfigData(const std::string &file_name_base, std::vector< Partition > const &partitions)
void reorderNodesIntoBaseAndHigherOrderNodes(Partition &partition, MeshLib::Mesh const &mesh)
void reorderNodesIntoBaseAndHigherOrderNodesPerPartition(std::vector< Partition > &partitions, MeshLib::Mesh const &mesh)
MeshLib::Properties partitionProperties(std::unique_ptr< MeshLib::Mesh > const &mesh, std::vector< Partition > &partitions)
Partition existing properties and add vtkGhostType cell data array property.
void setNumberOfNodesInPartitions(std::vector< Partition > &partitions, MeshLib::Mesh const &mesh)
std::pair< std::vector< MeshLib::Node const * >, std::vector< MeshLib::Node const * > > splitIntoBaseAndHigherOrderNodes(std::vector< MeshLib::Node const * > const &nodes, MeshLib::Mesh const &mesh)
void distributeNodesToPartitions(std::vector< Partition > &partitions, std::vector< std::size_t > const &nodes_partition_ids, std::vector< MeshLib::Node * > const &nodes, std::span< std::size_t const > const bulk_node_ids)
PartitionOffsets computePartitionOffsets(Partition const &partition)
std::size_t partitionLookup(std::size_t const &node_id, std::vector< std::size_t > const &partition_ids, std::span< std::size_t const > const node_id_mapping)
NodeWiseMeshPartitioner::IntegerType getNumberOfIntegerVariablesOfElements(std::vector< const MeshLib::Element * > const &elements)
void partitionMesh(std::vector< Partition > &partitions, MeshLib::Mesh const &mesh, std::vector< std::size_t > const &nodes_partition_ids, std::span< std::size_t const > const bulk_node_ids)
std::size_t copyFieldPropertyDataToPartitions(MeshLib::Properties const &properties, Partition const &p, std::size_t const id_offset_partition, std::vector< std::size_t > const &element_ip_data_offsets, MeshLib::PropertyVector< T > const &pv, MeshLib::PropertyVector< T > &partitioned_pv)
void writePropertyVectorValues(std::ostream &os, MeshLib::PropertyVector< T > const &pv)
void getElementIntegerVariables(const MeshLib::Element &elem, const std::unordered_map< std::size_t, long > &local_node_ids, std::vector< long > &elem_info, long &counter)
void writeElements(std::string const &file_name_base, std::vector< Partition > const &partitions, std::vector< long > const &element_offsets)
void distributeElementsIntoPartitions(std::vector< Partition > &partitions, MeshLib::Mesh const &mesh, std::vector< std::vector< std::size_t > > const &partition_ids_per_element)
std::size_t copyNodePropertyVectorValues(Partition const &p, std::size_t const offset, MeshLib::PropertyVector< T > const &pv, MeshLib::PropertyVector< T > &partitioned_pv)
void writeProperties(const std::string &file_name_base, MeshLib::Properties const &partitioned_properties, std::vector< Partition > const &partitions, MeshLib::MeshItemType const mesh_item_type)
bool copyPropertyVector(std::vector< MeshLib::Element * > const &global_mesh_elements, MeshLib::Properties &partitioned_properties, MeshLib::Properties const &properties, std::vector< Partition > const &partitions, MeshLib::PropertyVector< T > const *const pv, std::map< MeshLib::MeshItemType, std::size_t > const &total_number_of_tuples)
void determineAndAppendGhostNodesToPartitions(std::vector< Partition > &partitions, MeshLib::Mesh const &mesh, std::vector< std::size_t > const &nodes_partition_ids, std::span< std::size_t const > const node_id_mapping)
void writeValueBinary(std::ostream &out, T const &val)
write value as binary into the given output stream
void writePropertyVectorPartitionMetaData(std::ostream &os, PropertyVectorPartitionMetaData const &pvpmd)
void writePropertyVectorMetaData(std::ostream &os, PropertyVectorMetaData const &pvmd)
constexpr ranges::views::view_closure ids
For an element of a range view return its id.
Definition Mesh.h:223
std::optional< IntegrationPointMetaData > getIntegrationPointMetaData(MeshLib::Properties const &properties)
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
void applyToPropertyVectors(Properties const &properties, Function f)
constexpr std::string vtkGhostTypeString
static constexpr char const * toString(const MeshItemType t)
Returns a char array for a specific MeshItemType.
Definition MeshEnums.h:26
IntegrationPointMetaDataSingleField getIntegrationPointMetaDataSingleField(std::optional< IntegrationPointMetaData > const &ip_meta_data, std::string const &field_name)
std::vector< std::size_t > getIntegrationPointDataOffsetsOfMeshElements(std::vector< MeshLib::Element * > const &mesh_elements, MeshLib::PropertyVectorBase const &pv, MeshLib::Properties const &properties)
int getNumberOfElementIntegrationPoints(MeshLib::IntegrationPointMetaDataSingleField const &ip_meta_data, MeshLib::Element const &e)
std::ostream & writeConfig(std::ostream &os) const
std::ostream & writeConfig(std::ostream &os) const
std::vector< const MeshLib::Element * > elements
std::size_t numberOfMeshItems(MeshLib::MeshItemType const item_type) const
std::ostream & writeNodes(std::ostream &os, std::vector< std::size_t > const &global_node_ids) const
std::vector< MeshLib::Node const * > nodes
nodes.
struct NodeData used for parallel reading and also partitioning
Definition NodeData.h:12