OGS
PVD2XDMF.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
4#include <tclap/CmdLine.h>
5
6#include <array>
7#include <boost/property_tree/ptree.hpp>
8#include <boost/property_tree/xml_parser.hpp>
9#include <string>
10
11#include "BaseLib/FileTools.h"
12#include "BaseLib/Logging.h"
13#include "BaseLib/MPI.h"
14#include "BaseLib/MemWatch.h"
15#include "BaseLib/StringTools.h"
17#include "InfoLib/GitInfo.h"
21#include "MeshLib/Mesh.h"
22
23// TODO (naumov) use std::filesystem::path
24std::vector<std::pair<double, std::string>> readPvd(
25 std::string const& pvd_filename)
26{
27 DBUG("Start reading the PVD file {:s}", pvd_filename);
28 boost::property_tree::ptree pvd;
29 read_xml(pvd_filename, pvd,
30 boost::property_tree::xml_parser::trim_whitespace);
31
32 std::vector<std::pair<double, std::string>> timeseries;
33 for (auto const& dataset : pvd.get_child("VTKFile.Collection"))
34 {
35 if (dataset.first != "DataSet")
36 {
37 OGS_FATAL("Expected DataSet tag but got '{:s}'", dataset.first);
38 }
39
40 auto const time = dataset.second.get<double>("<xmlattr>.timestep");
41 auto const file = dataset.second.get<std::string>("<xmlattr>.file");
42 timeseries.emplace_back(time, file);
43 }
44 DBUG("Finished reading the PVD file {:s}", pvd_filename);
45
46 return timeseries;
47}
48
49template <typename T>
51 MeshLib::PropertyVectorBase* destination_pv)
52{
53 if (!dynamic_cast<MeshLib::PropertyVector<T>*>(destination_pv))
54 {
55 return false;
56 }
57
58 auto const* pv = properties.getPropertyVector<T>(
59 destination_pv->getPropertyName(), destination_pv->getMeshItemType(),
60 destination_pv->getNumberOfGlobalComponents());
61
62 assert(pv != nullptr);
63
64 std::copy(
65 std::begin(*pv), std::end(*pv),
66 std::begin(dynamic_cast<MeshLib::PropertyVector<T>&>(*destination_pv)));
67
68 return true;
69}
70
72 MeshLib::Mesh const& main_mesh,
73 std::string error_message)
74{
75 if (mesh.getDimension() != main_mesh.getDimension())
76 {
78 "{:s}The mesh has dimension {} which is different from the "
79 "dimension {} of in the main mesh.",
80 error_message, mesh.getDimension(), main_mesh.getDimension());
81 }
82 if (mesh.getNumberOfElements() != main_mesh.getNumberOfElements())
83 {
85 "{:s}The mesh has {} elements which is different from the number "
86 "of elements ({}) in the main mesh.",
87 error_message, mesh.getNumberOfElements(),
88 main_mesh.getNumberOfElements());
89 }
90 if (mesh.getNumberOfNodes() != main_mesh.getNumberOfNodes())
91 {
93 "{:s}The mesh has {} nodes which is different from the number of "
94 "nodes ({}) in the main mesh.",
95 error_message, mesh.getNumberOfNodes(),
96 main_mesh.getNumberOfNodes());
97 }
98}
99
100int main(int argc, char* argv[])
101{
102 TCLAP::CmdLine cmd(
103 "Converts a time series from PVD to XDMF format.\n\n"
104 "OpenGeoSys-6 software, version " +
106 ".\n"
107 "Copyright (c) 2012-2026, OpenGeoSys Community "
108 "(http://www.opengeosys.org)",
110
111 auto log_level_arg = BaseLib::makeLogLevelArg();
112 cmd.add(log_level_arg);
113
114 TCLAP::UnlabeledValueArg<std::string> pvd_file_arg(
115 "pvd-file", "Input (.pvd) file", true, "", "INPUT_FILE");
116 cmd.add(pvd_file_arg);
117
118 TCLAP::ValueArg<std::string> outdir_arg(
119 "o", "output-directory", "Output. The output directory to write to",
120 false, ".", "OUTPUT_PATH");
121 cmd.add(outdir_arg);
122
123 cmd.parse(argc, argv);
124
125 BaseLib::MPI::Setup mpi_setup(argc, argv);
126 BaseLib::initOGSLogger(log_level_arg.getValue());
127
128 auto const pvd_file_dir = BaseLib::extractPath(pvd_file_arg.getValue());
129
130 auto const timeseries = readPvd(pvd_file_arg.getValue());
131
132 if (timeseries.empty())
133 {
134 OGS_FATAL("Empty time series.");
135 }
136
137 // Initialized from the first timeseries entry and data is updated for each
138 // subsequent time step.
139 std::unique_ptr<MeshLib::Mesh> main_mesh;
140
141 std::filesystem::path const output_file{
142 BaseLib::extractBaseNameWithoutExtension(pvd_file_arg.getValue()) +
143 ".xdmf"};
144 std::filesystem::path output_file_path{outdir_arg.getValue()};
145 if (outdir_arg.getValue() != "")
146 {
147 output_file_path =
148 BaseLib::joinPaths(outdir_arg.getValue(), output_file.string());
149 }
150 std::set<std::string> variable_output_names;
151 std::unique_ptr<MeshLib::IO::XdmfHdfWriter> mesh_xdmf_hdf_writer;
152 // read first file in the time series; it is determining variables.
153 {
154 auto [time, filename] = timeseries[0];
155 DBUG("{} - {}", time, filename);
156
157 main_mesh.reset(MeshLib::IO::readMeshFromFile(
158 BaseLib::joinPaths(pvd_file_dir, filename)));
159 if (main_mesh == nullptr)
160 {
161 OGS_FATAL("Could not read mesh from '{:s}'.",
162 BaseLib::joinPaths(pvd_file_dir, filename));
163 }
164
165 for (auto const& p : main_mesh->getProperties())
166 {
167 variable_output_names.insert(std::string(p.first));
168 }
169 mesh_xdmf_hdf_writer = std::make_unique<MeshLib::IO::XdmfHdfWriter>(
170 std::vector{std::cref(*main_mesh)}, output_file_path,
171 0 /*timestep*/, time, variable_output_names,
172 true /*output_file.compression*/, 1 /*output_file.n_files*/,
173 1048576 /*chunk_size_bytes*/);
174 }
175
176 for (std::size_t timestep = 1; timestep < timeseries.size(); ++timestep)
177 {
178 auto [time, filename] = timeseries[timestep];
179 DBUG("{} - {}", time, filename);
180
181 std::unique_ptr<MeshLib::Mesh> mesh{MeshLib::IO::readMeshFromFile(
182 BaseLib::joinPaths(pvd_file_dir, filename))};
183 if (mesh == nullptr)
184 {
185 OGS_FATAL("Could not read mesh from '{:s}'.",
186 BaseLib::joinPaths(pvd_file_dir, filename));
187 }
188
190 *mesh, *main_mesh,
191 fmt::format("Error in comparison of mesh from file '{:s}' to the "
192 "main mesh:\n",
193 BaseLib::joinPaths(pvd_file_dir, filename)));
194
195 // We have to copy the values because the xdmf writer remembers the data
196 // pointers. Therefore replacing the properties will not work.
197 for (auto& [name, pv] : main_mesh->getProperties())
198 {
199 if (copyPropertyVector<double>(mesh->getProperties(), pv) ||
200 copyPropertyVector<float>(mesh->getProperties(), pv) ||
201 copyPropertyVector<int>(mesh->getProperties(), pv) ||
202 copyPropertyVector<long>(mesh->getProperties(), pv) ||
203 copyPropertyVector<unsigned>(mesh->getProperties(), pv) ||
204 copyPropertyVector<unsigned long>(mesh->getProperties(), pv) ||
205 copyPropertyVector<std::size_t>(mesh->getProperties(), pv) ||
206 copyPropertyVector<char>(mesh->getProperties(), pv) ||
207 copyPropertyVector<unsigned char>(mesh->getProperties(), pv))
208 {
209 continue;
210 }
211 OGS_FATAL(
212 "Could not copy property vector '{:s}' from '{:s}' mesh to the "
213 "main_mesh.",
214 name, BaseLib::joinPaths(pvd_file_dir, filename));
215 }
216
217 mesh_xdmf_hdf_writer->writeStep(time);
218 }
219 return EXIT_SUCCESS;
220}
#define OGS_FATAL(...)
Definition Error.h:19
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
int main(int argc, char *argv[])
Definition PVD2XDMF.cpp:100
void testIfMeshesAreEqual(MeshLib::Mesh const &mesh, MeshLib::Mesh const &main_mesh, std::string error_message)
Definition PVD2XDMF.cpp:71
std::vector< std::pair< double, std::string > > readPvd(std::string const &pvd_filename)
Definition PVD2XDMF.cpp:24
bool copyPropertyVector(MeshLib::Properties const &properties, MeshLib::PropertyVectorBase *destination_pv)
Definition PVD2XDMF.cpp:50
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:79
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:91
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
PropertyVector< T > const * getPropertyVector(std::string_view name) const
MeshItemType getMeshItemType() const
int getNumberOfGlobalComponents() const
std::string const & getPropertyName() const
TCLAP::ValueArg< std::string > makeLogLevelArg()
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:56
std::string extractPath(std::string const &pathname)
std::string extractBaseNameWithoutExtension(std::string const &pathname)
std::string joinPaths(std::string const &pathA, std::string const &pathB)
GITINFOLIB_EXPORT const std::string ogs_version
MeshLib::Mesh * readMeshFromFile(const std::string &file_name, bool const compute_element_neighbors)