Loading [MathJax]/extensions/MathZoom.js
OGS
writeXdmf.cpp
Go to the documentation of this file.
1
10#include "writeXdmf.h"
11
12#include <spdlog/fmt/bundled/ranges.h>
13
14#include <algorithm>
15#include <array>
16#include <fstream>
17#include <iostream>
18#include <iterator>
19#include <list>
20#include <map>
21#include <optional>
22#include <string_view>
23#include <vector>
24
25#include "BaseLib/Error.h"
26#include "BaseLib/cpp23.h"
27#include "MeshLib/Location.h"
28
29// naming convention for local function objects by postfix:
30// _transform: functions that take data (mostly XDMF meta data) and return
31// transformed data (XDMF string)
32// _fn: functions that take a function and return a new function
33
34using namespace fmt::literals;
35using namespace BaseLib;
36namespace MeshLib::IO
37{
38// similar definition in Location.h - XDMF uses capital letters
39// There is no correspondece for "IntegrationPoint" in XDMF Format
40// specification - add it as "Other"
41constexpr char const* mesh_item_type_strings[] = {"Node", "Edge", "Face",
42 "Cell", "Other"};
43
44// Transforms MeshItemType into string
45static std::string meshItemTypeString(
46 std::optional<MeshItemType> const& item_type)
47{
48 if (item_type)
49 {
50 return mesh_item_type_strings[static_cast<int>(item_type.value())];
51 }
52 OGS_FATAL("Cannot convert an empty optional mesh item type.");
53}
54
55// Transforms MeshPropertyDataType into string
56// constexpr with literal type std::string not yet supported
58{
59 std::array<std::string, to_underlying(MeshPropertyDataType::enum_length)>
60 ogs_to_xdmf_type = {};
61 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::float64)] = "Float";
62 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::float32)] = "Float";
63 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int32)] = "Int";
64 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int64)] = "Int";
65 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint32)] = "UInt";
66 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint64)] = "UInt";
67 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int8)] = "Int";
68 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint8)] = "UInt";
69 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::char_native)] = "Char";
70 ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uchar)] = "UChar";
71 return ogs_to_xdmf_type;
72}
73
74// Transform MeshPropertyDatatype into string.
75static std::string getPropertyDataTypeString(
76 MeshPropertyDataType const& ogs_data_type)
77{
78 return meshPropertyDatatypeString()[to_underlying(ogs_data_type)];
79}
80
81// Returns MeshPropertyDataType-to-Size (in bytes) lookup-table
82static constexpr auto meshPropertyDatatypeSize()
83{
85 property_sizes{};
88 property_sizes[to_underlying(MeshPropertyDataType::int32)] = 4,
89 property_sizes[to_underlying(MeshPropertyDataType::int64)] = 8,
92 property_sizes[to_underlying(MeshPropertyDataType::int8)] = 1,
93 property_sizes[to_underlying(MeshPropertyDataType::uint8)] = 1;
94 return property_sizes;
95}
96
98
99// Transform MeshPropertyDataType into type_sizes (in bytes)
100static std::string getPropertyDataTypeSize(
101 MeshPropertyDataType const& ogs_data_type)
102{
103 return fmt::format("{}", ogs_to_xdmf_type_fn[to_underlying(ogs_data_type)]);
104}
105
106// Collects all known data and return a function that takes all unknown data
107// known: XdmfData of mesh to be written, unknown: timesteps
108std::function<std::string(std::vector<double>)> write_xdmf(
109 XdmfData const& geometry, XdmfData const& topology,
110 std::vector<XdmfData> const& constant_attributes,
111 std::vector<XdmfData> const& variable_attributes,
112 std::string const& h5filename, std::string const& ogs_version,
113 std::string const& mesh_name)
114{
115 // Generates function that writes <DataItem>. Late binding needed because
116 // maximum number of steps unknown. Time step and h5filename are captured to
117 // return a unary function
118 // _a suffix is a user defined literal for fmt named arguments
119 auto const time_dataitem_genfn =
120 [](unsigned long long const time_step, int const max_step,
121 std::string const& h5filename, std::string const& mesh_name)
122 {
123 return
124 [time_step, max_step, h5filename, mesh_name](auto const& xdmfdata)
125 {
126 return fmt::format(
127 fmt::runtime("\n\t\t<DataItem DataType=\"{datatype}\" "
128 "Dimensions=\"{local_dimensions}\" "
129 "Format=\"HDF\" "
130 "Precision=\"{precision}\">"
131 "{filename}:/meshes/{meshname}/{datasetname}|"
132 "{time_step} {starts}:1 {strides}:1 "
133 "{local_dimensions}:{max_step} "
134 "{global_dimensions}</"
135 "DataItem>"),
136 "datatype"_a = getPropertyDataTypeString(xdmfdata.data_type),
137 "local_dimensions"_a =
138 fmt::join(xdmfdata.global_block_dims, " "),
139 "precision"_a = getPropertyDataTypeSize(xdmfdata.data_type),
140 "filename"_a = h5filename,
141 "meshname"_a = mesh_name,
142 "datasetname"_a = xdmfdata.name,
143 "starts"_a = fmt::join(xdmfdata.starts, " "),
144 "strides"_a = fmt::join(xdmfdata.strides, " "),
145 "global_dimensions"_a =
146 fmt::join(xdmfdata.global_block_dims, " "),
147 "time_step"_a = fmt::format("{}", time_step),
148 "max_step"_a = fmt::format("{}", max_step));
149 };
150 };
151
152 // string_join could be moved to ogs lib if used more
153 auto const string_join_fn = [](auto const& collection)
154 { return fmt::to_string(fmt::join(collection, "")); };
155
156 // m_bind could be moved to ogs lib if used more
157 auto const m_bind_fn = [](auto const& transform, auto const& join)
158 {
159 return [join, transform](auto const& collection)
160 {
161 std::vector<std::string> temp;
162 temp.reserve(collection.size());
163 std::transform(collection.begin(), collection.end(),
164 std::back_inserter(temp), transform);
165 return join(temp);
166 };
167 };
168
169 // XDMF if part of the data that is already written in any previous step
170 // subsequent steps can refer to this data collection of elements navigates
171 // the xml tree (take first child, then in child take 2nd child ...)
172 auto const pointer_transfrom = [](auto const& elements)
173 {
174 return fmt::format(
175 fmt::runtime("\n\t<xi:include xpointer=\"element(/{elements})\"/>"),
176 "elements"_a = fmt::join(elements, "/"));
177 };
178
179 // Defines content of <Attribute> in XDMF, child of Attribute is a single
180 // <DataItem>
181 auto const attribute_transform =
182 [](XdmfData const& attribute, auto const& dataitem_transform)
183 {
184 return fmt::format(
185 "\n\t<Attribute Center=\"{center}\" ElementCell=\"\" "
186 "ElementDegree=\"0\" "
187 "ElementFamily=\"\" ItemType=\"\" Name=\"{name}\" "
188 "Type=\"None\">{dataitem}\n\t</Attribute>",
189 "center"_a = meshItemTypeString(attribute.attribute_center),
190 "name"_a = attribute.name,
191 "dataitem"_a = dataitem_transform(attribute));
192 };
193
194 // Define content of <Geometry> in XDMF, same as attribute_transform
195 auto const geometry_transform =
196 [](XdmfData const& geometry, auto const& dataitem_transform)
197 {
198 return fmt::format(
199 fmt::runtime("\n\t<Geometry Origin=\"\" "
200 "Type=\"XYZ\">{dataitem}\n\t</Geometry>"),
201 "dataitem"_a = dataitem_transform(geometry));
202 };
203
204 auto tag_string = [](auto const& topology, int nodes_per_element,
205 auto const& dataitem_transform)
206 {
207 return fmt::format(
208 fmt::runtime("\n\t<Topology Type=\"{topology_type}\" "
209 "NodesPerElement=\"{nodes_per_element}\" "
210 "NumberOfElements=\"{number_of_elements}\">{dataitem}"
211 "\n\t</Topology>"),
212 "topology_type"_a =
214 "dataitem"_a = dataitem_transform(topology),
215 "nodes_per_element"_a = nodes_per_element,
216 "number_of_elements"_a = topology.size_partitioned_dim);
217 };
218
219 // Define content of <Topology> in XDMF, same as attribute_transform
220 auto const topology_transform =
221 [&tag_string](XdmfData const& topology, auto const& dataitem_transform)
222 {
223 switch (*topology.parent_data_type)
224 {
226 return tag_string(topology, 1, dataitem_transform);
228 return tag_string(topology, 2, dataitem_transform);
246 return fmt::format(
247 fmt::runtime(
248 "\n\t<Topology "
249 "Type=\"{topology_type}\">{dataitem}\n\t</Topology>"),
250 "topology_type"_a =
252 "dataitem"_a = dataitem_transform(topology));
253 }
254 OGS_FATAL("Could not transform unknown XDMF topology type");
255 return std::string{};
256 };
257
258 // Defines content of <Grid> of a single time step, takes specific transform
259 // functions for all of its child elements
260 auto const grid_transform =
261 [](double const& time_value, auto const& geometry, auto const& topology,
262 auto const& constant_attributes, auto const& variable_attributes)
263 {
264 return fmt::format(
265 R"(
266<Grid Name="Grid" GridType="Uniform">
267 <Time Value="{time_value:.{precision}g}"/>
268{geometry}
269{topology}
270{fix_attributes}
271{variable_attributes}
272</Grid>)",
273 "time_value"_a = time_value,
274 // Output of "Time Value" with sufficient precision.
275 "precision"_a = std::numeric_limits<double>::max_digits10,
276 "geometry"_a = geometry,
277 "topology"_a = topology,
278 "fix_attributes"_a = constant_attributes,
279 "variable_attributes"_a = variable_attributes);
280 };
281
282 // An attribute may change over time (variable) or stay constant
283 enum class time_attribute
284 {
285 constant,
286 variable
287 };
288
289 // Generates a function that either writes the data or points to existing
290 // data
291 auto const time_step_fn = [time_dataitem_genfn, pointer_transfrom,
292 h5filename,
293 mesh_name](auto const& component_transform,
294 unsigned long long const time_step,
295 int const max_step,
296 time_attribute const variable)
297 {
298 return [component_transform, time_dataitem_genfn, pointer_transfrom,
299 variable, time_step, max_step, h5filename,
300 mesh_name](XdmfData const& attr)
301 {
302 // new data arrived
303 bool const changed =
304 ((time_step > 0 && (variable == time_attribute::variable)) ||
305 time_step == 0);
306 if (changed)
307 {
308 auto dataitem = time_dataitem_genfn(time_step, max_step,
309 h5filename, mesh_name);
310 return component_transform(attr, dataitem);
311 }
312 else
313 {
314 std::array<unsigned int, 5> position = {1, 1, 2, 1, attr.index};
315 return pointer_transfrom(position);
316 };
317 };
318 };
319
320 // Top-Level transform function (take spatial and temporal transform
321 // functions) and return the time depended grid transform function
322 // ToDo (tm) Remove capturing m_bind and string_join as helper function
323
324 auto const time_grid_transform =
325 [time_step_fn, m_bind_fn, string_join_fn, grid_transform,
326 geometry_transform, topology_transform, attribute_transform](
327 double const time, int const step, int const max_step,
328 auto const& geometry, auto const& topology,
329 auto const& constant_attributes, auto const& variable_attributes)
330 {
331 auto const time_step_geometry_transform = time_step_fn(
332 geometry_transform, step, max_step, time_attribute::constant);
333 auto const time_step_topology_transform = time_step_fn(
334 topology_transform, step, max_step, time_attribute::constant);
335 auto const time_step_const_attribute_fn = time_step_fn(
336 attribute_transform, step, max_step, time_attribute::constant);
337 auto const time_step_variable_attribute_fn = time_step_fn(
338 attribute_transform, step, max_step, time_attribute::variable);
339
340 // lift both functions from handling single attributes to work with
341 // collection of attributes
342 auto const variable_attributes_transform =
343 m_bind_fn(time_step_variable_attribute_fn, string_join_fn);
344 auto const constant_attributes_transform =
345 m_bind_fn(time_step_const_attribute_fn, string_join_fn);
346
347 return grid_transform(
348 time, time_step_geometry_transform(geometry),
349 time_step_topology_transform(topology),
350 constant_attributes_transform(constant_attributes),
351 variable_attributes_transform(variable_attributes));
352 };
353
354 // Function that combines all the data from spatial (geometry, topology,
355 // attribute) and temporal domain (time) with the time domain
356 // (time_step_fn). And writes <Grid CollectionType="Temporal">
357 auto const temporal_grid_collection_transform =
358 [time_grid_transform](
359 auto const& times, auto const& geometry, auto const& topology,
360 auto const& constant_attributes, auto const& variable_attributes)
361 {
362 // c++20 ranges zip missing
363 auto const max_step = times.size();
364 std::vector<std::string> grids;
365 grids.reserve(max_step);
366 for (size_t time_step = 0; time_step < max_step; ++time_step)
367 {
368 grids.push_back(time_grid_transform(
369 times[time_step], time_step, max_step, geometry, topology,
370 constant_attributes, variable_attributes));
371 }
372 return fmt::format(
373 fmt::runtime(
374 "\n<Grid CollectionType=\"Temporal\" GridType=\"Collection\" "
375 "Name=\"Collection\">{grids}\n</Grid>\n"),
376 "grids"_a = fmt::join(grids, ""));
377 };
378
379 // Generator function that return a function that represents complete xdmf
380 // structure
381 auto const xdmf_writer_fn = [temporal_grid_collection_transform](
382 auto ogs_version, auto geometry,
383 auto topology, auto constant_attributes,
384 auto variable_attributes)
385 {
386 // This function will be the return of the surrounding named function
387 // capture by copy - it is the function that will survive this scope!!
388 // Use generalized lambda capture to move for optimization
389 return [temporal_grid_collection_transform,
390 ogs_version = std::move(ogs_version),
391 geometry = std::move(geometry), topology = std::move(topology),
392 constant_attributes = std::move(constant_attributes),
393 variable_attributes = std::move(variable_attributes)](
394 std::vector<double> const& times)
395 {
396 return fmt::format(
397 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Xdmf "
398 "xmlns:xi=\"http://www.w3.org/2001/XInclude\" "
399 "Version=\"3.0\">\n<Domain>\n<Information Name=\"OGS_VERSION\" "
400 "Value=\"{ogs_version}\"/>{grid_collection}</Domain>\n</Xdmf>",
401 "ogs_version"_a = ogs_version,
402 "grid_collection"_a = temporal_grid_collection_transform(
403 times, geometry, topology, constant_attributes,
404 variable_attributes));
405 };
406 };
407
408 return xdmf_writer_fn(ogs_version, geometry, topology, constant_attributes,
409 variable_attributes);
410}
411} // namespace MeshLib::IO
#define OGS_FATAL(...)
Definition Error.h:26
std::pair< std::string, std::size_t > ParentDataType2String(ParentDataType p)
MeshPropertyDataType
constexpr auto to_underlying(E e) noexcept
Converts an enumeration to its underlying type.
Definition cpp23.h:29
static std::string getPropertyDataTypeString(MeshPropertyDataType const &ogs_data_type)
Definition writeXdmf.cpp:75
static auto meshPropertyDatatypeString()
Definition writeXdmf.cpp:57
static std::string meshItemTypeString(std::optional< MeshItemType > const &item_type)
Definition writeXdmf.cpp:45
constexpr char const * mesh_item_type_strings[]
Definition writeXdmf.cpp:41
static std::string getPropertyDataTypeSize(MeshPropertyDataType const &ogs_data_type)
std::function< std::string(std::vector< double >)> write_xdmf(XdmfData const &geometry, XdmfData const &topology, std::vector< XdmfData > const &constant_attributes, std::vector< XdmfData > const &variable_attributes, std::string const &h5filename, std::string const &ogs_version, std::string const &mesh_name)
Generator function that creates a function capturing the spatial data of a mesh Temporal data can lat...
auto ogs_to_xdmf_type_fn
Definition writeXdmf.cpp:97
static constexpr auto meshPropertyDatatypeSize()
Definition writeXdmf.cpp:82
std::optional< MeshLib::MeshItemType > attribute_center
Definition XdmfData.h:68
std::size_t size_partitioned_dim
Definition XdmfData.h:66
std::string name
Definition XdmfData.h:67
std::optional< ParentDataType > parent_data_type
Definition XdmfData.h:70
write_xdmf generates a function based on spatial mesh data. The generated function finally generates ...