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