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