DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
xdmf_utils.h
1// Copyright (C) 2012-2024 Chris N. Richardson, Jørgen S. Dokken
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "HDF5Interface.h"
10#include <array>
11#include <basix/mdspan.hpp>
12#include <boost/algorithm/string.hpp>
13#include <boost/lexical_cast.hpp>
14#include <dolfinx/common/MPI.h>
15#include <dolfinx/common/types.h>
16#include <filesystem>
17#include <format>
18#include <iterator>
19#include <numeric>
20#include <pugixml.hpp>
21#include <span>
22#include <string>
23#include <string_view>
24#include <utility>
25#include <vector>
26
27namespace dolfinx
28{
29namespace fem
30{
31template <dolfinx::scalar T, std::floating_point U>
32class Function;
33} // namespace fem
34
35namespace fem
36{
37template <std::floating_point T>
40} // namespace fem
41
42namespace mesh
43{
44template <std::floating_point T>
45class Mesh;
46class Topology;
47enum class CellType : std::int8_t;
48} // namespace mesh
49
50namespace io::xdmf_utils
51{
52
55std::pair<std::string, int> get_cell_type(const pugi::xml_node& topology_node);
56
59std::array<std::string, 2> get_hdf5_paths(const pugi::xml_node& dataitem_node);
60
61std::filesystem::path
62get_hdf5_filename(const std::filesystem::path& xdmf_filename);
63
65std::vector<std::int64_t> get_dataset_shape(const pugi::xml_node& dataset_node);
66
68std::int64_t get_num_cells(const pugi::xml_node& topology_node);
69
71std::string vtk_cell_type_str(mesh::CellType cell_type, int num_nodes);
72
74template <typename T>
75void add_data_item(pugi::xml_node& xml_node, hid_t h5_id,
76 std::string_view h5_path, std::span<const T> x,
77 std::int64_t offset, const std::vector<std::int64_t>& shape,
78 std::string_view number_type, bool use_mpi_io)
79{
80 // Add DataItem node
81 assert(xml_node);
82 pugi::xml_node data_item_node = xml_node.append_child("DataItem");
83 assert(data_item_node);
84
85 // Add dimensions attribute
86 std::string dims;
87 for (auto d : shape)
88 std::format_to(std::back_inserter(dims), "{} ", d);
89 dims.pop_back();
90 data_item_node.append_attribute("Dimensions") = dims.c_str();
91
92 // Set type for topology data (needed by XDMF to prevent default to
93 // float)
94 if (!number_type.empty())
95 {
96 data_item_node.append_attribute("NumberType")
97 = std::string(number_type).c_str();
98 }
99
100 // Add format attribute
101 if (h5_id < 0)
102 {
103 data_item_node.append_attribute("Format") = "XML";
104 assert(shape.size() == 2);
105 std::string s;
106 for (std::size_t i = 0; i < x.size(); ++i)
107 {
108 if constexpr (std::floating_point<T>)
109 std::format_to(std::back_inserter(s), "{:.16}", x.data()[i]);
110 else
111 std::format_to(std::back_inserter(s), "{}", x.data()[i]);
112 s += ((i + 1) % shape[1] == 0 and shape[1] != 0) ? '\n' : ' ';
113 }
114
115 data_item_node.append_child(pugi::node_pcdata).set_value(s.c_str());
116 }
117 else
118 {
119 data_item_node.append_attribute("Format") = "HDF";
120
121 // Add HDF5 filename (excluding path) and HDF5 internal path to the
122 // XML file
123 const std::filesystem::path filename
124 = io::hdf5::get_filename(h5_id).filename();
125 const std::string xdmf_path
126 = std::format("{}:{}", filename.string(), h5_path);
127 data_item_node.append_child(pugi::node_pcdata).set_value(xdmf_path.c_str());
128
129 // Compute data offset and range of values
130 std::int64_t local_shape0 = std::reduce(
131 std::next(shape.begin()), shape.end(), x.size(), std::divides{});
132
133 const std::array local_range{offset, offset + local_shape0};
134 io::hdf5::write_dataset(h5_id, h5_path, x.data(), local_range, shape,
135 use_mpi_io, false);
136
137 // Add partitioning attribute to dataset
138 // std::vector<std::size_t> partitions;
139 // std::vector<std::size_t> offset_tmp(1, offset);
140 // dolfinx::MPI::gather(comm, offset_tmp, partitions);
141 // dolfinx::MPI::broadcast(comm, partitions);
142 // io::hdf5::add_attribute(h5_id, h5_path, "partition", partitions);
143 }
144}
145
150template <typename T>
151std::vector<T> get_dataset(MPI_Comm comm, const pugi::xml_node& dataset_node,
152 hid_t h5_id,
153 std::array<std::int64_t, 2> range = {0, 0})
154{
155 // FIXME: Need to sort out dataset dimensions - can't depend on HDF5
156 // shape, and a Topology data item is not required to have a
157 // 'Dimensions' attribute since the dimensions can be determined from
158 // the number of cells and the cell type (for topology, one must
159 // supply cell type + (number of cells or dimensions)).
160 //
161 // A geometry data item must have 'Dimensions' attribute.
162
163 assert(dataset_node);
164 pugi::xml_attribute format_attr = dataset_node.attribute("Format");
165 assert(format_attr);
166
167 // Get data set shape from 'Dimensions' attribute (empty if not
168 // available)
169 const std::vector shape_xml = xdmf_utils::get_dataset_shape(dataset_node);
170
171 const std::string format = format_attr.as_string();
172 std::vector<T> data_vector;
173 // Only read ASCII on process 0
174 const int mpi_rank = dolfinx::MPI::rank(comm);
175 if (format == "XML")
176 {
177 if (mpi_rank == 0)
178 {
179 // Read data and trim any leading/trailing whitespace
180 pugi::xml_node data_node = dataset_node.first_child();
181 assert(data_node);
182 std::string data_str = data_node.value();
183
184 // Split data based on spaces and line breaks
185 std::vector<boost::iterator_range<std::string::iterator>> data_vector_str;
186 boost::split(data_vector_str, data_str, boost::is_any_of(" \n"));
187
188 // Add data to numerical vector
189 data_vector.reserve(data_vector_str.size());
190 for (auto& v : data_vector_str)
191 {
192 if (v.begin() != v.end())
193 {
194 data_vector.push_back(
195 boost::lexical_cast<T>(boost::copy_range<std::string>(v)));
196 }
197 }
198 }
199 }
200 else if (format == "HDF")
201 {
202 // Get file and data path
203 auto paths = xdmf_utils::get_hdf5_paths(dataset_node);
204
205 // Get data shape from HDF5 file
206 const std::vector shape_hdf5 = io::hdf5::get_dataset_shape(h5_id, paths[1]);
207
208 // FIXME: should we support empty data sets?
209 // Check that data set is not empty
210 assert(!shape_hdf5.empty());
211 assert(shape_hdf5[0] != 0);
212
213 // Determine range of data to read from HDF5 file. This is
214 // complicated by the XML Dimension attribute and the HDF5 storage
215 // possibly having different shapes, e.g. the HDF5 storage may be a
216 // flat array.
217
218 // If range = {0, 0} then no range is supplied and we must determine
219 // the range
220 if (range[0] == 0 and range[1] == 0)
221 {
222 if (shape_xml == shape_hdf5)
223 {
224 range = common::local_range(mpi_rank, shape_hdf5[0],
225 dolfinx::MPI::size(comm));
226 }
227 else if (!shape_xml.empty() and shape_hdf5.size() == 1)
228 {
229 // Size of dims > 0
230 std::int64_t d = std::reduce(shape_xml.begin(), shape_xml.end(),
231 std::int64_t(1), std::multiplies{});
232
233 // Check for data size consistency
234 if (d * shape_xml[0] != shape_hdf5[0])
235 {
236 throw std::runtime_error("Data size in XDMF/XML and size of HDF5 "
237 "dataset are inconsistent");
238 }
239
240 // Compute data range to read
241 range = common::local_range(mpi_rank, shape_xml[0],
242 dolfinx::MPI::rank(comm));
243 range[0] *= d;
244 range[1] *= d;
245 }
246 else
247 {
248 throw std::runtime_error("This combination of array shapes in XDMF and "
249 "HDF5 is not supported");
250 }
251 }
252
253 // Retrieve data
254 data_vector = io::hdf5::read_dataset<T>(h5_id, paths[1], range, true);
255 }
256 else
257 throw std::runtime_error(
258 std::format("Storage format \"{}\" is unknown", format));
259
260 // Get dimensions for consistency (if available in DataItem node)
261 if (shape_xml.empty())
262 {
263 std::int64_t size = 1;
264 for (auto dim : shape_xml)
265 size *= dim;
266
267 std::int64_t size_global = 0;
268 const std::int64_t size_local = data_vector.size();
269 MPI_Allreduce(&size_local, &size_global, 1, MPI_INT64_T, MPI_SUM, comm);
270 if (size != size_global)
271 {
272 throw std::runtime_error(
273 "Data sizes in attribute and size of data read are inconsistent");
274 }
275 }
276
277 return data_vector;
278}
279
280} // namespace io::xdmf_utils
281} // namespace dolfinx
Definition CoordinateElement.h:39
Definition ElementDofLayout.h:31
Definition Function.h:44
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:25
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
int size(MPI_Comm comm)
Definition MPI.cpp:81
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:73
constexpr std::array< std::int64_t, 2 > local_range(int index, std::int64_t N, int size)
Partition a global range [0, N - 1] across callers into non-overlapping sub-partitions of almost equa...
Definition local_range.h:26
Finite element method functionality.
Definition assemble_expression_impl.h:28
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
CellType
Cell type identifier.
Definition cell_types.h:24
Top-level namespace.
Definition defines.h:12