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>
57 std::vector<std::unique_ptr<MeshLib::Mesh>>
const& partitioned_meshes,
60 std::vector<MeshEntityMapInfo>
const& merged_node_map,
61 std::vector<MeshEntityMapInfo>
const& merged_element_map)
80 if (pv_name ==
"OGS_VERSION" || pv_name ==
"IntegrationPointMetaData")
83 merged_mesh, pv_name, item_type, pv_num_components);
89 std::vector<MeshLib::PropertyVector<T>*> partition_property_vectors;
90 partition_property_vectors.reserve(partitioned_meshes.size());
91 for (
auto const& mesh : partitioned_meshes)
93 partition_property_vectors.emplace_back(
94 mesh->getProperties().getPropertyVector<T>(pv_name, item_type,
98 auto createNewCellOrNodePropertyVector =
99 [&](std::vector<MeshEntityMapInfo>
const& mesh_entity_map)
102 merged_mesh, pv_name, item_type, pv_num_components);
103 std::size_t counter = 0;
104 for (
auto const& entity_info : mesh_entity_map)
106 auto const& partition_pv =
107 partition_property_vectors[entity_info.partition_id];
108 for (
int i_com = 0; i_com < pv_num_components; i_com++)
110 (*new_pv)[counter * pv_num_components + i_com] =
111 (*partition_pv)[entity_info.original_id *
121 createNewCellOrNodePropertyVector(merged_node_map);
127 createNewCellOrNodePropertyVector(merged_element_map);
133 std::vector<std::vector<std::size_t>> partition_element_offsets;
134 partition_element_offsets.reserve(partitioned_meshes.size());
135 for (
auto const& mesh : partitioned_meshes)
137 partition_element_offsets.emplace_back(
139 mesh->getElements(), *pv, properties));
143 merged_mesh, pv_name, item_type, pv_num_components);
146 auto const ip_meta_data =
150 auto number_of_integration_points = [&](
auto const* element)
153 ip_meta_data, *element);
156 std::size_t
const counter = ranges::accumulate(
158 ranges::views::transform(number_of_integration_points),
160 new_pv->resize(counter * pv_num_components);
162 auto const global_ip_offsets =
166 std::size_t element_counter = 0;
167 for (
auto const& element_info : merged_element_map)
170 *(partition_property_vectors[element_info.partition_id]);
172 auto const& offsets =
173 partition_element_offsets[element_info.partition_id];
175 int const begin_pos = offsets[element_info.original_id];
176 int const end_pos = offsets[element_info.original_id + 1];
178 std::copy(partition_pv.
begin() + begin_pos,
179 partition_pv.
begin() + end_pos,
180 new_pv->begin() + global_ip_offsets[element_counter]);
191 std::ifstream ins(pvtu_file_name);
195 OGS_FATAL(
"Could not open pvtu file {:s}.", pvtu_file_name);
198 using boost::property_tree::ptree;
202 auto root = pt.get_child(
"VTKFile");
204 std::vector<std::string> vtu_file_names;
208 for (ptree::value_type
const& v : root.get_child(
"PUnstructuredGrid"))
210 if (v.first ==
"Piece")
215 std::filesystem::path(v.second.get(
"<xmlattr>.Source",
""))
221 if (vtu_file_names.empty())
223 OGS_FATAL(
"PVTU file {:s} does not contain any vtu piece",
227 return vtu_file_names;
231std::tuple<std::vector<MeshLib::Node*>, std::vector<std::size_t>>
234 std::vector<std::size_t> number_of_nodes_per_partition;
235 ranges::transform(meshes, std::back_inserter(number_of_nodes_per_partition),
237 {
return mesh->getNumberOfNodes(); });
238 std::vector<std::size_t>
const offsets =
241 std::vector<MeshLib::Node*> all_nodes;
242 all_nodes.reserve(offsets.back());
243 for (
auto const& mesh : meshes)
245 ranges::copy(mesh->getNodes(), std::back_inserter(all_nodes));
247 return {all_nodes, offsets};
250std::tuple<std::vector<MeshLib::Element*>, std::vector<MeshEntityMapInfo>>
253 std::vector<MeshLib::Element*> regular_elements;
255 std::size_t partition_counter = 0;
256 std::vector<MeshEntityMapInfo> merged_element_map;
257 for (
auto const& mesh : meshes)
261 auto const*
const ghost_id_vector =
263 assert(ghost_id_vector);
265 auto const& mesh_elements = mesh->getElements();
267 auto const last_element_id_of_previous_partition =
268 regular_elements.size();
271 {
return (*ghost_id_vector)[element->getID()] == 0; };
273 auto regular_mesh_elements =
274 mesh_elements | ranges::views::filter(is_regular_element);
275 regular_elements.insert(regular_elements.end(),
276 regular_mesh_elements.begin(),
277 regular_mesh_elements.end());
279 for (
auto element_id = last_element_id_of_previous_partition;
280 element_id < regular_elements.size();
283 merged_element_map.push_back(
284 {partition_counter, regular_elements[element_id]->getID()});
290 return {regular_elements, merged_element_map};
297 std::vector<MeshLib::Node*> query_nodes;
299 std::numeric_limits<double>::epsilon() * extent.squaredNorm();
301 auto constexpr dir = std::numeric_limits<double>::max();
302 Eigen::Vector3d
const max = {std::nextafter(node[0] + eps, dir),
303 std::nextafter(node[1] + eps, dir),
304 std::nextafter(node[2] + eps, dir)};
307 if (query_nodes.empty())
310 "query_nodes for node [{}], ({}, {}, {}) of element "
311 "[{}] are empty, eps is {}",
312 node.
getID(), node[0], node[1], node[2], element_id, eps);
315 std::find_if(query_nodes.begin(), query_nodes.end(),
316 [&node, eps](
auto const* p)
318 return (p->asEigenVector3d() - node.asEigenVector3d())
319 .squaredNorm() < eps;
321 if (it == query_nodes.end())
324 "did not find node: [{}] ({}, {}, {}) of element [{}] in "
326 node.
getID(), node[0], node[1], node[2], element_id);
332 std::vector<MeshLib::Element*>
const& regular_elements,
334 Eigen::Vector3d
const& extent)
336 for (
auto& e : regular_elements)
338 for (
unsigned i = 0; i < e->getNumberOfNodes(); i++)
340 auto*
const node = e->getNode(i);
342 if (!oct_tree.
addPoint(node, node_ptr))
345 oct_tree, extent, *node, e->getID());
346 e->setNode(i, node_ptr);
352std::pair<std::vector<MeshLib::Node*>, std::vector<MeshEntityMapInfo>>
354 std::vector<std::size_t>
const& partition_offsets,
357 std::vector<MeshLib::Node*> unique_merged_nodes;
358 unique_merged_nodes.reserve(all_merged_nodes_tmp.size());
360 std::vector<MeshEntityMapInfo> merged_node_map;
361 merged_node_map.reserve(all_merged_nodes_tmp.size());
363 for (std::size_t i = 0; i < partition_offsets.size() - 1; ++i)
365 for (std::size_t pos = partition_offsets[i];
366 pos < partition_offsets[i + 1];
369 auto* node = all_merged_nodes_tmp[pos];
371 if (oct_tree.
addPoint(node, node_ptr))
373 unique_merged_nodes.push_back(node);
374 merged_node_map.push_back({i, pos - partition_offsets[i]});
378 return {unique_merged_nodes, merged_node_map};
381int main(
int argc,
char* argv[])
384 "This tool merges VTU files of PVTU into one single VTU file. Apart "
385 "from the mesh data, all property data are merged as well"
386 "\n\nOpenGeoSys-6 software, version " +
389 "Copyright (c) 2012-2025, OpenGeoSys Community "
390 "(http://www.opengeosys.org)",
393 TCLAP::ValueArg<std::string> output_arg(
394 "o",
"output",
"Output (.vtu). The output mesh file (format = *.vtu)",
395 true,
"",
"OUTPUT_FILE");
398 TCLAP::ValueArg<std::string> input_arg(
399 "i",
"input",
"Input (.pvtu). The partitioned input mesh file",
true,
403 cmd.add(log_level_arg);
404 cmd.parse(argc, argv);
411 OGS_FATAL(
"The extension of input file name {:s} is not \"pvtu\"",
412 input_arg.getValue());
416 OGS_FATAL(
"The extension of output file name {:s} is not \"vtu\"",
417 output_arg.getValue());
422 std::vector<std::unique_ptr<MeshLib::Mesh>> meshes;
423 meshes.reserve(vtu_file_names.size());
427 for (
auto const& file_name : vtu_file_names)
429 auto mesh = std::unique_ptr<MeshLib::Mesh>(
437 "Property vector vtkGhostType does not exist in mesh {:s}.",
441 meshes.emplace_back(std::move(mesh));
443 INFO(
"Reading meshes took {} s", io_timer.
elapsed());
446 merged_element_timer.
start();
451 "Collection of {} regular elements and computing element map took {} s",
452 regular_elements.size(), merged_element_timer.
elapsed());
456 collect_nodes_timer.
start();
457 auto [all_merged_nodes_tmp, partition_offsets] =
459 INFO(
"Collection of {} nodes and computing offsets took {} s",
460 all_merged_nodes_tmp.size(), collect_nodes_timer.
elapsed());
463 merged_nodes_timer.
start();
464 GeoLib::AABB aabb(all_merged_nodes_tmp.begin(), all_merged_nodes_tmp.end());
465 auto oct_tree = std::unique_ptr<GeoLib::OctTree<MeshLib::Node, 16>>(
469 auto [unique_merged_nodes, merged_node_map] =
471 INFO(
"Make nodes unique ({} unique nodes) / computing map took {} s",
472 unique_merged_nodes.size(), merged_nodes_timer.
elapsed());
475 reset_nodes_in_elements_timer.
start();
478 INFO(
"Reset nodes in regular elements took {} s",
479 reset_nodes_in_elements_timer.
elapsed());
482 mesh_creation_timer.
start();
486 MeshLib::Mesh(
"pvtu_merged_mesh", unique_merged_nodes, regular_elements,
488 INFO(
"creation of merged mesh took {} s", mesh_creation_timer.
elapsed());
490 auto const& properties = meshes[0]->getProperties();
493 property_timer.
start();
496 [&, &merged_node_map = merged_node_map,
497 &merged_element_map = merged_element_map](
auto type,
498 auto const& property)
504 properties, merged_node_map, merged_element_map);
506 INFO(
"merge properties into merged mesh took {} s",
512 writing_timer.
start();
513 auto const result = writer.
writeToFile(output_arg.getValue());
516 ERR(
"Could not write mesh to '{:s}'.", output_arg.getValue());
519 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
constexpr PROP_VAL_TYPE * begin()
TCLAP::ValueArg< std::string > makeLogLevelArg()
void initOGSLogger(std::string const &log_level)
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)