12#include <tclap/CmdLine.h>
15#include <boost/property_tree/ptree.hpp>
16#include <boost/property_tree/xml_parser.hpp>
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>
27#include <unordered_set>
55 std::vector<std::unique_ptr<MeshLib::Mesh>>
const& partitioned_meshes,
58 std::vector<MeshEntityMapInfo>
const& merged_node_map,
59 std::vector<MeshEntityMapInfo>
const& merged_element_map)
78 if (pv_name ==
"OGS_VERSION" || pv_name ==
"IntegrationPointMetaData")
81 merged_mesh, pv_name, item_type, pv_num_components);
82 new_pv->assign(pv->begin(), pv->end());
87 std::vector<MeshLib::PropertyVector<T>*> partition_property_vectors;
88 partition_property_vectors.reserve(partitioned_meshes.size());
89 for (
auto const& mesh : partitioned_meshes)
91 partition_property_vectors.emplace_back(
92 mesh->getProperties().getPropertyVector<T>(pv_name, item_type,
96 auto createNewCellOrNodePropertyVector =
97 [&](std::vector<MeshEntityMapInfo>
const& mesh_entity_map)
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)
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++)
108 (*new_pv)[counter * pv_num_components + i_com] =
109 (*partition_pv)[entity_info.original_id *
119 createNewCellOrNodePropertyVector(merged_node_map);
125 createNewCellOrNodePropertyVector(merged_element_map);
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)
135 partition_element_offsets.emplace_back(
137 mesh->getElements(), *pv, properties));
141 merged_mesh, pv_name, item_type, pv_num_components);
144 auto const ip_meta_data =
148 auto number_of_integration_points = [&](
auto const* element)
151 ip_meta_data, *element);
154 std::size_t
const counter = ranges::accumulate(
156 ranges::views::transform(number_of_integration_points),
158 new_pv->resize(counter * pv_num_components);
160 auto const global_ip_offsets =
164 std::size_t element_counter = 0;
165 for (
auto const& element_info : merged_element_map)
168 *(partition_property_vectors[element_info.partition_id]);
170 auto const& offsets =
171 partition_element_offsets[element_info.partition_id];
173 int const begin_pos = offsets[element_info.original_id];
174 int const end_pos = offsets[element_info.original_id + 1];
176 std::copy(partition_pv.begin() + begin_pos,
177 partition_pv.begin() + end_pos,
178 new_pv->begin() + global_ip_offsets[element_counter]);
189 std::ifstream ins(pvtu_file_name);
193 OGS_FATAL(
"Could not open pvtu file {:s}.", pvtu_file_name);
196 using boost::property_tree::ptree;
200 auto root = pt.get_child(
"VTKFile");
202 std::vector<std::string> vtu_file_names;
206 for (ptree::value_type
const& v : root.get_child(
"PUnstructuredGrid"))
208 if (v.first ==
"Piece")
213 std::filesystem::path(v.second.get(
"<xmlattr>.Source",
""))
219 if (vtu_file_names.empty())
221 OGS_FATAL(
"PVTU file {:s} does not contain any vtu piece",
225 return vtu_file_names;
229std::tuple<std::vector<MeshLib::Node*>, std::vector<std::size_t>>
232 std::vector<std::size_t> number_of_nodes_per_partition;
233 ranges::transform(meshes, std::back_inserter(number_of_nodes_per_partition),
235 {
return mesh->getNumberOfNodes(); });
236 std::vector<std::size_t>
const offsets =
239 std::vector<MeshLib::Node*> all_nodes;
240 all_nodes.reserve(offsets.back());
241 for (
auto const& mesh : meshes)
243 ranges::copy(mesh->getNodes(), std::back_inserter(all_nodes));
245 return {all_nodes, offsets};
248std::tuple<std::vector<MeshLib::Element*>, std::vector<MeshEntityMapInfo>>
251 std::vector<MeshLib::Element*> regular_elements;
253 std::size_t partition_counter = 0;
254 std::vector<MeshEntityMapInfo> merged_element_map;
255 for (
auto const& mesh : meshes)
259 auto const*
const ghost_id_vector =
261 assert(ghost_id_vector);
263 auto const& mesh_elements = mesh->getElements();
265 auto const last_element_id_of_previous_partition =
266 regular_elements.size();
269 {
return (*ghost_id_vector)[element->getID()] == 0; };
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());
277 for (
auto element_id = last_element_id_of_previous_partition;
278 element_id < regular_elements.size();
281 merged_element_map.push_back(
282 {partition_counter, regular_elements[element_id]->getID()});
288 return {regular_elements, merged_element_map};
295 std::vector<MeshLib::Node*> query_nodes;
297 std::numeric_limits<double>::epsilon() * extent.squaredNorm();
302 if (query_nodes.empty())
305 "query_nodes for node [{}], ({}, {}, {}) of element "
306 "[{}] are empty, eps is {}",
307 node.
getID(), node[0], node[1], node[2], element_id, eps);
310 std::find_if(query_nodes.begin(), query_nodes.end(),
311 [&node, eps](
auto const* p)
313 return (p->asEigenVector3d() - node.asEigenVector3d())
314 .squaredNorm() < eps;
316 if (it == query_nodes.end())
319 "did not find node: [{}] ({}, {}, {}) of element [{}] in "
321 node.
getID(), node[0], node[1], node[2], element_id);
327 std::vector<MeshLib::Element*>
const& regular_elements,
329 Eigen::Vector3d
const& extent)
331 for (
auto& e : regular_elements)
333 for (
unsigned i = 0; i < e->getNumberOfNodes(); i++)
335 auto*
const node = e->getNode(i);
337 if (!oct_tree.
addPoint(node, node_ptr))
340 oct_tree, extent, *node, e->getID());
341 e->setNode(i, node_ptr);
347std::pair<std::vector<MeshLib::Node*>, std::vector<MeshEntityMapInfo>>
349 std::vector<std::size_t>
const& partition_offsets,
352 std::vector<MeshLib::Node*> unique_merged_nodes;
353 unique_merged_nodes.reserve(all_merged_nodes_tmp.size());
355 std::vector<MeshEntityMapInfo> merged_node_map;
356 merged_node_map.reserve(all_merged_nodes_tmp.size());
358 for (std::size_t i = 0; i < partition_offsets.size() - 1; ++i)
360 for (std::size_t pos = partition_offsets[i];
361 pos < partition_offsets[i + 1];
364 auto* node = all_merged_nodes_tmp[pos];
366 if (oct_tree.
addPoint(node, node_ptr))
368 unique_merged_nodes.push_back(node);
369 merged_node_map.push_back({i, pos - partition_offsets[i]});
373 return {unique_merged_nodes, merged_node_map};
376int main(
int argc,
char* argv[])
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 " +
384 "Copyright (c) 2012-2025, OpenGeoSys Community "
385 "(http://www.opengeosys.org)",
388 TCLAP::ValueArg<std::string> output_arg(
389 "o",
"output",
"the output mesh (*.vtu)",
true,
"",
"output.vtu");
392 TCLAP::ValueArg<std::string> input_arg(
393 "i",
"input",
"the partitioned input mesh (*.pvtu)",
true,
"",
396 cmd.parse(argc, argv);
402 OGS_FATAL(
"The extension of input file name {:s} is not \"pvtu\"",
403 input_arg.getValue());
407 OGS_FATAL(
"The extension of output file name {:s} is not \"vtu\"",
408 output_arg.getValue());
413 std::vector<std::unique_ptr<MeshLib::Mesh>> meshes;
414 meshes.reserve(vtu_file_names.size());
418 for (
auto const& file_name : vtu_file_names)
420 auto mesh = std::unique_ptr<MeshLib::Mesh>(
428 "Property vector vtkGhostType does not exist in mesh {:s}.",
432 meshes.emplace_back(std::move(mesh));
434 INFO(
"Reading meshes took {} s", io_timer.
elapsed());
437 merged_element_timer.
start();
442 "Collection of {} regular elements and computing element map took {} s",
443 regular_elements.size(), merged_element_timer.
elapsed());
447 collect_nodes_timer.
start();
448 auto [all_merged_nodes_tmp, partition_offsets] =
450 INFO(
"Collection of {} nodes and computing offsets took {} s",
451 all_merged_nodes_tmp.size(), collect_nodes_timer.
elapsed());
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>>(
460 auto [unique_merged_nodes, merged_node_map] =
462 INFO(
"Make nodes unique ({} unique nodes) / computing map took {} s",
463 unique_merged_nodes.size(), merged_nodes_timer.
elapsed());
466 reset_nodes_in_elements_timer.
start();
469 INFO(
"Reset nodes in regular elements took {} s",
470 reset_nodes_in_elements_timer.
elapsed());
473 mesh_creation_timer.
start();
477 MeshLib::Mesh(
"pvtu_merged_mesh", unique_merged_nodes, regular_elements,
479 INFO(
"creation of merged mesh took {} s", mesh_creation_timer.
elapsed());
481 auto const& properties = meshes[0]->getProperties();
484 property_timer.
start();
487 [&, &merged_node_map = merged_node_map,
488 &merged_element_map = merged_element_map](
auto type,
489 auto const& property)
495 properties, merged_node_map, merged_element_map);
497 INFO(
"merge properties into merged mesh took {} s",
503 writing_timer.
start();
504 auto const result = writer.
writeToFile(output_arg.getValue());
507 ERR(
"Could not write mesh to '{:s}'.", output_arg.getValue());
510 INFO(
"writing mesh took {} s", writing_timer.
elapsed());
Definition of the AABB class.
Definition of the Element class.
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
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[])
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)
MeshLib::Node * getExistingNodeFromOctTree(GeoLib::OctTree< MeshLib::Node, 16 > &oct_tree, Eigen::Vector3d const &extent, MeshLib::Node const &node, std::size_t const element_id)
void resetNodesInRegularElements(std::vector< MeshLib::Element * > const ®ular_elements, GeoLib::OctTree< MeshLib::Node, 16 > &oct_tree, Eigen::Vector3d const &extent)
std::tuple< std::vector< MeshLib::Element * >, std::vector< MeshEntityMapInfo > > getRegularElements(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
std::vector< std::string > readVtuFileNames(std::string const &pvtu_file_name)
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)
std::tuple< std::vector< MeshLib::Node * >, std::vector< std::size_t > > getMergedNodesVector(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
Definition of the Point3d class.
void applyToPropertyVectors(Properties const &properties, Function f)
Definition of the RunTime class.
Implementation of the VtuInterface class.
double elapsed() const
Get the elapsed time in seconds.
void start()
Start the timer.
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Eigen::Vector3d const & getMaxPoint() const
Eigen::Vector3d const & getMinPoint() const
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
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.
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
bool existsPropertyVector(std::string_view name) const
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)
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)