Loading [MathJax]/extensions/tex2jax.js
OGS
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
PVTU2VTU.cpp
Go to the documentation of this file.
1
12#include <tclap/CmdLine.h>
13
14#include <algorithm>
15#include <boost/property_tree/ptree.hpp>
16#include <boost/property_tree/xml_parser.hpp>
17#include <filesystem>
18#include <fstream>
19#include <memory>
20#include <numeric>
21#include <range/v3/algorithm/copy.hpp>
22#include <range/v3/algorithm/transform.hpp>
23#include <range/v3/numeric/accumulate.hpp>
24#include <range/v3/range/conversion.hpp>
25#include <range/v3/view/filter.hpp>
26#include <string>
27#include <unordered_set>
28#include <vector>
29
30#include "BaseLib/FileTools.h"
31#include "BaseLib/MPI.h"
32#include "BaseLib/RunTime.h"
33#include "GeoLib/AABB.h"
34#include "GeoLib/OctTree.h"
35#include "InfoLib/GitInfo.h"
36#include "MathLib/Point3d.h"
39#include "MeshLib/Mesh.h"
40#include "MeshLib/Node.h"
41#include "MeshLib/Properties.h"
45
47{
48 std::size_t partition_id;
49 std::size_t original_id;
50};
51
52template <typename T>
54 MeshLib::Mesh& merged_mesh,
55 std::vector<std::unique_ptr<MeshLib::Mesh>> const& partitioned_meshes,
56 MeshLib::PropertyVector<T> const* const pv,
57 MeshLib::Properties const& properties,
58 std::vector<MeshEntityMapInfo> const& merged_node_map,
59 std::vector<MeshEntityMapInfo> const& merged_element_map)
60{
61 if (pv == nullptr)
62 {
63 return false;
64 }
65
66 if (pv->getPropertyName() == "vtkGhostType")
67 {
68 // Do nothing
69 return true;
70 }
71
72 auto const item_type = pv->getMeshItemType();
73
74 auto const pv_name = pv->getPropertyName();
75
76 auto const pv_num_components = pv->getNumberOfGlobalComponents();
77
78 if (pv_name == "OGS_VERSION" || pv_name == "IntegrationPointMetaData")
79 {
81 merged_mesh, pv_name, item_type, pv_num_components);
82 new_pv->assign(pv->begin(), pv->end());
83
84 return true;
85 }
86
87 std::vector<MeshLib::PropertyVector<T>*> partition_property_vectors;
88 partition_property_vectors.reserve(partitioned_meshes.size());
89 for (auto const& mesh : partitioned_meshes)
90 {
91 partition_property_vectors.emplace_back(
92 mesh->getProperties().getPropertyVector<T>(pv_name, item_type,
93 pv_num_components));
94 }
95
96 auto createNewCellOrNodePropertyVector =
97 [&](std::vector<MeshEntityMapInfo> const& mesh_entity_map)
98 {
100 merged_mesh, pv_name, item_type, pv_num_components);
101 std::size_t counter = 0;
102 for (auto const& entity_info : mesh_entity_map)
103 {
104 auto const& partition_pv =
105 partition_property_vectors[entity_info.partition_id];
106 for (int i_com = 0; i_com < pv_num_components; i_com++)
107 {
108 (*new_pv)[counter * pv_num_components + i_com] =
109 (*partition_pv)[entity_info.original_id *
110 pv_num_components +
111 i_com];
112 }
113 counter++;
114 }
115 };
116
117 if (item_type == MeshLib::MeshItemType::Node)
118 {
119 createNewCellOrNodePropertyVector(merged_node_map);
120 return true;
121 }
122
123 if (item_type == MeshLib::MeshItemType::Cell)
124 {
125 createNewCellOrNodePropertyVector(merged_element_map);
126 return true;
127 }
128
130 {
131 std::vector<std::vector<std::size_t>> partition_element_offsets;
132 partition_element_offsets.reserve(partitioned_meshes.size());
133 for (auto const& mesh : partitioned_meshes)
134 {
135 partition_element_offsets.emplace_back(
137 mesh->getElements(), *pv, properties));
138 }
139
141 merged_mesh, pv_name, item_type, pv_num_components);
142
143 // Count the integration points
144 auto const ip_meta_data =
146 MeshLib::getIntegrationPointMetaData(properties), pv_name);
147
148 auto number_of_integration_points = [&](auto const* element)
149 {
151 ip_meta_data, *element);
152 };
153
154 std::size_t const counter = ranges::accumulate(
155 merged_mesh.getElements() |
156 ranges::views::transform(number_of_integration_points),
157 std::size_t{0});
158 new_pv->resize(counter * pv_num_components);
159
160 auto const global_ip_offsets =
162 merged_mesh.getElements(), *pv, properties);
163
164 std::size_t element_counter = 0;
165 for (auto const& element_info : merged_element_map)
166 {
167 MeshLib::PropertyVector<T> const& partition_pv =
168 *(partition_property_vectors[element_info.partition_id]);
169
170 auto const& offsets =
171 partition_element_offsets[element_info.partition_id];
172
173 int const begin_pos = offsets[element_info.original_id];
174 int const end_pos = offsets[element_info.original_id + 1];
175
176 std::copy(partition_pv.begin() + begin_pos,
177 partition_pv.begin() + end_pos,
178 new_pv->begin() + global_ip_offsets[element_counter]);
179
180 element_counter++;
181 }
182 }
183
184 return true;
185}
186
187std::vector<std::string> readVtuFileNames(std::string const& pvtu_file_name)
188{
189 std::ifstream ins(pvtu_file_name);
190
191 if (!ins)
192 {
193 OGS_FATAL("Could not open pvtu file {:s}.", pvtu_file_name);
194 }
195
196 using boost::property_tree::ptree;
197 ptree pt;
198 read_xml(ins, pt);
199
200 auto root = pt.get_child("VTKFile");
201
202 std::vector<std::string> vtu_file_names;
203
204 std::string file_path = BaseLib::extractPath(pvtu_file_name);
205
206 for (ptree::value_type const& v : root.get_child("PUnstructuredGrid"))
207 {
208 if (v.first == "Piece")
209 {
210 vtu_file_names.push_back(BaseLib::joinPaths(
211 file_path,
212 // only gets the vtu file name:
213 std::filesystem::path(v.second.get("<xmlattr>.Source", ""))
214 .filename()
215 .string()));
216 }
217 }
218
219 if (vtu_file_names.empty())
220 {
221 OGS_FATAL("PVTU file {:s} does not contain any vtu piece",
222 pvtu_file_name);
223 }
224
225 return vtu_file_names;
226}
227
228// all nodes (also 'ghost' nodes) of all meshes sorted by partition
229std::tuple<std::vector<MeshLib::Node*>, std::vector<std::size_t>>
230getMergedNodesVector(std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes)
231{
232 std::vector<std::size_t> number_of_nodes_per_partition;
233 ranges::transform(meshes, std::back_inserter(number_of_nodes_per_partition),
234 [](auto const& mesh)
235 { return mesh->getNumberOfNodes(); });
236 std::vector<std::size_t> const offsets =
237 BaseLib::sizesToOffsets(number_of_nodes_per_partition);
238
239 std::vector<MeshLib::Node*> all_nodes;
240 all_nodes.reserve(offsets.back());
241 for (auto const& mesh : meshes)
242 {
243 ranges::copy(mesh->getNodes(), std::back_inserter(all_nodes));
244 }
245 return {all_nodes, offsets};
246}
247
248std::tuple<std::vector<MeshLib::Element*>, std::vector<MeshEntityMapInfo>>
249getRegularElements(std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes)
250{
251 std::vector<MeshLib::Element*> regular_elements;
252
253 std::size_t partition_counter = 0;
254 std::vector<MeshEntityMapInfo> merged_element_map;
255 for (auto const& mesh : meshes)
256 {
257 MeshLib::Properties const& properties = mesh->getProperties();
258
259 auto const* const ghost_id_vector =
260 properties.getPropertyVector<unsigned char>("vtkGhostType");
261 assert(ghost_id_vector);
262
263 auto const& mesh_elements = mesh->getElements();
264
265 auto const last_element_id_of_previous_partition =
266 regular_elements.size();
267
268 auto is_regular_element = [&](MeshLib::Element const* element)
269 { return (*ghost_id_vector)[element->getID()] == 0; };
270
271 auto regular_mesh_elements =
272 mesh_elements | ranges::views::filter(is_regular_element);
273 regular_elements.insert(regular_elements.end(),
274 regular_mesh_elements.begin(),
275 regular_mesh_elements.end());
276
277 for (auto element_id = last_element_id_of_previous_partition;
278 element_id < regular_elements.size();
279 element_id++)
280 {
281 merged_element_map.push_back(
282 {partition_counter, regular_elements[element_id]->getID()});
283 }
284
285 partition_counter++;
286 }
287
288 return {regular_elements, merged_element_map};
289}
290
292 GeoLib::OctTree<MeshLib::Node, 16>& oct_tree, Eigen::Vector3d const& extent,
293 MeshLib::Node const& node, std::size_t const element_id)
294{
295 std::vector<MeshLib::Node*> query_nodes;
296 auto const eps =
297 std::numeric_limits<double>::epsilon() * extent.squaredNorm();
298 Eigen::Vector3d const min = node.asEigenVector3d().array() - eps;
299 Eigen::Vector3d const max = node.asEigenVector3d().array() + eps;
300 oct_tree.getPointsInRange(min, max, query_nodes);
301
302 if (query_nodes.empty())
303 {
304 OGS_FATAL(
305 "query_nodes for node [{}], ({}, {}, {}) of element "
306 "[{}] are empty, eps is {}",
307 node.getID(), node[0], node[1], node[2], element_id, eps);
308 }
309 auto const it =
310 std::find_if(query_nodes.begin(), query_nodes.end(),
311 [&node, eps](auto const* p)
312 {
313 return (p->asEigenVector3d() - node.asEigenVector3d())
314 .squaredNorm() < eps;
315 });
316 if (it == query_nodes.end())
317 {
318 OGS_FATAL(
319 "did not find node: [{}] ({}, {}, {}) of element [{}] in "
320 "query_nodes",
321 node.getID(), node[0], node[1], node[2], element_id);
322 }
323 return *it;
324}
325
327 std::vector<MeshLib::Element*> const& regular_elements,
329 Eigen::Vector3d const& extent)
330{
331 for (auto& e : regular_elements)
332 {
333 for (unsigned i = 0; i < e->getNumberOfNodes(); i++)
334 {
335 auto* const node = e->getNode(i);
336 MeshLib::Node* node_ptr = nullptr;
337 if (!oct_tree.addPoint(node, node_ptr))
338 {
339 auto const node_ptr = getExistingNodeFromOctTree(
340 oct_tree, extent, *node, e->getID());
341 e->setNode(i, node_ptr);
342 }
343 }
344 }
345}
346
347std::pair<std::vector<MeshLib::Node*>, std::vector<MeshEntityMapInfo>>
348makeNodesUnique(std::vector<MeshLib::Node*> const& all_merged_nodes_tmp,
349 std::vector<std::size_t> const& partition_offsets,
351{
352 std::vector<MeshLib::Node*> unique_merged_nodes;
353 unique_merged_nodes.reserve(all_merged_nodes_tmp.size());
354
355 std::vector<MeshEntityMapInfo> merged_node_map;
356 merged_node_map.reserve(all_merged_nodes_tmp.size());
357
358 for (std::size_t i = 0; i < partition_offsets.size() - 1; ++i)
359 {
360 for (std::size_t pos = partition_offsets[i];
361 pos < partition_offsets[i + 1];
362 ++pos)
363 {
364 auto* node = all_merged_nodes_tmp[pos];
365 MeshLib::Node* node_ptr = nullptr;
366 if (oct_tree.addPoint(node, node_ptr))
367 {
368 unique_merged_nodes.push_back(node);
369 merged_node_map.push_back({i, pos - partition_offsets[i]});
370 }
371 }
372 }
373 return {unique_merged_nodes, merged_node_map};
374}
375
376int main(int argc, char* argv[])
377{
378 TCLAP::CmdLine cmd(
379 "This tool merges VTU files of PVTU into one single VTU file. Apart "
380 "from the mesh data, all property data are merged as well"
381 "\n\nOpenGeoSys-6 software, version " +
383 ".\n"
384 "Copyright (c) 2012-2025, OpenGeoSys Community "
385 "(http://www.opengeosys.org)",
387
388 TCLAP::ValueArg<std::string> output_arg(
389 "o", "output", "the output mesh (*.vtu)", true, "", "output.vtu");
390 cmd.add(output_arg);
391
392 TCLAP::ValueArg<std::string> input_arg(
393 "i", "input", "the partitioned input mesh (*.pvtu)", true, "",
394 "input.pvtu");
395 cmd.add(input_arg);
396 cmd.parse(argc, argv);
397
398 BaseLib::MPI::Setup mpi_setup(argc, argv);
399
400 if (BaseLib::getFileExtension(input_arg.getValue()) != ".pvtu")
401 {
402 OGS_FATAL("The extension of input file name {:s} is not \"pvtu\"",
403 input_arg.getValue());
404 }
405 if (BaseLib::getFileExtension(output_arg.getValue()) != ".vtu")
406 {
407 OGS_FATAL("The extension of output file name {:s} is not \"vtu\"",
408 output_arg.getValue());
409 }
410
411 auto const vtu_file_names = readVtuFileNames(input_arg.getValue());
412
413 std::vector<std::unique_ptr<MeshLib::Mesh>> meshes;
414 meshes.reserve(vtu_file_names.size());
415
416 BaseLib::RunTime io_timer;
417 io_timer.start();
418 for (auto const& file_name : vtu_file_names)
419 {
420 auto mesh = std::unique_ptr<MeshLib::Mesh>(
422
423 MeshLib::Properties const& properties = mesh->getProperties();
424
425 if (!properties.existsPropertyVector<unsigned char>("vtkGhostType"))
426 {
427 OGS_FATAL(
428 "Property vector vtkGhostType does not exist in mesh {:s}.",
429 file_name);
430 }
431
432 meshes.emplace_back(std::move(mesh));
433 }
434 INFO("Reading meshes took {} s", io_timer.elapsed());
435
436 BaseLib::RunTime merged_element_timer;
437 merged_element_timer.start();
438 // If structured binding is used for the returned tuple, Mac compiler gives
439 // an error in reference to local binding in calling applyToPropertyVectors.
440 auto [regular_elements, merged_element_map] = getRegularElements(meshes);
441 INFO(
442 "Collection of {} regular elements and computing element map took {} s",
443 regular_elements.size(), merged_element_timer.elapsed());
444
445 // alternative implementation of getNodesOfRegularElements
446 BaseLib::RunTime collect_nodes_timer;
447 collect_nodes_timer.start();
448 auto [all_merged_nodes_tmp, partition_offsets] =
449 getMergedNodesVector(meshes);
450 INFO("Collection of {} nodes and computing offsets took {} s",
451 all_merged_nodes_tmp.size(), collect_nodes_timer.elapsed());
452
453 BaseLib::RunTime merged_nodes_timer;
454 merged_nodes_timer.start();
455 GeoLib::AABB aabb(all_merged_nodes_tmp.begin(), all_merged_nodes_tmp.end());
456 auto oct_tree = std::unique_ptr<GeoLib::OctTree<MeshLib::Node, 16>>(
458 aabb.getMinPoint(), aabb.getMaxPoint(), 1e-16));
459
460 auto [unique_merged_nodes, merged_node_map] =
461 makeNodesUnique(all_merged_nodes_tmp, partition_offsets, *oct_tree);
462 INFO("Make nodes unique ({} unique nodes) / computing map took {} s",
463 unique_merged_nodes.size(), merged_nodes_timer.elapsed());
464
465 BaseLib::RunTime reset_nodes_in_elements_timer;
466 reset_nodes_in_elements_timer.start();
467 auto const extent = aabb.getMaxPoint() - aabb.getMinPoint();
468 resetNodesInRegularElements(regular_elements, *oct_tree, extent);
469 INFO("Reset nodes in regular elements took {} s",
470 reset_nodes_in_elements_timer.elapsed());
471
472 BaseLib::RunTime mesh_creation_timer;
473 mesh_creation_timer.start();
474 // The Node pointers of 'merged_nodes' and Element pointers of
475 // 'regular_elements' are shared with 'meshes', the partitioned meshes.
476 MeshLib::Mesh merged_mesh =
477 MeshLib::Mesh("pvtu_merged_mesh", unique_merged_nodes, regular_elements,
478 false /* compute_element_neighbors */);
479 INFO("creation of merged mesh took {} s", mesh_creation_timer.elapsed());
480
481 auto const& properties = meshes[0]->getProperties();
482
483 BaseLib::RunTime property_timer;
484 property_timer.start();
486 properties,
487 [&, &merged_node_map = merged_node_map,
488 &merged_element_map = merged_element_map](auto type,
489 auto const& property)
490 {
492 merged_mesh, meshes,
493 dynamic_cast<MeshLib::PropertyVector<decltype(type)> const*>(
494 property),
495 properties, merged_node_map, merged_element_map);
496 });
497 INFO("merge properties into merged mesh took {} s",
498 property_timer.elapsed());
499
500 MeshLib::IO::VtuInterface writer(&merged_mesh);
501
502 BaseLib::RunTime writing_timer;
503 writing_timer.start();
504 auto const result = writer.writeToFile(output_arg.getValue());
505 if (!result)
506 {
507 ERR("Could not write mesh to '{:s}'.", output_arg.getValue());
508 return EXIT_FAILURE;
509 }
510 INFO("writing mesh took {} s", writing_timer.elapsed());
511
512 // Since the Node pointers of 'merged_nodes' and Element pointers of
513 // 'regular_elements' are held by 'meshes', the partitioned meshes, the
514 // memory by these pointers are released by 'meshes' automatically.
515 // Therefore, only node vector and element vector of merged_mesh should be
516 // cleaned.
517 merged_mesh.shallowClean();
518
519 return EXIT_SUCCESS;
520}
Definition of the AABB class.
Definition of the Element class.
#define OGS_FATAL(...)
Definition Error.h:26
Filename manipulation routines.
Git information.
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:35
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
Definition of the class Properties that implements a container of properties.
Definition of the Mesh class.
Definition of the Node class.
Implementation of the OctTree class.
int main(int argc, char *argv[])
Definition PVTU2VTU.cpp:376
std::pair< std::vector< MeshLib::Node * >, std::vector< MeshEntityMapInfo > > makeNodesUnique(std::vector< MeshLib::Node * > const &all_merged_nodes_tmp, std::vector< std::size_t > const &partition_offsets, GeoLib::OctTree< MeshLib::Node, 16 > &oct_tree)
Definition PVTU2VTU.cpp:348
MeshLib::Node * getExistingNodeFromOctTree(GeoLib::OctTree< MeshLib::Node, 16 > &oct_tree, Eigen::Vector3d const &extent, MeshLib::Node const &node, std::size_t const element_id)
Definition PVTU2VTU.cpp:291
void resetNodesInRegularElements(std::vector< MeshLib::Element * > const &regular_elements, GeoLib::OctTree< MeshLib::Node, 16 > &oct_tree, Eigen::Vector3d const &extent)
Definition PVTU2VTU.cpp:326
std::tuple< std::vector< MeshLib::Element * >, std::vector< MeshEntityMapInfo > > getRegularElements(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
Definition PVTU2VTU.cpp:249
std::vector< std::string > readVtuFileNames(std::string const &pvtu_file_name)
Definition PVTU2VTU.cpp:187
bool createPropertyVector(MeshLib::Mesh &merged_mesh, std::vector< std::unique_ptr< MeshLib::Mesh > > const &partitioned_meshes, MeshLib::PropertyVector< T > const *const pv, MeshLib::Properties const &properties, std::vector< MeshEntityMapInfo > const &merged_node_map, std::vector< MeshEntityMapInfo > const &merged_element_map)
Definition PVTU2VTU.cpp:53
std::tuple< std::vector< MeshLib::Node * >, std::vector< std::size_t > > getMergedNodesVector(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
Definition PVTU2VTU.cpp:230
Definition of the Point3d class.
void applyToPropertyVectors(Properties const &properties, Function f)
Definition of the RunTime class.
Implementation of the VtuInterface 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
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Definition AABB.h:56
Eigen::Vector3d const & getMaxPoint() const
Definition AABB.h:187
Eigen::Vector3d const & getMinPoint() const
Definition AABB.h:180
bool addPoint(POINT *pnt, POINT *&ret_pnt)
void getPointsInRange(T const &min, T const &max, std::vector< POINT * > &pnts) const
static OctTree< POINT, MAX_POINTS > * createOctTree(Eigen::Vector3d ll, Eigen::Vector3d ur, double eps=std::numeric_limits< double >::epsilon())
std::size_t getID() const
Eigen::Vector3d const & asEigenVector3d() const
Definition Point3d.h:64
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
static MeshLib::Mesh * readVTUFile(std::string const &file_name, bool const compute_element_neighbors=false)
bool writeToFile(std::filesystem::path const &file_path)
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:111
void shallowClean()
Definition Mesh.cpp:133
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition Properties.h:33
bool existsPropertyVector(std::string_view name) const
Definition Properties.h:74
PropertyVector< T > const * getPropertyVector(std::string_view name) const
MeshItemType getMeshItemType() const
int getNumberOfGlobalComponents() const
std::string const & getPropertyName() const
std::string getFileExtension(const std::string &path)
std::string extractPath(std::string const &pathname)
std::string joinPaths(std::string const &pathA, std::string const &pathB)
std::vector< ranges::range_value_t< R > > sizesToOffsets(R const &sizes)
Definition Algorithm.h:283
GITINFOLIB_EXPORT const std::string ogs_version
PropertyVector< T > * getOrCreateMeshProperty(Mesh &mesh, std::string const &property_name, MeshItemType const item_type, int const number_of_components)
std::optional< IntegrationPointMetaData > getIntegrationPointMetaData(MeshLib::Properties const &properties)
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::size_t partition_id
Definition PVTU2VTU.cpp:48
std::size_t original_id
Definition PVTU2VTU.cpp:49