7#include "HDF5Interface.h"
10#include <dolfinx/common/IndexMap.h>
11#include <dolfinx/io/cells.h>
12#include <dolfinx/mesh/Mesh.h>
13#include <dolfinx/mesh/Topology.h>
21namespace dolfinx::io::VTKHDF
30template <std::
floating_po
int U>
31void write_mesh(
const std::filesystem::path& filename,
32 const mesh::Mesh<U>& mesh)
34 hid_t h5file = hdf5::open_file(mesh.comm(), filename,
"w",
true);
37 hdf5::add_group(h5file,
"VTKHDF");
38 hid_t vtk_group = H5Gopen(h5file,
"VTKHDF", H5P_DEFAULT);
39 hdf5::set_attribute(vtk_group,
"Version", std::vector{2, 2});
40 hdf5::set_attribute(vtk_group,
"Type",
"UnstructuredGrid");
44 std::vector<mesh::CellType> cell_types
45 = mesh.topology()->entity_types(mesh.topology()->dim());
47 std::vector cell_index_maps
48 = mesh.topology()->index_maps(mesh.topology()->dim());
49 std::vector<std::int32_t> num_cells;
50 std::vector<std::int64_t> num_cells_global;
51 for (
auto& im : cell_index_maps)
53 num_cells.push_back(im->size_local());
54 num_cells_global.push_back(im->size_global());
58 std::shared_ptr<const common::IndexMap> geom_imap
59 = mesh.geometry().index_map();
60 std::int64_t size_global = geom_imap->size_global();
61 std::vector<std::int64_t> geom_global_shape = {size_global, 3};
62 std::array<std::int64_t, 2> geom_irange = geom_imap->local_range();
63 hdf5::write_dataset(h5file,
"/VTKHDF/Points", mesh.geometry().x().data(),
64 geom_irange, geom_global_shape,
true,
false);
65 hdf5::write_dataset(h5file,
"VTKHDF/NumberOfPoints", &size_global, {0, 1},
70 std::vector<std::int64_t> topology_flattened;
71 std::vector<std::int64_t> topology_offsets;
72 std::vector<std::uint8_t> vtkcelltypes;
73 for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
75 md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> g_dofmap
76 = mesh.geometry().dofmaps().at(i);
78 std::vector<std::uint16_t> perm
81 std::vector<std::int32_t> local_dm;
82 local_dm.reserve(g_dofmap.extent(1) * num_cells[i]);
83 for (
int j = 0; j < num_cells[i]; ++j)
84 for (std::size_t k = 0; k < g_dofmap.extent(1); ++k)
85 local_dm.push_back(g_dofmap(j, inverse_perm[k]));
87 std::vector<std::int64_t> global_dm(local_dm.size());
88 geom_imap->local_to_global(local_dm, global_dm);
90 topology_flattened.insert(topology_flattened.end(), global_dm.begin(),
92 topology_offsets.insert(topology_offsets.end(), g_dofmap.extent(0),
95 vtkcelltypes.end(), cell_index_maps[i]->size_local(),
100 std::partial_sum(topology_offsets.cbegin(), topology_offsets.cend(),
101 topology_offsets.begin());
103 std::vector<int> num_nodes_per_cell;
104 std::vector<std::int64_t> cell_start_pos;
105 std::vector<std::int64_t> cell_stop_pos;
106 for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
108 num_nodes_per_cell.push_back(mesh.geometry().cmaps().at(i).dim());
109 std::array<std::int64_t, 2> r = cell_index_maps[i]->local_range();
110 cell_start_pos.push_back(r[0]);
111 cell_stop_pos.push_back(r[1]);
115 std::int64_t offset_start_position
116 = std::accumulate(cell_start_pos.begin(), cell_start_pos.end(), 0);
117 std::int64_t offset_stop_position
118 = std::accumulate(cell_stop_pos.begin(), cell_stop_pos.end(), 0);
121 std::int64_t topology_start
122 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
123 cell_start_pos.begin(), 0);
125 std::transform(topology_offsets.cbegin(), topology_offsets.cend(),
126 topology_offsets.begin(),
127 [topology_start](
auto x) { return x + topology_start; });
129 std::int64_t num_all_cells_global
130 = std::accumulate(num_cells_global.begin(), num_cells_global.end(), 0);
131 hdf5::write_dataset(h5file,
"/VTKHDF/Offsets", topology_offsets.data(),
132 {offset_start_position + 1, offset_stop_position + 1},
133 {num_all_cells_global + 1},
true,
false);
136 std::int64_t topology_size_global
137 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
138 num_cells_global.begin(), 0);
140 std::int64_t topology_stop = topology_start + topology_flattened.size();
141 hdf5::write_dataset(h5file,
"/VTKHDF/Connectivity", topology_flattened.data(),
142 {topology_start, topology_stop}, {topology_size_global},
146 hdf5::write_dataset(h5file,
"/VTKHDF/Types", vtkcelltypes.data(),
147 {offset_start_position, offset_stop_position},
148 {num_all_cells_global},
true,
false);
149 hdf5::write_dataset(h5file,
"/VTKHDF/NumberOfConnectivityIds",
150 &topology_size_global, {0, 1}, {1},
true,
false);
151 hdf5::write_dataset(h5file,
"/VTKHDF/NumberOfCells", &num_all_cells_global,
152 {0, 1}, {1},
true,
false);
153 hdf5::close_file(h5file);
177template <std::
floating_po
int U>
178void write_data(std::string_view point_or_cell,
179 const std::filesystem::path& filename,
180 const mesh::Mesh<U>& mesh,
const std::vector<U>& data,
183 std::vector<std::shared_ptr<const common::IndexMap>> index_maps;
184 if (point_or_cell ==
"Point")
185 index_maps = {mesh.geometry().index_map()};
186 else if (point_or_cell ==
"Cell")
187 index_maps = mesh.topology()->index_maps(mesh.topology()->dim());
189 throw std::runtime_error(
"Selection must be Point or Cell");
191 const std::string poc(point_or_cell);
192 std::string dataset_name = std::format(
"/VTKHDF/{}Data/u", poc);
194 = std::accumulate(index_maps.begin(), index_maps.end(), 0,
195 [](
int a,
auto im) { return a + im->size_local(); });
196 int data_width = data.size() / npoints;
197 if (data.size() % npoints != 0)
199 throw std::runtime_error(
200 "Data size mismatch with number of local vertices/cells");
202 spdlog::debug(
"Data vector width={}", data_width);
204 hid_t h5file = hdf5::open_file(mesh.comm(), filename,
"a",
true);
205 hdf5::add_group(h5file,
"VTKHDF/Steps");
206 hid_t vtk_group = H5Gopen(h5file,
"VTKHDF/Steps", H5P_DEFAULT);
208 std::int64_t point_data_offset = 0;
209 if (htri_t attr_exists = H5Aexists(vtk_group,
"NSteps"); attr_exists < 0)
210 throw std::runtime_error(
"Error checking attribute");
211 else if (attr_exists == 0)
212 hdf5::set_attribute(vtk_group,
"NSteps", 1);
216 std::int32_t nsteps = 0;
217 hid_t attr_id = H5Aopen(vtk_group,
"NSteps", H5P_DEFAULT);
218 H5Aread(attr_id, H5T_NATIVE_INT32, &nsteps);
220 H5Awrite(attr_id, H5T_NATIVE_INT32, &nsteps);
223 std::vector<std::int64_t> data_shape
224 = hdf5::get_dataset_shape(h5file, dataset_name);
225 assert(data_shape.size() == 2);
226 point_data_offset = data_shape[0];
232 = [&h5file]<
typename T>(
const std::string& dset_name, T value)
235 if (hdf5::has_dataset(h5file, dset_name))
237 std::vector<std::int64_t> shape
238 = hdf5::get_dataset_shape(h5file, dset_name);
239 assert(shape.size() == 1);
242 hdf5::write_dataset(h5file, dset_name, &value, {s, s + 1}, {s + 1},
true,
247 append_dataset(
"/VTKHDF/Steps/CellOffsets", 0);
248 append_dataset(
"/VTKHDF/Steps/ConnectivityIdOffsets", 0);
249 append_dataset(
"/VTKHDF/Steps/NumberOfParts", 1);
250 append_dataset(
"/VTKHDF/Steps/PartOffsets", 0);
251 append_dataset(
"/VTKHDF/Steps/PointOffsets", 0);
254 hdf5::add_group(h5file, std::format(
"/VTKHDF/Steps/{}DataOffsets", poc));
255 append_dataset(std::format(
"/VTKHDF/Steps/{}DataOffsets/u", poc),
260 append_dataset(
"/VTKHDF/Steps/Values", time);
262 std::string group_name = std::format(
"/VTKHDF/{}Data", poc);
263 hdf5::add_group(h5file, group_name);
267 std::int64_t range0 = std::accumulate(index_maps.begin(), index_maps.end(), 0,
269 { return a + im->local_range()[0]; });
270 std::array<std::int64_t, 2> range{range0, range0 + npoints};
272 std::int64_t global_size = std::accumulate(
273 index_maps.begin(), index_maps.end(), 0,
274 [](std::int64_t a,
auto im) { return a + im->size_global(); });
276 std::vector<std::int64_t> shape0 = {global_size, data_width};
277 if (hdf5::has_dataset(h5file, dataset_name))
279 std::vector<std::int64_t> shape
280 = hdf5::get_dataset_shape(h5file, dataset_name);
281 assert(shape.size() == 2);
282 std::int64_t offset = shape[0];
286 hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0,
true,
291 hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0,
true,
295 hid_t dset_id = hdf5::open_dataset(h5file, dataset_name);
296 hdf5::set_attribute(dset_id,
"NumberOfComponents", data_width);
298 hid_t vtk_group = H5Gopen(h5file, group_name.c_str(), H5P_DEFAULT);
299 hdf5::set_attribute(vtk_group,
"Vectors",
"u");
304 hdf5::close_file(h5file);
318template <std::
floating_po
int U>
319mesh::Mesh<U> read_mesh(MPI_Comm comm,
const std::filesystem::path& filename,
320 std::size_t gdim = 3,
321 std::optional<std::int32_t> max_facet_to_cell_links = 2)
323 hid_t h5file = hdf5::open_file(comm, filename,
"r",
true);
325 std::vector<std::int64_t> shape
326 = hdf5::get_dataset_shape(h5file,
"/VTKHDF/Types");
329 std::array<std::int64_t, 2> local_cell_range
332 hid_t dset_id = hdf5::open_dataset(h5file,
"/VTKHDF/Types");
333 std::vector<std::uint8_t> types
334 = hdf5::read_dataset<std::uint8_t>(dset_id, local_cell_range,
true);
338 dset_id = hdf5::open_dataset(h5file,
"/VTKHDF/Offsets");
339 std::vector<std::int64_t> offsets = hdf5::read_dataset<std::int64_t>(
340 dset_id, {local_cell_range[0], local_cell_range[1] + 1},
true);
344 std::vector<std::array<std::uint8_t, 2>> types_unique;
345 std::vector<std::uint8_t> cell_degrees;
346 for (std::size_t i = 0; i < types.size(); ++i)
348 std::int64_t num_nodes = offsets[i + 1] - offsets[i];
355 : (std::uint8_t)degree;
357 cell_degrees.push_back(cell_degree);
360 std::ranges::sort(types_unique);
361 auto [unique_end, range_end] = std::ranges::unique(types_unique);
362 types_unique.erase(unique_end, range_end);
369 int count = 2 * types_unique.size();
370 std::vector<std::int32_t> recv_count(mpi_size);
371 MPI_Allgather(&count, 1, MPI_INT32_T, recv_count.data(), 1, MPI_INT32_T,
373 std::vector<std::int32_t> recv_offsets(mpi_size + 1, 0);
374 std::partial_sum(recv_count.begin(), recv_count.end(),
375 recv_offsets.begin() + 1);
377 std::vector<std::array<std::uint8_t, 2>> recv_types;
379 std::vector<std::uint8_t> send_types;
380 for (std::array<std::uint8_t, 2> t : types_unique)
381 send_types.insert(send_types.end(), t.begin(), t.end());
383 std::vector<std::uint8_t> recv_types_buffer(recv_offsets.back());
384 MPI_Allgatherv(send_types.data(), send_types.size(), MPI_UINT8_T,
385 recv_types_buffer.data(), recv_count.data(),
386 recv_offsets.data(), MPI_UINT8_T, comm);
388 for (std::size_t i = 0; i < recv_types_buffer.size(); i += 2)
389 recv_types.push_back({recv_types_buffer[i], recv_types_buffer[i + 1]});
391 std::ranges::sort(recv_types);
392 auto [unique_end, range_end] = std::ranges::unique(recv_types);
393 recv_types.erase(unique_end, range_end);
397 std::map<std::array<std::uint8_t, 2>, std::int32_t> type_to_index;
398 std::vector<mesh::CellType> dolfinx_cell_type;
399 std::vector<std::uint8_t> dolfinx_cell_degree;
400 for (std::array<std::uint8_t, 2> ct : recv_types)
403 type_to_index.insert({ct, dolfinx_cell_degree.size()});
404 dolfinx_cell_degree.push_back(ct[1]);
405 dolfinx_cell_type.push_back(cell_type);
408 dset_id = hdf5::open_dataset(h5file,
"/VTKHDF/NumberOfPoints");
409 std::vector npoints = hdf5::read_dataset<std::int64_t>(dset_id, {0, 1},
true);
411 spdlog::info(
"Mesh with {} points", npoints[0]);
412 std::array<std::int64_t, 2> local_point_range
415 std::vector<std::int64_t> x_shape
416 = hdf5::get_dataset_shape(h5file,
"/VTKHDF/Points");
417 dset_id = hdf5::open_dataset(h5file,
"/VTKHDF/Points");
418 std::vector<U> points_local
419 = hdf5::read_dataset<U>(dset_id, local_point_range,
true);
425 throw std::runtime_error(
"Geometric dimension must be less than or equal "
428 std::vector<U> points_pruned((local_point_range[1] - local_point_range[0])
430 for (std::int64_t i = 0; i < local_point_range[1] - local_point_range[0]; ++i)
432 std::copy_n(points_local.begin() + i * 3, gdim,
433 points_pruned.begin() + i * gdim);
436 dset_id = hdf5::open_dataset(h5file,
"/VTKHDF/Connectivity");
437 std::vector<std::int64_t> topology = hdf5::read_dataset<std::int64_t>(
438 dset_id, {offsets.front(), offsets.back()},
true);
440 std::transform(offsets.cbegin(), offsets.cend(), offsets.begin(),
441 [offset = offsets.front()](
auto x) { return x - offset; });
442 hdf5::close_file(h5file);
445 std::vector<std::vector<std::int64_t>> cells_local(recv_types.size());
446 for (std::size_t j = 0; j < types.size(); ++j)
448 std::int32_t type_index = type_to_index.at({types[j], cell_degrees[j]});
450 std::vector<std::uint16_t> perm
452 for (std::int64_t k = 0; k < offsets[j + 1] - offsets[j]; ++k)
453 cells_local[type_index].push_back(topology[perm[k] + offsets[j]]);
457 std::vector<fem::CoordinateElement<U>> coordinate_elements;
459 dolfinx_cell_type.cbegin(), dolfinx_cell_type.cend(),
460 dolfinx_cell_degree.cbegin(), std::back_inserter(coordinate_elements),
461 [](
auto cell_type,
auto cell_degree)
463 basix::element::lagrange_variant variant
464 = (cell_degree > 2) ? basix::element::lagrange_variant::equispaced
465 : basix::element::lagrange_variant::unset;
466 return fem::CoordinateElement<U>(cell_type, cell_degree, variant);
471 max_facet_to_cell_links);
472 std::vector<std::span<const std::int64_t>> cells_span(cells_local.begin(),
475 points_pruned, {(std::size_t)x_shape[0], gdim}, part,
476 max_facet_to_cell_links, 1);
Functions supporting mesh operations.
int size(MPI_Comm comm)
Definition MPI.cpp:72
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
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
AdjacencyList< std::int32_t > partition_graph(MPI_Comm comm, int nparts, const AdjacencyList< std::int64_t > &local_graph, bool ghosting)
Partition graph across processes using the default graph partitioner.
Definition partition.cpp:22
std::tuple< mesh::CellType, std::int8_t > vtk_to_dolfinx(std::int8_t vtk_cell_type)
Get DOLFINx cell type and degree from VTK cell type.
Definition cells.h:186
std::int8_t get_vtk_cell_type(mesh::CellType cell, int dim)
Get VTK cell identifier.
Definition cells.cpp:717
std::vector< std::uint16_t > perm_vtk(mesh::CellType type, int num_nodes)
Permutation array to map from VTK to DOLFINx node ordering.
Definition cells.cpp:534
std::vector< std::uint16_t > transpose(std::span< const std::uint16_t > map)
Compute the transpose of a re-ordering map.
Definition cells.cpp:688
int cell_degree(mesh::CellType type, int num_nodes)
Get the Lagrange order of a given cell with a given number of nodes.
Definition cells.cpp:610
Mesh< typename std::remove_reference_t< typename U::value_type > > create_mesh(MPI_Comm comm, MPI_Comm commt, std::vector< std::span< const std::int64_t > > cells, const std::vector< fem::CoordinateElement< typename std::remove_reference_t< typename U::value_type > > > &elements, MPI_Comm commg, const U &x, std::array< std::size_t, 2 > xshape, const CellPartitionFunction &partitioner, std::optional< std::int32_t > max_facet_to_cell_links, int num_threads, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:1050
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode, const graph::partition_fn &partfn, std::optional< std::int32_t > max_facet_to_cell_links)
Create a function that computes destination rank for mesh cells on this rank by applying the default ...
Definition utils.cpp:101
CellType
Cell type identifier.
Definition cell_types.h:22