4#include <tclap/CmdLine.h>
7#include <boost/property_tree/ptree.hpp>
8#include <boost/property_tree/xml_parser.hpp>
13#include <range/v3/algorithm/copy.hpp>
14#include <range/v3/algorithm/transform.hpp>
15#include <range/v3/numeric/accumulate.hpp>
16#include <range/v3/range/conversion.hpp>
17#include <range/v3/view/filter.hpp>
19#include <unordered_set>
50 std::vector<std::unique_ptr<MeshLib::Mesh>>
const& partitioned_meshes,
53 std::vector<MeshEntityMapInfo>
const& merged_node_map,
54 std::vector<MeshEntityMapInfo>
const& merged_element_map)
73 if (pv_name ==
"OGS_VERSION" || pv_name ==
"IntegrationPointMetaData")
76 merged_mesh, pv_name, item_type, pv_num_components);
82 std::vector<MeshLib::PropertyVector<T>*> partition_property_vectors;
83 partition_property_vectors.reserve(partitioned_meshes.size());
84 for (
auto const& mesh : partitioned_meshes)
86 partition_property_vectors.emplace_back(
87 mesh->getProperties().getPropertyVector<T>(pv_name, item_type,
91 auto createNewCellOrNodePropertyVector =
92 [&](std::vector<MeshEntityMapInfo>
const& mesh_entity_map)
95 merged_mesh, pv_name, item_type, pv_num_components);
96 std::size_t counter = 0;
97 for (
auto const& entity_info : mesh_entity_map)
99 auto const& partition_pv =
100 partition_property_vectors[entity_info.partition_id];
101 for (
int i_com = 0; i_com < pv_num_components; i_com++)
103 (*new_pv)[counter * pv_num_components + i_com] =
104 (*partition_pv)[entity_info.original_id *
114 createNewCellOrNodePropertyVector(merged_node_map);
120 createNewCellOrNodePropertyVector(merged_element_map);
126 std::vector<std::vector<std::size_t>> partition_element_offsets;
127 partition_element_offsets.reserve(partitioned_meshes.size());
128 for (
auto const& mesh : partitioned_meshes)
130 partition_element_offsets.emplace_back(
132 mesh->getElements(), *pv, properties));
136 merged_mesh, pv_name, item_type, pv_num_components);
139 auto const ip_meta_data =
143 auto number_of_integration_points = [&](
auto const* element)
146 ip_meta_data, *element);
149 std::size_t
const counter = ranges::accumulate(
151 ranges::views::transform(number_of_integration_points),
153 new_pv->resize(counter * pv_num_components);
155 auto const global_ip_offsets =
159 std::size_t element_counter = 0;
160 for (
auto const& element_info : merged_element_map)
163 *(partition_property_vectors[element_info.partition_id]);
165 auto const& offsets =
166 partition_element_offsets[element_info.partition_id];
168 int const begin_pos = offsets[element_info.original_id];
169 int const end_pos = offsets[element_info.original_id + 1];
171 std::copy(partition_pv.
begin() + begin_pos,
172 partition_pv.
begin() + end_pos,
173 new_pv->begin() + global_ip_offsets[element_counter]);
184 std::ifstream ins(pvtu_file_name);
188 OGS_FATAL(
"Could not open pvtu file {:s}.", pvtu_file_name);
191 using boost::property_tree::ptree;
195 auto root = pt.get_child(
"VTKFile");
197 std::vector<std::string> vtu_file_names;
201 for (ptree::value_type
const& v : root.get_child(
"PUnstructuredGrid"))
203 if (v.first ==
"Piece")
208 std::filesystem::path(v.second.get(
"<xmlattr>.Source",
""))
214 if (vtu_file_names.empty())
216 OGS_FATAL(
"PVTU file {:s} does not contain any vtu piece",
220 return vtu_file_names;
224std::tuple<std::vector<MeshLib::Node*>, std::vector<std::size_t>>
227 std::vector<std::size_t> number_of_nodes_per_partition;
228 ranges::transform(meshes, std::back_inserter(number_of_nodes_per_partition),
230 {
return mesh->getNumberOfNodes(); });
231 std::vector<std::size_t>
const offsets =
234 std::vector<MeshLib::Node*> all_nodes;
235 all_nodes.reserve(offsets.back());
236 for (
auto const& mesh : meshes)
238 ranges::copy(mesh->getNodes(), std::back_inserter(all_nodes));
240 return {all_nodes, offsets};
243std::tuple<std::vector<MeshLib::Element*>, std::vector<MeshEntityMapInfo>>
246 std::vector<MeshLib::Element*> regular_elements;
248 std::size_t partition_counter = 0;
249 std::vector<MeshEntityMapInfo> merged_element_map;
250 for (
auto const& mesh : meshes)
254 auto const*
const ghost_id_vector =
256 assert(ghost_id_vector);
258 auto const& mesh_elements = mesh->getElements();
260 auto const last_element_id_of_previous_partition =
261 regular_elements.size();
264 {
return (*ghost_id_vector)[element->getID()] == 0; };
266 auto regular_mesh_elements =
267 mesh_elements | ranges::views::filter(is_regular_element);
268 regular_elements.insert(regular_elements.end(),
269 regular_mesh_elements.begin(),
270 regular_mesh_elements.end());
272 for (
auto element_id = last_element_id_of_previous_partition;
273 element_id < regular_elements.size();
276 merged_element_map.push_back(
277 {partition_counter, regular_elements[element_id]->getID()});
283 return {regular_elements, merged_element_map};
290 std::vector<MeshLib::Node*> query_nodes;
292 std::numeric_limits<double>::epsilon() * extent.squaredNorm();
294 auto constexpr dir = std::numeric_limits<double>::max();
295 Eigen::Vector3d
const max = {std::nextafter(node[0] + eps, dir),
296 std::nextafter(node[1] + eps, dir),
297 std::nextafter(node[2] + eps, dir)};
300 if (query_nodes.empty())
303 "query_nodes for node [{}], ({}, {}, {}) of element "
304 "[{}] are empty, eps is {}",
305 node.
getID(), node[0], node[1], node[2], element_id, eps);
308 std::find_if(query_nodes.begin(), query_nodes.end(),
309 [&node, eps](
auto const* p)
311 return (p->asEigenVector3d() - node.asEigenVector3d())
312 .squaredNorm() < eps;
314 if (it == query_nodes.end())
317 "did not find node: [{}] ({}, {}, {}) of element [{}] in "
319 node.
getID(), node[0], node[1], node[2], element_id);
325 std::vector<MeshLib::Element*>
const& regular_elements,
327 Eigen::Vector3d
const& extent)
329 for (
auto& e : regular_elements)
331 for (
unsigned i = 0; i < e->getNumberOfNodes(); i++)
333 auto*
const node = e->getNode(i);
335 if (!oct_tree.
addPoint(node, node_ptr))
338 oct_tree, extent, *node, e->getID());
339 e->setNode(i, node_ptr);
345std::pair<std::vector<MeshLib::Node*>, std::vector<MeshEntityMapInfo>>
347 std::vector<std::size_t>
const& partition_offsets,
350 std::vector<MeshLib::Node*> unique_merged_nodes;
351 unique_merged_nodes.reserve(all_merged_nodes_tmp.size());
353 std::vector<MeshEntityMapInfo> merged_node_map;
354 merged_node_map.reserve(all_merged_nodes_tmp.size());
356 for (std::size_t i = 0; i < partition_offsets.size() - 1; ++i)
358 for (std::size_t pos = partition_offsets[i];
359 pos < partition_offsets[i + 1];
362 auto* node = all_merged_nodes_tmp[pos];
364 if (oct_tree.
addPoint(node, node_ptr))
366 unique_merged_nodes.push_back(node);
367 merged_node_map.push_back({i, pos - partition_offsets[i]});
371 return {unique_merged_nodes, merged_node_map};
375 std::vector<std::unique_ptr<MeshLib::Mesh>>
const& meshes)
378 merged_element_timer.
start();
383 "Collection of {} regular elements and computing element map took {} s",
384 regular_elements.size(), merged_element_timer.
elapsed());
388 collect_nodes_timer.
start();
389 auto [all_merged_nodes_tmp, partition_offsets] =
391 INFO(
"Collection of {} nodes and computing offsets took {} s",
392 all_merged_nodes_tmp.size(), collect_nodes_timer.
elapsed());
395 merged_nodes_timer.
start();
396 GeoLib::AABB aabb(all_merged_nodes_tmp.begin(), all_merged_nodes_tmp.end());
397 auto oct_tree = std::unique_ptr<GeoLib::OctTree<MeshLib::Node, 16>>(
401 auto [unique_merged_nodes, merged_node_map] =
403 INFO(
"Make nodes unique ({} unique nodes) / computing map took {} s",
404 unique_merged_nodes.size(), merged_nodes_timer.
elapsed());
407 reset_nodes_in_elements_timer.
start();
410 INFO(
"Reset nodes in regular elements took {} s",
411 reset_nodes_in_elements_timer.
elapsed());
414 mesh_creation_timer.
start();
417 auto merged_mesh = std::make_unique<MeshLib::Mesh>(
418 "pvtu_merged_mesh", unique_merged_nodes, regular_elements,
420 INFO(
"creation of merged mesh took {} s", mesh_creation_timer.
elapsed());
422 auto const& properties = meshes[0]->getProperties();
425 property_timer.
start();
426 applyToPropertyVectors(
428 [&, &merged_node_map = merged_node_map,
429 &merged_element_map = merged_element_map](
auto type,
430 auto const& property)
433 *merged_mesh, meshes,
436 properties, merged_node_map, merged_element_map);
438 INFO(
"merge properties into merged mesh took {} s",
447 std::string_view
const property_name,
452 INFO(
"Skipping property '{}' since it is not from type '{}'.",
456 auto const* subdomain_pv =
458 auto const number_of_components =
462 original_mesh, std::string(property_name), mesh_item_type,
463 number_of_components);
465 for (std::size_t i = 0; i < global_ids.
size(); ++i)
467 auto const& global_id = global_ids[i];
468 for (std::remove_cvref_t<
decltype(number_of_components)>
469 component_number = 0;
470 component_number < number_of_components;
473 original_pv->getComponent(global_id, component_number) =
474 subdomain_pv->getComponent(i, component_number);
477 INFO(
"Data array {} from data type '{}' transferred.", property_name,
486 auto const& subdomain_properties = subdomain_mesh.
getProperties();
487 if (!subdomain_properties.existsPropertyVector<std::size_t>(
491 "The data array '{}' is required for the transfer of data from "
492 "subdomain mesh to the global mesh. But it doesn't exist in "
493 "subdomain mesh '{}'.",
496 auto const* global_ids =
499 if (global_ids ==
nullptr)
501 OGS_FATAL(
"The data array '{}' is not available but required.",
505 auto const& property_names =
506 subdomain_properties.getPropertyVectorNames(mesh_item_type);
508 for (
auto const& property_name : property_names)
517 "Skipping property '{}' since it is adjusted locally in "
518 "NodeWiseMeshPartitioner::renumberBulkNodeIdsProperty().",
522 if (property_name ==
"vtkGhostType")
525 "Skipping property 'vtkGhostType' since it is only required "
526 "for parallel execution.");
530 property_name, mesh_item_type) ||
532 property_name, mesh_item_type) ||
534 property_name, mesh_item_type) ||
536 *global_ids, property_name,
539 *global_ids, property_name, mesh_item_type) ||
540 transfer<int>(subdomain_properties, original_mesh, *global_ids,
541 property_name, mesh_item_type) ||
543 property_name, mesh_item_type))
545 INFO(
"Data array {} transferred from '{}' to'{}'", property_name,
561int main(
int argc,
char* argv[])
564 "This tool merges VTU files of PVTU into one single VTU file. Apart "
565 "from the mesh data, all property data are merged as well"
566 "\n\nOpenGeoSys-6 software, version " +
569 "Copyright (c) 2012-2026, OpenGeoSys Community "
570 "(http://www.opengeosys.org)",
573 TCLAP::ValueArg<std::string> output_arg(
574 "o",
"output",
"Output (.vtu). The output mesh file (format = *.vtu)",
575 true,
"",
"OUTPUT_FILE");
578 TCLAP::ValueArg<std::string> input_arg(
579 "i",
"input",
"Input (.pvtu). The partitioned input mesh file",
true,
583 TCLAP::ValueArg<std::string> original_mesh_input_arg(
584 "m",
"original_mesh",
585 "optional, the original unpartitioned input mesh (*.vtu)",
false,
"",
587 cmd.add(original_mesh_input_arg);
591 cmd.add(log_level_arg);
592 cmd.parse(argc, argv);
599 OGS_FATAL(
"The extension of input file name {:s} is not \"pvtu\"",
600 input_arg.getValue());
604 OGS_FATAL(
"The extension of output file name {:s} is not \"vtu\"",
605 output_arg.getValue());
610 std::vector<std::unique_ptr<MeshLib::Mesh>> meshes;
611 meshes.reserve(vtu_file_names.size());
615 for (
auto const& file_name : vtu_file_names)
617 auto mesh = std::unique_ptr<MeshLib::Mesh>(
625 "Property vector vtkGhostType does not exist in mesh {:s}.",
629 meshes.emplace_back(std::move(mesh));
631 INFO(
"Reading meshes took {} s", io_timer.
elapsed());
633 std::unique_ptr<MeshLib::Mesh> merged_mesh;
634 if (original_mesh_input_arg.getValue().empty())
644 merged_mesh = std::unique_ptr<MeshLib::Mesh>(
646 original_mesh_input_arg.getValue()));
647 INFO(
"Reading original unpartitioned mesh took {} s",
649 for (
auto const& subdomain_mesh : meshes)
652 *(subdomain_mesh.get()), *merged_mesh);
659 writing_timer.
start();
660 auto const result = writer.
writeToFile(output_arg.getValue());
663 ERR(
"Could not write mesh to '{:s}'.", output_arg.getValue());
666 INFO(
"writing mesh took {} s", writing_timer.
elapsed());
673 merged_mesh->shallowClean();
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
bool transfer(MeshLib::Properties const &subdomain_properties, MeshLib::Mesh &original_mesh, MeshLib::PropertyVector< std::size_t > const &global_ids, std::string_view const property_name, MeshLib::MeshItemType const mesh_item_type)
int main(int argc, char *argv[])
std::unique_ptr< MeshLib::Mesh > mergeSubdomainMeshes(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
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 transferPropertiesFromPartitionedMeshToUnpartitionedMesh(MeshLib::Mesh const &subdomain_mesh, MeshLib::Mesh &original_mesh, MeshLib::MeshItemType const mesh_item_type)
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)
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.
Properties & getProperties()
const std::string getName() const
Get name of 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()
constexpr std::size_t size() const
TCLAP::ValueArg< std::string > makeLogLevelArg()
void initOGSLogger(std::string const &log_level)
std::string getFileExtension(const std::string &path)
std::string typeToString()
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)
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
constexpr std::string_view globalIDString(MeshLib::MeshItemType const mesh_item_type)
IntegrationPointMetaDataSingleField getIntegrationPointMetaDataSingleField(std::optional< IntegrationPointMetaData > const &ip_meta_data, std::string const &field_name)