DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
VTKHDF.h
1// Copyright (C) 2024 Chris Richardson
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#include "HDF5Interface.h"
8
9#include <dolfinx/common/IndexMap.h>
10#include <dolfinx/io/cells.h>
11#include <dolfinx/mesh/Mesh.h>
12#include <dolfinx/mesh/Topology.h>
13#include <dolfinx/mesh/utils.h>
14
15#include <map>
16#include <vector>
17
18namespace dolfinx::io::VTKHDF
19{
20
25template <typename U>
26void write_mesh(std::string filename, const mesh::Mesh<U>& mesh);
27
33template <typename U>
34mesh::Mesh<U> read_mesh(MPI_Comm comm, std::string filename);
35
36} // namespace dolfinx::io::VTKHDF
37
38using namespace dolfinx;
39
40template <typename U>
41void io::VTKHDF::write_mesh(std::string filename, const mesh::Mesh<U>& mesh)
42{
43 hid_t h5file = io::hdf5::open_file(mesh.comm(), filename, "w", true);
44
45 // Create VTKHDF group
46 io::hdf5::add_group(h5file, "VTKHDF");
47
48 hid_t vtk_group = H5Gopen(h5file, "VTKHDF", H5P_DEFAULT);
49
50 // Create "Version" attribute
51 hsize_t dims = 2;
52 hid_t space_id = H5Screate_simple(1, &dims, NULL);
53 hid_t attr_id = H5Acreate(vtk_group, "Version", H5T_NATIVE_INT32, space_id,
54 H5P_DEFAULT, H5P_DEFAULT);
55 std::array<std::int32_t, 2> version = {2, 2};
56 H5Awrite(attr_id, H5T_NATIVE_INT32, version.data());
57 H5Aclose(attr_id);
58 H5Sclose(space_id);
59
60 // Create "Type" attribute
61 space_id = H5Screate(H5S_SCALAR);
62 hid_t atype = H5Tcopy(H5T_C_S1);
63 H5Tset_size(atype, 16);
64 H5Tset_strpad(atype, H5T_STR_NULLTERM);
65 attr_id
66 = H5Acreate(vtk_group, "Type", atype, space_id, H5P_DEFAULT, H5P_DEFAULT);
67 H5Awrite(attr_id, atype, "UnstructuredGrid");
68
69 H5Aclose(attr_id);
70 H5Sclose(space_id);
71 H5Gclose(vtk_group);
72
73 // Extract topology information for each cell type
74 auto cell_types = mesh.topology()->entity_types(mesh.topology()->dim());
75
76 std::vector cell_index_maps
77 = mesh.topology()->index_maps(mesh.topology()->dim());
78 std::vector<std::int32_t> num_cells;
79 std::vector<std::int64_t> num_cells_global;
80 for (auto im : cell_index_maps)
81 {
82 num_cells.push_back(im->size_local());
83 num_cells_global.push_back(im->size_global());
84 }
85
86 // Geometry dofmap and points
87 auto geom_imap = mesh.geometry().index_map();
88 std::int32_t gdim = mesh.geometry().dim();
89 std::int64_t size_global = geom_imap->size_global();
90 std::vector<std::int64_t> geom_global_shape = {size_global, gdim};
91 auto geom_irange = geom_imap->local_range();
92
93 io::hdf5::write_dataset(h5file, "/VTKHDF/Points", mesh.geometry().x().data(),
94 geom_irange, geom_global_shape, true, false);
95
96 io::hdf5::write_dataset(h5file, "VTKHDF/NumberOfPoints", &size_global, {0, 1},
97 {1}, true, false);
98
99 // VTKHDF stores the cells as an adjacency list, \
100 // where cell types might be jumbled up.
101 std::vector<std::int64_t> topology_flattened;
102 std::vector<std::int64_t> topology_offsets;
103 std::vector<std::uint8_t> vtkcelltypes;
104 for (int i = 0; i < cell_index_maps.size(); ++i)
105 {
106 auto g_dofmap = mesh.geometry().dofmap(i);
107
108 std::vector<std::int32_t> local_dm;
109 local_dm.reserve(g_dofmap.extent(1) * num_cells[i]);
110 auto perm = io::cells::perm_vtk(cell_types[i], g_dofmap.extent(1));
111 for (int j = 0; j < num_cells[i]; ++j)
112 for (int k = 0; k < g_dofmap.extent(1); ++k)
113 local_dm.push_back(g_dofmap(j, perm[k]));
114
115 std::vector<std::int64_t> global_dm(local_dm.size());
116 geom_imap->local_to_global(local_dm, global_dm);
117
118 topology_flattened.insert(topology_flattened.end(), global_dm.begin(),
119 global_dm.end());
120
121 topology_offsets.insert(topology_offsets.end(), g_dofmap.extent(0),
122 g_dofmap.extent(1));
123
124 vtkcelltypes.insert(
125 vtkcelltypes.end(), cell_index_maps[i]->size_local(),
126 io::cells::get_vtk_cell_type(cell_types[i], mesh.topology()->dim()));
127 }
128 // Create topo_offsets
129 std::partial_sum(topology_offsets.cbegin(), topology_offsets.cend(),
130 topology_offsets.begin());
131
132 std::vector<int> num_nodes_per_cell;
133 std::vector<std::int64_t> cell_start_pos;
134 std::vector<std::int64_t> cell_stop_pos;
135 for (int i = 0; i < cell_index_maps.size(); ++i)
136 {
137 num_nodes_per_cell.push_back(mesh.geometry().cmaps()[i].dim());
138 auto r = cell_index_maps[i]->local_range();
139 cell_start_pos.push_back(r[0]);
140 cell_stop_pos.push_back(r[1]);
141 }
142
143 // Compute overall cell offset from offsets for each cell type
144 std::int64_t offset_start_position
145 = std::accumulate(cell_start_pos.begin(), cell_start_pos.end(), 0);
146 std::int64_t offset_stop_position
147 = std::accumulate(cell_stop_pos.begin(), cell_stop_pos.end(), 0);
148
149 // Compute overall topology offset from offsets for each cell type
150 std::int64_t topology_start
151 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
152 cell_start_pos.begin(), 0);
153
154 std::for_each(topology_offsets.begin(), topology_offsets.end(),
155 [topology_start](std::int64_t& t) { t += topology_start; });
156
157 std::int64_t num_all_cells_global
158 = std::accumulate(num_cells_global.begin(), num_cells_global.end(), 0);
159 io::hdf5::write_dataset(h5file, "/VTKHDF/Offsets", topology_offsets.data(),
160 {offset_start_position + 1, offset_stop_position + 1},
161 {num_all_cells_global + 1}, true, false);
162
163 // Store global mesh connectivity
164 std::int64_t topology_size_global
165 = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
166 num_cells_global.begin(), 0);
167
168 std::int64_t topology_stop = topology_start + topology_flattened.size();
169 io::hdf5::write_dataset(
170 h5file, "/VTKHDF/Connectivity", topology_flattened.data(),
171 {topology_start, topology_stop}, {topology_size_global}, true, false);
172
173 // Store cell types
174 io::hdf5::write_dataset(h5file, "/VTKHDF/Types", vtkcelltypes.data(),
175 {offset_start_position, offset_stop_position},
176 {num_all_cells_global}, true, false);
177
178 io::hdf5::write_dataset(h5file, "/VTKHDF/NumberOfConnectivityIds",
179 &topology_size_global, {0, 1}, {1}, true, false);
180
181 io::hdf5::write_dataset(h5file, "/VTKHDF/NumberOfCells",
182 &num_all_cells_global, {0, 1}, {1}, true, false);
183
184 io::hdf5::close_file(h5file);
185}
186
187template <typename U>
188mesh::Mesh<U> io::VTKHDF::read_mesh(MPI_Comm comm, std::string filename)
189{
190 hid_t h5file = io::hdf5::open_file(comm, filename, "r", true);
191
192 std::vector<std::int64_t> shape
193 = io::hdf5::get_dataset_shape(h5file, "/VTKHDF/Types");
194 int rank = MPI::rank(comm);
195 int size = MPI::size(comm);
196 auto local_cell_range = dolfinx::MPI::local_range(rank, shape[0], size);
197
198 hid_t dset_id = io::hdf5::open_dataset(h5file, "/VTKHDF/Types");
199 std::vector<std::uint8_t> types
200 = io::hdf5::read_dataset<std::uint8_t>(dset_id, local_cell_range, true);
201 H5Dclose(dset_id);
202 std::vector<std::uint8_t> types_unique(types.begin(), types.end());
203 {
204 std::ranges::sort(types_unique);
205 auto [unique_end, range_end] = std::ranges::unique(types_unique);
206 types_unique.erase(unique_end, range_end);
207 }
208
209 // Share cell types with all processes to make global list of cell types
210 // FIXME: amount of data is small, but number of connections does not scale.
211 std::int32_t count = types_unique.size();
212 std::vector<std::int32_t> recv_count(size);
213 std::vector<std::int32_t> recv_offsets(size + 1, 0);
214 MPI_Allgather(&count, 1, MPI_INT32_T, recv_count.data(), 1, MPI_INT32_T,
215 comm);
216 std::partial_sum(recv_count.begin(), recv_count.end(),
217 recv_offsets.begin() + 1);
218 std::vector<std::uint8_t> recv_types(recv_offsets.back());
219 MPI_Allgatherv(types_unique.data(), count, MPI_UINT8_T, recv_types.data(),
220 recv_count.data(), recv_offsets.data(), MPI_UINT8_T, comm);
221 {
222 std::ranges::sort(recv_types);
223 auto [unique_end, range_end] = std::ranges::unique(recv_types);
224 recv_types.erase(unique_end, range_end);
225 }
226
227 // Create reverse map from VTK to dolfinx cell types
228 std::map<std::uint8_t, mesh::CellType> vtk_to_dolfinx;
229 const std::vector<mesh::CellType> dolfinx_cells
230 = {mesh::CellType::point, mesh::CellType::interval,
231 mesh::CellType::triangle, mesh::CellType::quadrilateral,
232 mesh::CellType::tetrahedron, mesh::CellType::prism,
233 mesh::CellType::pyramid, mesh::CellType::hexahedron};
234 for (auto dolfinx_type : dolfinx_cells)
235 {
236 vtk_to_dolfinx.insert({io::cells::get_vtk_cell_type(
237 dolfinx_type, mesh::cell_dim(dolfinx_type)),
238 dolfinx_type});
239 }
240
241 // Map from VTKCellType to index in list of cell types
242 std::map<std::uint8_t, std::int32_t> type_to_index;
243 std::vector<mesh::CellType> dolfinx_cell_type;
244 for (std::size_t i = 0; i < recv_types.size(); ++i)
245 {
246 type_to_index.insert({recv_types[i], i});
247 dolfinx_cell_type.push_back(vtk_to_dolfinx.at(recv_types[i]));
248 }
249
250 dset_id = io::hdf5::open_dataset(h5file, "/VTKHDF/NumberOfPoints");
251 std::vector<std::int64_t> npoints
252 = io::hdf5::read_dataset<std::int64_t>(dset_id, {0, 1}, true);
253 H5Dclose(dset_id);
254 spdlog::info("Mesh with {} points", npoints[0]);
255 auto local_point_range = MPI::local_range(rank, npoints[0], size);
256
257 std::vector<std::int64_t> x_shape
258 = io::hdf5::get_dataset_shape(h5file, "/VTKHDF/Points");
259 dset_id = io::hdf5::open_dataset(h5file, "/VTKHDF/Points");
260 std::vector<U> points_local
261 = io::hdf5::read_dataset<U>(dset_id, local_point_range, true);
262 H5Dclose(dset_id);
263 dset_id = io::hdf5::open_dataset(h5file, "/VTKHDF/Offsets");
264 std::vector<std::int64_t> offsets = io::hdf5::read_dataset<std::int64_t>(
265 dset_id, {local_cell_range[0], local_cell_range[1] + 1}, true);
266 H5Dclose(dset_id);
267 dset_id = io::hdf5::open_dataset(h5file, "/VTKHDF/Connectivity");
268 std::vector<std::int64_t> topology = io::hdf5::read_dataset<std::int64_t>(
269 dset_id, {offsets.front(), offsets.back()}, true);
270 H5Dclose(dset_id);
271 const std::int64_t offset0 = offsets.front();
272 std::for_each(offsets.begin(), offsets.end(),
273 [&offset0](std::int64_t& v) { v -= offset0; });
274 io::hdf5::close_file(h5file);
275
276 // Create cell topologies for each celltype in mesh
277 std::vector<std::vector<std::int64_t>> cells_local(recv_types.size());
278 for (std::size_t j = 0; j < types.size(); ++j)
279 {
280 std::int32_t type_index = type_to_index.at(types[j]);
281 mesh::CellType cell_type = dolfinx_cell_type[type_index];
282 auto perm = io::cells::perm_vtk(cell_type, offsets[j + 1] - offsets[j]);
283
284 for (std::size_t k = 0; k < offsets[j + 1] - offsets[j]; ++k)
285 cells_local[type_index].push_back(topology[perm[k] + offsets[j]]);
286 }
287
288 // Make first order coordinate elements
289 std::vector<fem::CoordinateElement<U>> coordinate_elements;
290 for (auto& cell : dolfinx_cell_type)
291 coordinate_elements.push_back(fem::CoordinateElement<U>(cell, 1));
292
293 auto part = create_cell_partitioner(mesh::GhostMode::none);
294 std::vector<std::span<const std::int64_t>> cells_span;
295 for (auto& cells : cells_local)
296 cells_span.push_back(cells);
297 std::array<std::size_t, 2> xs
298 = {(std::size_t)x_shape[0], (std::size_t)x_shape[1]};
299 return create_mesh(comm, comm, cells_span, coordinate_elements, comm,
300 points_local, xs, part);
301}
Definition XDMFFile.h:29
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
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 rank, std::int64_t N, int size)
Return local range for the calling process, partitioning the global [0, N - 1] range across all ranks...
Definition MPI.h:91
std::int8_t get_vtk_cell_type(mesh::CellType cell, int dim)
Definition cells.cpp:682
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:577
int cell_dim(CellType type)
Return topological dimension of cell type.
Definition cell_types.cpp:134
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)
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:803
CellType
Cell type identifier.
Definition cell_types.h:22
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode=mesh::GhostMode::none, const graph::partition_fn &partfn=&graph::partition_graph)
Create a function that computes destination rank for mesh cells on this rank by applying the default ...
Definition utils.cpp:85
Top-level namespace.
Definition defines.h:12
std::string version()
Return DOLFINx version string.
Definition defines.cpp:10