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