DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
VTKHDF.h
1// Copyright (C) 2024-2025 Chris Richardson, Jørgen S. Dokken
2// Copyright (C) 2026 Garth N. Wells
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include "HDF5Interface.h"
11#include <algorithm>
12#include <array>
13#include <basix/cell.h>
14#include <basix/finite-element.h>
15#include <basix/mdspan.hpp>
16#include <cassert>
17#include <concepts>
18#include <cstddef>
19#include <cstdint>
20#include <dolfinx/common/IndexMap.h>
21#include <dolfinx/common/MPI.h>
22#include <dolfinx/common/log.h>
23#include <dolfinx/fem/CoordinateElement.h>
24#include <dolfinx/graph/partition.h>
25#include <dolfinx/io/cells.h>
26#include <dolfinx/mesh/Mesh.h>
27#include <dolfinx/mesh/Topology.h>
28#include <dolfinx/mesh/cell_types.h>
29#include <dolfinx/mesh/utils.h>
30#include <filesystem>
31#include <format>
32#include <iterator>
33#include <limits>
34#include <map>
35#include <memory>
36#include <numeric>
37#include <optional>
38#include <span>
39#include <stdexcept>
40#include <string>
41#include <string_view>
42#include <tuple>
43#include <utility>
44#include <vector>
45
46namespace dolfinx::io::VTKHDF
47{
48namespace impl
49{
50// Construct a nodal coordinate element for a (VTK type, degree) group.
51template <std::floating_point U>
52fem::CoordinateElement<U>
53coordinate_element(std::array<std::uint8_t, 2> type_degree)
54{
55 mesh::CellType cell_type = std::get<0>(cells::vtk_to_dolfinx(type_degree[0]));
56
57 // VTK_QUADRATIC_WEDGE and VTK_QUADRATIC_PYRAMID. Both are serendipity
58 // layouts (15 and 13 nodes) that Basix has no element for, and
59 // vtk_to_dolfinx reports degree 2 for them, so they would otherwise
60 // silently become 18- and 14-node Lagrange cells read from a 15- and
61 // 13-node connectivity. Every rank sees the same cell types, so this
62 // throws collectively.
63 if (type_degree[0] == 26 or type_degree[0] == 27)
64 {
65 throw std::runtime_error(std::format(
66 "VTK cell type {} is a serendipity layout that Basix cannot express.",
67 static_cast<int>(type_degree[0])));
68 }
69
70 // VTK_QUADRATIC_QUAD and VTK_QUADRATIC_HEXAHEDRON, the two quadratic
71 // serendipity layouts that Basix has an element for
72 if (type_degree[0] == 23 or type_degree[0] == 25)
73 {
74 basix::cell::type cell = mesh::cell_type_to_basix_type(cell_type);
75 basix::FiniteElement<U> element
76 = basix::create_element<U>(basix::element::family::serendipity, cell, 2,
77 basix::element::lagrange_variant::unset,
78 basix::element::dpc_variant::unset, false);
79 auto x = element.x();
80 auto M = element.M();
81 auto [geometry, geometry_shape] = basix::cell::geometry<U>(cell);
82 const std::vector<std::vector<int>> edges = basix::cell::topology(cell)[1];
83
84 // VTK stores edge midpoint values, not Basix's edge moments.
85 for (std::size_t i = 0; i < edges.size(); ++i)
86 {
87 std::vector<U> midpoint(geometry_shape[1]);
88 for (std::size_t j = 0; j < geometry_shape[1]; ++j)
89 {
90 midpoint[j] = (geometry[edges[i][0] * geometry_shape[1] + j]
91 + geometry[edges[i][1] * geometry_shape[1] + j])
92 / 2;
93 }
94 x[1][i] = {std::move(midpoint), {1, geometry_shape[1]}};
95 M[1][i] = {{1}, {1, 1, 1, 1}};
96 }
97 std::array<std::vector<basix::impl::mdspan_t<const U, 2>>, 4> x_view;
98 std::array<std::vector<basix::impl::mdspan_t<const U, 4>>, 4> M_view;
99 for (std::size_t d = 0; d < 4; ++d)
100 {
101 for (const auto& [data, shape] : x[d])
102 x_view[d].emplace_back(data.data(), shape);
103 for (const auto& [data, shape] : M[d])
104 M_view[d].emplace_back(data.data(), shape);
105 }
106 const auto& [wcoeffs, wshape] = element.wcoeffs();
107 return fem::CoordinateElement<U>(std::make_shared<basix::FiniteElement<U>>(
108 basix::create_custom_element<U>(
109 cell, {}, {wcoeffs.data(), wshape}, x_view, M_view, 0,
110 element.map_type(), element.sobolev_space(), false,
111 element.embedded_subdegree(), element.embedded_superdegree(),
112 element.polyset_type())));
113 }
114 int cell_degree = type_degree[1];
115 basix::element::lagrange_variant variant
116 = (cell_degree > 2) ? basix::element::lagrange_variant::equispaced
117 : basix::element::lagrange_variant::unset;
118 return fem::CoordinateElement<U>(cell_type, cell_degree, variant);
119}
120} // namespace impl
121
129template <std::floating_point U>
130void write_mesh(const std::filesystem::path& filename,
131 const mesh::Mesh<U>& mesh)
132{
133 const int tdim = mesh.topology()->dim();
134 std::vector<mesh::CellType> cell_types = mesh.topology()->entity_types(tdim);
135
136 // Mapped before the file is opened so that an unsupported cell layout
137 // throws on every rank before any collective IO has happened
138 std::vector<std::int8_t> vtk_types;
139 vtk_types.reserve(cell_types.size());
140 for (std::size_t i = 0; i < cell_types.size(); ++i)
141 {
142 vtk_types.push_back(cells::get_vtk_cell_type(
143 cell_types[i], mesh.geometry().cmaps().at(i).dim()));
144 }
145
146 hdf5::Handle h5file(hdf5::open_file(mesh.comm(), filename, "w", true),
147 H5Fclose);
148
149 // Create VTKHDF group
150 hdf5::add_group(h5file, "VTKHDF");
151 {
152 hdf5::Handle vtk_group(H5Gopen(h5file, "VTKHDF", H5P_DEFAULT), H5Gclose);
153 if (vtk_group < 0)
154 throw std::runtime_error("Failed to open HDF5 group \"VTKHDF\".");
155 hdf5::set_attribute(vtk_group, "Version", std::vector{2, 2});
156 hdf5::set_attribute(vtk_group, "Type", "UnstructuredGrid");
157 }
158
159 // Extract topology information for each cell type
160 std::vector cell_index_maps = mesh.topology()->index_maps(tdim);
161 std::vector<std::int32_t> num_cells;
162 std::vector<std::int64_t> num_cells_global;
163 for (auto& im : cell_index_maps)
164 {
165 num_cells.push_back(im->size_local());
166 num_cells_global.push_back(im->size_global());
167 }
168
169 // Geometry dofmap and points
170 std::shared_ptr<const common::IndexMap> geom_imap
171 = mesh.geometry().index_map();
172 std::int64_t size_global = geom_imap->size_global();
173 std::vector<std::int64_t> geom_global_shape = {size_global, 3};
174 std::array<std::int64_t, 2> geom_irange = geom_imap->local_range();
175 hdf5::write_dataset(h5file, "/VTKHDF/Points", mesh.geometry().x().data(),
176 geom_irange, geom_global_shape, true, false);
177 hdf5::write_dataset(h5file, "VTKHDF/NumberOfPoints", &size_global, {0, 1},
178 {1}, true, false);
179
180 // Note: VTKHDF stores the cells as an adjacency list, where cell
181 // types might be jumbled up
182 std::vector<std::int64_t> topology_flattened;
183 std::vector<std::int64_t> topology_offsets;
184 std::vector<std::uint8_t> vtkcelltypes;
185 for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
186 {
187 md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> g_dofmap
188 = mesh.geometry().dofmaps().at(i);
189
190 std::vector<std::uint16_t> perm
191 = cells::perm_vtk(cell_types[i], g_dofmap.extent(1));
192 std::vector<std::uint16_t> inverse_perm = cells::transpose(perm);
193 std::vector<std::int32_t> local_dm;
194 local_dm.reserve(g_dofmap.extent(1) * num_cells[i]);
195 for (int j = 0; j < num_cells[i]; ++j)
196 for (std::size_t k = 0; k < g_dofmap.extent(1); ++k)
197 local_dm.push_back(g_dofmap(j, inverse_perm[k]));
198
199 std::vector<std::int64_t> global_dm(local_dm.size());
200 geom_imap->local_to_global(local_dm, global_dm);
201
202 topology_flattened.insert(topology_flattened.end(), global_dm.begin(),
203 global_dm.end());
204 topology_offsets.insert(topology_offsets.end(), g_dofmap.extent(0),
205 g_dofmap.extent(1));
206 vtkcelltypes.insert(vtkcelltypes.end(), cell_index_maps[i]->size_local(),
207 vtk_types[i]);
208 }
209
210 // Create topo_offsets
211 std::partial_sum(topology_offsets.cbegin(), topology_offsets.cend(),
212 topology_offsets.begin());
213
214 std::vector<int> num_nodes_per_cell;
215 std::vector<std::int64_t> cell_start_pos;
216 std::vector<std::int64_t> cell_stop_pos;
217 for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
218 {
219 num_nodes_per_cell.push_back(mesh.geometry().cmaps().at(i).dim());
220 std::array<std::int64_t, 2> r = cell_index_maps[i]->local_range();
221 cell_start_pos.push_back(r[0]);
222 cell_stop_pos.push_back(r[1]);
223 }
224
225 // Compute overall cell offset from offsets for each cell type
226 std::int64_t offset_start_position = std::accumulate(
227 cell_start_pos.begin(), cell_start_pos.end(), std::int64_t(0));
228 std::int64_t offset_stop_position = std::accumulate(
229 cell_stop_pos.begin(), cell_stop_pos.end(), std::int64_t(0));
230
231 // Compute overall topology offset from offsets for each cell type
232 std::int64_t topology_start
233 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
234 cell_start_pos.begin(), std::int64_t(0));
235
236 std::ranges::transform(topology_offsets, topology_offsets.begin(),
237 [topology_start](std::int64_t x)
238 { return x + topology_start; });
239
240 std::int64_t num_all_cells_global = std::accumulate(
241 num_cells_global.begin(), num_cells_global.end(), std::int64_t(0));
242 hdf5::write_dataset(h5file, "/VTKHDF/Offsets", topology_offsets.data(),
243 {offset_start_position + 1, offset_stop_position + 1},
244 {num_all_cells_global + 1}, true, false);
245
246 // Store global mesh connectivity
247 std::int64_t topology_size_global
248 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
249 num_cells_global.begin(), std::int64_t(0));
250
251 std::int64_t topology_stop = topology_start + topology_flattened.size();
252 hdf5::write_dataset(h5file, "/VTKHDF/Connectivity", topology_flattened.data(),
253 {topology_start, topology_stop}, {topology_size_global},
254 true, false);
255
256 // Store cell types
257 hdf5::write_dataset(h5file, "/VTKHDF/Types", vtkcelltypes.data(),
258 {offset_start_position, offset_stop_position},
259 {num_all_cells_global}, true, false);
260 hdf5::write_dataset(h5file, "/VTKHDF/NumberOfConnectivityIds",
261 &topology_size_global, {0, 1}, {1}, true, false);
262 hdf5::write_dataset(h5file, "/VTKHDF/NumberOfCells", &num_all_cells_global,
263 {0, 1}, {1}, true, false);
264 h5file.close();
265}
266
288template <std::floating_point U>
289void write_data(std::string_view point_or_cell,
290 const std::filesystem::path& filename,
291 const mesh::Mesh<U>& mesh, const std::vector<U>& data,
292 double time)
293{
294 std::vector<std::shared_ptr<const common::IndexMap>> index_maps;
295 if (point_or_cell == "Point")
296 index_maps = {mesh.geometry().index_map()};
297 else if (point_or_cell == "Cell")
298 index_maps = mesh.topology()->index_maps(mesh.topology()->dim());
299 else
300 throw std::invalid_argument("Selection must be Point or Cell.");
301
302 const std::string poc(point_or_cell);
303 std::string dataset_name = std::format("/VTKHDF/{}Data/u", poc);
304 std::int32_t npoints = std::accumulate(
305 index_maps.begin(), index_maps.end(), std::int32_t(0),
306 [](std::int32_t a, auto& im) { return a + im->size_local(); });
307 if (npoints == 0 and !data.empty())
308 {
309 throw std::invalid_argument(
310 "Data supplied on a process with no local vertices/cells.");
311 }
312 if (npoints > 0 and data.size() % npoints != 0)
313 {
314 throw std::invalid_argument(
315 "Data size mismatch with number of local vertices/cells.");
316 }
317
318 // A process with no local entities cannot determine the data width
319 // from its (empty) data, but every process must build the same global
320 // dataset shape. Take the width from the processes that do have data,
321 // reducing (max width, -min width) so that an inconsistent width is
322 // detected identically on all processes.
323 const std::int32_t width_local = npoints == 0 ? 0 : data.size() / npoints;
324 std::array<std::int32_t, 2> width_local_pair{
325 width_local,
326 npoints == 0 ? std::numeric_limits<std::int32_t>::min() : -width_local};
327 std::array<std::int32_t, 2> width{0, 0};
328 MPI_Allreduce(width_local_pair.data(), width.data(), 2, MPI_INT32_T, MPI_MAX,
329 mesh.comm());
330 const std::int32_t data_width = width[0];
331 if (data_width == 0 or data_width != -width[1])
332 {
333 throw std::runtime_error(
334 "Data width is zero, or differs between processes.");
335 }
336 logger().debug("Data vector width={}", data_width);
337
338 hdf5::Handle h5file(hdf5::open_file(mesh.comm(), filename, "a", true),
339 H5Fclose);
340 hdf5::add_group(h5file, "VTKHDF/Steps");
341
342 std::int64_t point_data_offset = 0;
343 {
344 hdf5::Handle vtk_group(H5Gopen(h5file, "VTKHDF/Steps", H5P_DEFAULT),
345 H5Gclose);
346 if (vtk_group < 0)
347 throw std::runtime_error("Failed to open HDF5 group \"VTKHDF/Steps\".");
348
349 if (htri_t attr_exists = H5Aexists(vtk_group, "NSteps"); attr_exists < 0)
350 throw std::runtime_error("Error checking attribute");
351 else if (attr_exists == 0)
352 hdf5::set_attribute(vtk_group, "NSteps", 1);
353 else
354 {
355 // Read and increment attribute
356 std::int32_t nsteps = 0;
357 {
358 hdf5::Handle attr_id(H5Aopen(vtk_group, "NSteps", H5P_DEFAULT),
359 H5Aclose);
360 if (attr_id < 0)
361 throw std::runtime_error("Failed to open HDF5 attribute \"NSteps\".");
362 if (H5Aread(attr_id, H5T_NATIVE_INT32, &nsteps) < 0)
363 throw std::runtime_error("Failed to read HDF5 attribute \"NSteps\".");
364 }
365 hdf5::set_attribute(vtk_group, "NSteps", nsteps + 1);
366
367 std::vector<std::int64_t> data_shape
368 = hdf5::get_dataset_shape(h5file, dataset_name);
369 assert(data_shape.size() == 2);
370 point_data_offset = data_shape[0];
371 }
372 }
373
374 // Add a single value to end of a 1D dataset
375 auto append_dataset
376 = [&h5file]<typename T>(const std::string& dset_name, T value)
377 {
378 std::int32_t s = 0;
379 if (hdf5::has_dataset(h5file, dset_name))
380 {
381 std::vector<std::int64_t> shape
382 = hdf5::get_dataset_shape(h5file, dset_name);
383 assert(shape.size() == 1);
384 s = shape[0];
385 }
386 hdf5::write_dataset(h5file, dset_name, &value, {s, s + 1}, {s + 1}, true,
387 true);
388 };
389
390 // Mesh remains the same, so these values are the same for each time step
391 append_dataset("/VTKHDF/Steps/CellOffsets", 0);
392 append_dataset("/VTKHDF/Steps/ConnectivityIdOffsets", 0);
393 append_dataset("/VTKHDF/Steps/NumberOfParts", 1);
394 append_dataset("/VTKHDF/Steps/PartOffsets", 0);
395 append_dataset("/VTKHDF/Steps/PointOffsets", 0);
396
397 // Add the current data size to the end of the offset array
398 hdf5::add_group(h5file, std::format("/VTKHDF/Steps/{}DataOffsets", poc));
399 append_dataset(std::format("/VTKHDF/Steps/{}DataOffsets/u", poc),
400 point_data_offset);
401
402 // Time values
403 // FIXME: check these are increasing?
404 append_dataset("/VTKHDF/Steps/Values", time);
405
406 std::string group_name = std::format("/VTKHDF/{}Data", poc);
407 hdf5::add_group(h5file, group_name);
408
409 // Add point/cell data into dataset, extending each time by
410 // global_size with each process writing its own part.
411 std::int64_t range0 = std::accumulate(
412 index_maps.begin(), index_maps.end(), std::int64_t(0),
413 [](std::int64_t a, auto& im) { return a + im->local_range()[0]; });
414 std::array<std::int64_t, 2> range{range0, range0 + npoints};
415
416 std::int64_t global_size = std::accumulate(
417 index_maps.begin(), index_maps.end(), std::int64_t(0),
418 [](std::int64_t a, auto& im) { return a + im->size_global(); });
419
420 std::vector<std::int64_t> shape0 = {global_size, data_width};
421 if (hdf5::has_dataset(h5file, dataset_name))
422 {
423 std::vector<std::int64_t> shape
424 = hdf5::get_dataset_shape(h5file, dataset_name);
425 assert(shape.size() == 2);
426 std::int64_t offset = shape[0];
427 range[0] += offset;
428 range[1] += offset;
429 shape0[0] += offset;
430 hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0, true,
431 true);
432 }
433 else
434 {
435 hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0, true,
436 true);
437 if (data_width > 1)
438 {
439 hdf5::Handle dset_id(hdf5::open_dataset(h5file, dataset_name), H5Dclose);
440 hdf5::set_attribute(dset_id, "NumberOfComponents", data_width);
441 hdf5::Handle vec_group(H5Gopen(h5file, group_name.c_str(), H5P_DEFAULT),
442 H5Gclose);
443 if (vec_group < 0)
444 throw std::runtime_error("Failed to open HDF5 group for vector data.");
445 hdf5::set_attribute(vec_group, "Vectors", "u");
446 }
447 }
448
449 h5file.close();
450}
451
465template <std::floating_point U>
466mesh::Mesh<U> read_mesh(MPI_Comm comm, const std::filesystem::path& filename,
467 std::size_t gdim = 3,
468 std::optional<std::int32_t> max_facet_to_cell_links = 2,
469 int num_threads = 1)
470{
471 if (gdim > 3)
472 {
473 throw std::invalid_argument(
474 "Geometric dimension must be less than or equal to 3.");
475 }
476
477 hdf5::Handle h5file(hdf5::open_file(comm, filename, "r", true), H5Fclose);
478
479 std::vector<std::int64_t> shape
480 = hdf5::get_dataset_shape(h5file, "/VTKHDF/Types");
481 int rank = dolfinx::MPI::rank(comm);
482 int mpi_size = dolfinx::MPI::size(comm);
483 std::array<std::int64_t, 2> local_cell_range
484 = common::local_range(rank, shape[0], mpi_size);
485
486 std::vector<std::uint8_t> types = hdf5::read_dataset<std::uint8_t>(
487 h5file, "/VTKHDF/Types", local_cell_range, true);
488
489 // Read in offsets to determine the different cell-types in the mesh
490 std::vector<std::int64_t> offsets = hdf5::read_dataset<std::int64_t>(
491 h5file, "/VTKHDF/Offsets", {local_cell_range[0], local_cell_range[1] + 1},
492 true);
493
494 // The VTK type identifies the family; degree distinguishes Lagrange layouts.
495 std::vector<std::array<std::uint8_t, 2>> types_unique;
496 std::vector<std::uint8_t> cell_degrees;
497 for (std::size_t i = 0; i < types.size(); ++i)
498 {
499 std::int64_t num_nodes = offsets[i + 1] - offsets[i];
500 auto [cell_type, degree] = io::cells::vtk_to_dolfinx(types[i]);
501 // If arbitrary order Lagrange VTK cell (indicated by -1), determine degree
502 // from number of nodes
503
504 std::uint8_t cell_degree
505 = degree == -1 ? io::cells::cell_degree(cell_type, num_nodes)
506 : (std::uint8_t)degree;
507 types_unique.push_back({types[i], cell_degree});
508 cell_degrees.push_back(cell_degree);
509 }
510 {
511 std::ranges::sort(types_unique);
512 auto [unique_end, range_end] = std::ranges::unique(types_unique);
513 types_unique.erase(unique_end, range_end);
514 }
515
516 // Share cell types with all processes to make global list of cell
517 // types
518 // FIXME: amount of data is small, but number of connections does not
519 // scale
520 int count = 2 * types_unique.size();
521 std::vector<std::int32_t> recv_count(mpi_size);
522 MPI_Allgather(&count, 1, MPI_INT32_T, recv_count.data(), 1, MPI_INT32_T,
523 comm);
524 std::vector<std::int32_t> recv_offsets(mpi_size + 1, 0);
525 std::partial_sum(recv_count.begin(), recv_count.end(),
526 recv_offsets.begin() + 1);
527
528 std::vector<std::array<std::uint8_t, 2>> recv_types;
529 {
530 std::vector<std::uint8_t> send_types;
531 for (std::array<std::uint8_t, 2> t : types_unique)
532 send_types.insert(send_types.end(), t.begin(), t.end());
533
534 std::vector<std::uint8_t> recv_types_buffer(recv_offsets.back());
535 MPI_Allgatherv(send_types.data(), send_types.size(), MPI_UINT8_T,
536 recv_types_buffer.data(), recv_count.data(),
537 recv_offsets.data(), MPI_UINT8_T, comm);
538
539 for (std::size_t i = 0; i < recv_types_buffer.size(); i += 2)
540 recv_types.push_back({recv_types_buffer[i], recv_types_buffer[i + 1]});
541
542 std::ranges::sort(recv_types);
543 auto [unique_end, range_end] = std::ranges::unique(recv_types);
544 recv_types.erase(unique_end, range_end);
545 }
546
547 // Map (VTK type, degree) to coordinate element index.
548 std::map<std::array<std::uint8_t, 2>, std::int32_t> type_to_index;
549 std::vector<mesh::CellType> dolfinx_cell_type;
550 for (std::array<std::uint8_t, 2> ct : recv_types)
551 {
552 mesh::CellType cell_type = std::get<0>(io::cells::vtk_to_dolfinx(ct[0]));
553 type_to_index.insert({ct, dolfinx_cell_type.size()});
554 dolfinx_cell_type.push_back(cell_type);
555 }
556
557 std::vector npoints = hdf5::read_dataset<std::int64_t>(
558 h5file, "/VTKHDF/NumberOfPoints", {0, 1}, true);
559 logger().info("Mesh with {} points", npoints[0]);
560 std::array<std::int64_t, 2> local_point_range
561 = common::local_range(rank, npoints[0], mpi_size);
562
563 std::vector<std::int64_t> x_shape
564 = hdf5::get_dataset_shape(h5file, "/VTKHDF/Points");
565 std::vector<U> points_local = hdf5::read_dataset<U>(h5file, "/VTKHDF/Points",
566 local_point_range, true);
567
568 // Remove coordinates if gdim != 3
569 std::vector<U> points_pruned((local_point_range[1] - local_point_range[0])
570 * gdim);
571 for (std::int64_t i = 0; i < local_point_range[1] - local_point_range[0]; ++i)
572 {
573 std::copy_n(points_local.begin() + i * 3, gdim,
574 points_pruned.begin() + i * gdim);
575 }
576
577 std::vector<std::int64_t> topology = hdf5::read_dataset<std::int64_t>(
578 h5file, "/VTKHDF/Connectivity", {offsets.front(), offsets.back()}, true);
579 std::ranges::transform(offsets, offsets.begin(),
580 [offset = offsets.front()](std::int64_t x)
581 { return x - offset; });
582 h5file.close();
583
584 // Create cell topologies for each cell type.
585 std::vector<std::vector<std::int64_t>> cells_local(recv_types.size());
586 {
587 std::vector<std::size_t> num_nodes_per_type(recv_types.size(), 0);
588 for (std::size_t j = 0; j < types.size(); ++j)
589 {
590 std::int32_t type_index = type_to_index.at({types[j], cell_degrees[j]});
591 num_nodes_per_type[type_index] += offsets[j + 1] - offsets[j];
592 }
593 for (std::size_t t = 0; t < cells_local.size(); ++t)
594 cells_local[t].reserve(num_nodes_per_type[t]);
595 }
596 for (std::size_t j = 0; j < types.size(); ++j)
597 {
598 std::int32_t type_index = type_to_index.at({types[j], cell_degrees[j]});
599 mesh::CellType cell_type = dolfinx_cell_type[type_index];
600 std::vector<std::uint16_t> perm
601 = cells::perm_vtk(cell_type, offsets[j + 1] - offsets[j]);
602 for (std::int64_t k = 0; k < offsets[j + 1] - offsets[j]; ++k)
603 cells_local[type_index].push_back(topology[perm[k] + offsets[j]]);
604 }
605
606 // Make coordinate elements
607 std::vector<fem::CoordinateElement<U>> coordinate_elements;
608 std::ranges::transform(recv_types, std::back_inserter(coordinate_elements),
609 impl::coordinate_element<U>);
610
611 std::vector<std::span<const std::int64_t>> cells_span(cells_local.begin(),
612 cells_local.end());
613 return mesh::create_mesh(comm, comm, cells_span, coordinate_elements, comm,
614 points_pruned, {(std::size_t)x_shape[0], gdim},
615 graph::Partitioner{}, mesh::GhostMode::none,
616 max_facet_to_cell_links, num_threads);
617}
618} // namespace dolfinx::io::VTKHDF
Functions supporting mesh operations.
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
@ cell
Cell.
Definition Form.h:44
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:213
std::int8_t get_vtk_cell_type(mesh::CellType cell, int num_nodes)
Get the VTK cell identifier for a cell type and node layout.
Definition cells.cpp:749
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:556
std::vector< std::uint16_t > transpose(std::span< const std::uint16_t > map)
Compute the transpose of a re-ordering map.
Definition cells.cpp:720
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:632
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 graph::Partitioner &partitioner, GhostMode ghost_mode, std::optional< std::int32_t > max_facet_to_cell_links, int num_threads, const graph::Reorder &reorder_fn=graph::Reorder{})
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:1402
CellType
Cell type identifier.
Definition cell_types.h:24
basix::cell::type cell_type_to_basix_type(CellType celltype)
Convert a cell type to a Basix cell type.
Definition cell_types.cpp:171
auto logger() -> spdlog::logger &
Dolfinx's logger.
Definition log.cpp:24