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> 
   18namespace dolfinx::io::VTKHDF
 
   27template <std::
floating_po
int U>
 
   28void write_mesh(std::string filename, 
const mesh::Mesh<U>& mesh)
 
   30  hid_t h5file = hdf5::open_file(mesh.comm(), filename, 
"w", 
true);
 
   33  hdf5::add_group(h5file, 
"VTKHDF");
 
   34  hid_t vtk_group = H5Gopen(h5file, 
"VTKHDF", H5P_DEFAULT);
 
   35  hdf5::set_attribute(vtk_group, 
"Version", std::vector{2, 2});
 
   36  hdf5::set_attribute(vtk_group, 
"Type", 
"UnstructuredGrid");
 
   40  std::vector<mesh::CellType> cell_types
 
   41      = mesh.topology()->entity_types(mesh.topology()->dim());
 
   43  std::vector cell_index_maps
 
   44      = mesh.topology()->index_maps(mesh.topology()->dim());
 
   45  std::vector<std::int32_t> num_cells;
 
   46  std::vector<std::int64_t> num_cells_global;
 
   47  for (
auto& im : cell_index_maps)
 
   49    num_cells.push_back(im->size_local());
 
   50    num_cells_global.push_back(im->size_global());
 
   54  std::shared_ptr<const common::IndexMap> geom_imap
 
   55      = mesh.geometry().index_map();
 
   56  std::int64_t size_global = geom_imap->size_global();
 
   57  std::vector<std::int64_t> geom_global_shape = {size_global, 3};
 
   58  std::array<std::int64_t, 2> geom_irange = geom_imap->local_range();
 
   59  hdf5::write_dataset(h5file, 
"/VTKHDF/Points", mesh.geometry().x().data(),
 
   60                      geom_irange, geom_global_shape, 
true, 
false);
 
   61  hdf5::write_dataset(h5file, 
"VTKHDF/NumberOfPoints", &size_global, {0, 1},
 
   66  std::vector<std::int64_t> topology_flattened;
 
   67  std::vector<std::int64_t> topology_offsets;
 
   68  std::vector<std::uint8_t> vtkcelltypes;
 
   69  for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
 
   71    md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> g_dofmap
 
   72        = mesh.geometry().dofmap(i);
 
   74    std::vector<std::uint16_t> perm
 
   77    std::vector<std::int32_t> local_dm;
 
   78    local_dm.reserve(g_dofmap.extent(1) * num_cells[i]);
 
   79    for (
int j = 0; j < num_cells[i]; ++j)
 
   80      for (std::size_t k = 0; k < g_dofmap.extent(1); ++k)
 
   81        local_dm.push_back(g_dofmap(j, inverse_perm[k]));
 
   83    std::vector<std::int64_t> global_dm(local_dm.size());
 
   84    geom_imap->local_to_global(local_dm, global_dm);
 
   86    topology_flattened.insert(topology_flattened.end(), global_dm.begin(),
 
   88    topology_offsets.insert(topology_offsets.end(), g_dofmap.extent(0),
 
   91        vtkcelltypes.end(), cell_index_maps[i]->size_local(),
 
   96  std::partial_sum(topology_offsets.cbegin(), topology_offsets.cend(),
 
   97                   topology_offsets.begin());
 
   99  std::vector<int> num_nodes_per_cell;
 
  100  std::vector<std::int64_t> cell_start_pos;
 
  101  std::vector<std::int64_t> cell_stop_pos;
 
  102  for (std::size_t i = 0; i < cell_index_maps.size(); ++i)
 
  104    num_nodes_per_cell.push_back(mesh.geometry().cmaps()[i].dim());
 
  105    std::array<std::int64_t, 2> r = cell_index_maps[i]->local_range();
 
  106    cell_start_pos.push_back(r[0]);
 
  107    cell_stop_pos.push_back(r[1]);
 
  111  std::int64_t offset_start_position
 
  112      = std::accumulate(cell_start_pos.begin(), cell_start_pos.end(), 0);
 
  113  std::int64_t offset_stop_position
 
  114      = std::accumulate(cell_stop_pos.begin(), cell_stop_pos.end(), 0);
 
  117  std::int64_t topology_start
 
  118      = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
 
  119                           cell_start_pos.begin(), 0);
 
  121  std::transform(topology_offsets.cbegin(), topology_offsets.cend(),
 
  122                 topology_offsets.begin(),
 
  123                 [topology_start](
auto x) { return x + topology_start; });
 
  125  std::int64_t num_all_cells_global
 
  126      = std::accumulate(num_cells_global.begin(), num_cells_global.end(), 0);
 
  127  hdf5::write_dataset(h5file, 
"/VTKHDF/Offsets", topology_offsets.data(),
 
  128                      {offset_start_position + 1, offset_stop_position + 1},
 
  129                      {num_all_cells_global + 1}, 
true, 
false);
 
  132  std::int64_t topology_size_global
 
  133      = std::inner_product(num_nodes_per_cell.begin(), num_nodes_per_cell.end(),
 
  134                           num_cells_global.begin(), 0);
 
  136  std::int64_t topology_stop = topology_start + topology_flattened.size();
 
  137  hdf5::write_dataset(h5file, 
"/VTKHDF/Connectivity", topology_flattened.data(),
 
  138                      {topology_start, topology_stop}, {topology_size_global},
 
  142  hdf5::write_dataset(h5file, 
"/VTKHDF/Types", vtkcelltypes.data(),
 
  143                      {offset_start_position, offset_stop_position},
 
  144                      {num_all_cells_global}, 
true, 
false);
 
  145  hdf5::write_dataset(h5file, 
"/VTKHDF/NumberOfConnectivityIds",
 
  146                      &topology_size_global, {0, 1}, {1}, 
true, 
false);
 
  147  hdf5::write_dataset(h5file, 
"/VTKHDF/NumberOfCells", &num_all_cells_global,
 
  148                      {0, 1}, {1}, 
true, 
false);
 
  149  hdf5::close_file(h5file);
 
  173template <std::
floating_po
int U>
 
  174void write_data(std::string point_or_cell, std::string filename,
 
  175                const mesh::Mesh<U>& mesh, 
const std::vector<U>& data,
 
  178  std::vector<std::shared_ptr<const common::IndexMap>> index_maps;
 
  179  if (point_or_cell == 
"Point")
 
  180    index_maps = {mesh.geometry().index_map()};
 
  181  else if (point_or_cell == 
"Cell")
 
  182    index_maps = mesh.topology()->index_maps(mesh.topology()->dim());
 
  184    throw std::runtime_error(
"Selection must be Point or Cell");
 
  186  std::string dataset_name = 
"/VTKHDF/" + point_or_cell + 
"Data/u";
 
  188      = std::accumulate(index_maps.begin(), index_maps.end(), 0,
 
  189                        [](
int a, 
auto im) { return a + im->size_local(); });
 
  190  int data_width = data.size() / npoints;
 
  191  if (data.size() % npoints != 0)
 
  193    throw std::runtime_error(
 
  194        "Data size mismatch with number of local vertices/cells");
 
  196  spdlog::debug(
"Data vector width={}", data_width);
 
  198  hid_t h5file = hdf5::open_file(mesh.comm(), filename, 
"a", 
true);
 
  199  hdf5::add_group(h5file, 
"VTKHDF/Steps");
 
  200  hid_t vtk_group = H5Gopen(h5file, 
"VTKHDF/Steps", H5P_DEFAULT);
 
  202  std::int64_t point_data_offset = 0;
 
  203  if (htri_t attr_exists = H5Aexists(vtk_group, 
"NSteps"); attr_exists < 0)
 
  204    throw std::runtime_error(
"Error checking attribute");
 
  205  else if (attr_exists == 0)
 
  206    hdf5::set_attribute(vtk_group, 
"NSteps", 1);
 
  210    std::int32_t nsteps = 0;
 
  211    hid_t attr_id = H5Aopen(vtk_group, 
"NSteps", H5P_DEFAULT);
 
  212    H5Aread(attr_id, H5T_NATIVE_INT32, &nsteps);
 
  214    H5Awrite(attr_id, H5T_NATIVE_INT32, &nsteps);
 
  217    std::vector<std::int64_t> data_shape
 
  218        = hdf5::get_dataset_shape(h5file, dataset_name);
 
  219    assert(data_shape.size() == 2);
 
  220    point_data_offset = data_shape[0];
 
  226      = [&h5file]<
typename T>(
const std::string& dset_name, T value)
 
  229    if (hdf5::has_dataset(h5file, dset_name))
 
  231      std::vector<std::int64_t> shape
 
  232          = hdf5::get_dataset_shape(h5file, dset_name);
 
  233      assert(shape.size() == 1);
 
  236    hdf5::write_dataset(h5file, dset_name, &value, {s, s + 1}, {s + 1}, 
true,
 
  241  append_dataset(
"/VTKHDF/Steps/CellOffsets", 0);
 
  242  append_dataset(
"/VTKHDF/Steps/ConnectivityIdOffsets", 0);
 
  243  append_dataset(
"/VTKHDF/Steps/NumberOfParts", 1);
 
  244  append_dataset(
"/VTKHDF/Steps/PartOffsets", 0);
 
  245  append_dataset(
"/VTKHDF/Steps/PointOffsets", 0);
 
  248  hdf5::add_group(h5file, 
"/VTKHDF/Steps/" + point_or_cell + 
"DataOffsets");
 
  249  append_dataset(
"/VTKHDF/Steps/" + point_or_cell + 
"DataOffsets/u",
 
  254  append_dataset(
"/VTKHDF/Steps/Values", time);
 
  256  std::string group_name = 
"/VTKHDF/" + point_or_cell + 
"Data";
 
  257  hdf5::add_group(h5file, group_name);
 
  261  std::int64_t range0 = std::accumulate(index_maps.begin(), index_maps.end(), 0,
 
  263                                        { return a + im->local_range()[0]; });
 
  264  std::array<std::int64_t, 2> range{range0, range0 + npoints};
 
  266  std::int64_t global_size = std::accumulate(
 
  267      index_maps.begin(), index_maps.end(), 0,
 
  268      [](std::int64_t a, 
auto im) { return a + im->size_global(); });
 
  270  std::vector<std::int64_t> shape0 = {global_size, data_width};
 
  271  if (hdf5::has_dataset(h5file, dataset_name))
 
  273    std::vector<std::int64_t> shape
 
  274        = hdf5::get_dataset_shape(h5file, dataset_name);
 
  275    assert(shape.size() == 2);
 
  276    std::int64_t offset = shape[0];
 
  280    hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0, 
true,
 
  285    hdf5::write_dataset(h5file, dataset_name, data.data(), range, shape0, 
true,
 
  289      hid_t dset_id = hdf5::open_dataset(h5file, dataset_name);
 
  290      hdf5::set_attribute(dset_id, 
"NumberOfComponents", data_width);
 
  292      hid_t vtk_group = H5Gopen(h5file, group_name.c_str(), H5P_DEFAULT);
 
  293      hdf5::set_attribute(vtk_group, 
"Vectors", 
"u");
 
  298  hdf5::close_file(h5file);
 
  312template <std::
floating_po
int U>
 
  313mesh::Mesh<U> read_mesh(MPI_Comm comm, std::string filename,
 
  314                        std::size_t gdim = 3,
 
  315                        std::optional<std::int32_t> max_facet_to_cell_links = 2)
 
  317  hid_t h5file = hdf5::open_file(comm, filename, 
"r", 
true);
 
  319  std::vector<std::int64_t> shape
 
  320      = hdf5::get_dataset_shape(h5file, 
"/VTKHDF/Types");
 
  323  std::array<std::int64_t, 2> local_cell_range
 
  326  hid_t dset_id = hdf5::open_dataset(h5file, 
"/VTKHDF/Types");
 
  327  std::vector<std::uint8_t> types
 
  328      = hdf5::read_dataset<std::uint8_t>(dset_id, local_cell_range, 
true);
 
  332  std::map<std::uint8_t, mesh::CellType> vtk_to_dolfinx;
 
  334    for (
auto type : {mesh::CellType::point, mesh::CellType::interval,
 
  335                      mesh::CellType::triangle, mesh::CellType::quadrilateral,
 
  336                      mesh::CellType::tetrahedron, mesh::CellType::prism,
 
  337                      mesh::CellType::pyramid, mesh::CellType::hexahedron})
 
  339      vtk_to_dolfinx.insert(
 
  345  dset_id = hdf5::open_dataset(h5file, 
"/VTKHDF/Offsets");
 
  346  std::vector<std::int64_t> offsets = hdf5::read_dataset<std::int64_t>(
 
  347      dset_id, {local_cell_range[0], local_cell_range[1] + 1}, 
true);
 
  351  std::vector<std::array<std::uint8_t, 2>> types_unique;
 
  352  std::vector<std::uint8_t> cell_degrees;
 
  353  for (std::size_t i = 0; i < types.size(); ++i)
 
  355    std::int64_t num_nodes = offsets[i + 1] - offsets[i];
 
  359    cell_degrees.push_back(cell_degree);
 
  362    std::ranges::sort(types_unique);
 
  363    auto [unique_end, range_end] = std::ranges::unique(types_unique);
 
  364    types_unique.erase(unique_end, range_end);
 
  371  int count = 2 * types_unique.size();
 
  372  std::vector<std::int32_t> recv_count(mpi_size);
 
  373  MPI_Allgather(&count, 1, MPI_INT32_T, recv_count.data(), 1, MPI_INT32_T,
 
  375  std::vector<std::int32_t> recv_offsets(mpi_size + 1, 0);
 
  376  std::partial_sum(recv_count.begin(), recv_count.end(),
 
  377                   recv_offsets.begin() + 1);
 
  379  std::vector<std::array<std::uint8_t, 2>> recv_types;
 
  381    std::vector<std::uint8_t> send_types;
 
  382    for (std::array<std::uint8_t, 2> t : types_unique)
 
  383      send_types.insert(send_types.end(), t.begin(), t.end());
 
  385    std::vector<std::uint8_t> recv_types_buffer(recv_offsets.back());
 
  386    MPI_Allgatherv(send_types.data(), send_types.size(), MPI_UINT8_T,
 
  387                   recv_types_buffer.data(), recv_count.data(),
 
  388                   recv_offsets.data(), MPI_UINT8_T, comm);
 
  390    for (std::size_t i = 0; i < recv_types_buffer.size(); i += 2)
 
  391      recv_types.push_back({recv_types_buffer[i], recv_types_buffer[i + 1]});
 
  393    std::ranges::sort(recv_types);
 
  394    auto [unique_end, range_end] = std::ranges::unique(recv_types);
 
  395    recv_types.erase(unique_end, range_end);
 
  399  std::map<std::array<std::uint8_t, 2>, std::int32_t> type_to_index;
 
  400  std::vector<mesh::CellType> dolfinx_cell_type;
 
  401  std::vector<std::uint8_t> dolfinx_cell_degree;
 
  402  for (std::array<std::uint8_t, 2> ct : recv_types)
 
  405    type_to_index.insert({ct, dolfinx_cell_degree.size()});
 
  406    dolfinx_cell_degree.push_back(ct[1]);
 
  407    dolfinx_cell_type.push_back(cell_type);
 
  410  dset_id = hdf5::open_dataset(h5file, 
"/VTKHDF/NumberOfPoints");
 
  411  std::vector npoints = hdf5::read_dataset<std::int64_t>(dset_id, {0, 1}, 
true);
 
  413  spdlog::info(
"Mesh with {} points", npoints[0]);
 
  414  std::array<std::int64_t, 2> local_point_range
 
  417  std::vector<std::int64_t> x_shape
 
  418      = hdf5::get_dataset_shape(h5file, 
"/VTKHDF/Points");
 
  419  dset_id = hdf5::open_dataset(h5file, 
"/VTKHDF/Points");
 
  420  std::vector<U> points_local
 
  421      = hdf5::read_dataset<U>(dset_id, local_point_range, 
true);
 
  426  std::vector<U> points_pruned((local_point_range[1] - local_point_range[0])
 
  428  for (std::int64_t i = 0; i < local_point_range[1] - local_point_range[0]; ++i)
 
  430    std::copy_n(points_local.begin() + i * 3, gdim,
 
  431                points_pruned.begin() + i * gdim);
 
  434  dset_id = hdf5::open_dataset(h5file, 
"/VTKHDF/Connectivity");
 
  435  std::vector<std::int64_t> topology = hdf5::read_dataset<std::int64_t>(
 
  436      dset_id, {offsets.front(), offsets.back()}, 
true);
 
  438  std::transform(offsets.cbegin(), offsets.cend(), offsets.begin(),
 
  439                 [offset = offsets.front()](
auto x) { return x - offset; });
 
  440  hdf5::close_file(h5file);
 
  443  std::vector<std::vector<std::int64_t>> cells_local(recv_types.size());
 
  444  for (std::size_t j = 0; j < types.size(); ++j)
 
  446    std::int32_t type_index = type_to_index.at({types[j], cell_degrees[j]});
 
  448    std::vector<std::uint16_t> perm
 
  450    for (std::int64_t k = 0; k < offsets[j + 1] - offsets[j]; ++k)
 
  451      cells_local[type_index].push_back(topology[perm[k] + offsets[j]]);
 
  455  std::vector<fem::CoordinateElement<U>> coordinate_elements;
 
  457      dolfinx_cell_type.cbegin(), dolfinx_cell_type.cend(),
 
  458      dolfinx_cell_degree.cbegin(), std::back_inserter(coordinate_elements),
 
  459      [](
auto cell_type, 
auto cell_degree)
 
  461        basix::element::lagrange_variant variant
 
  462            = (cell_degree > 2) ? basix::element::lagrange_variant::equispaced
 
  463                                : basix::element::lagrange_variant::unset;
 
  464        return fem::CoordinateElement<U>(cell_type, cell_degree, variant);
 
  469                                      max_facet_to_cell_links);
 
  470  std::vector<std::span<const std::int64_t>> cells_span(cells_local.begin(),
 
  473                           points_pruned, {(std::size_t)x_shape[0], gdim}, part,
 
  474                           max_facet_to_cell_links);
 
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:89
 
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:21
 
std::int8_t get_vtk_cell_type(mesh::CellType cell, int dim)
Get VTK cell identifier.
Definition cells.cpp:707
 
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:525
 
std::vector< std::uint16_t > transpose(std::span< const std::uint16_t > map)
Compute the transpose of a re-ordering map.
Definition cells.cpp:679
 
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:601
 
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode=mesh::GhostMode::none, const graph::partition_fn &partfn=&graph::partition_graph, std::optional< std::int32_t > max_facet_to_cell_links=2)
Create a function that computes destination rank for mesh cells on this rank by applying the default ...
Definition utils.cpp:100
 
int cell_dim(CellType type)
Return topological dimension of cell type.
Definition cell_types.cpp:134
 
CellType
Cell type identifier.
Definition cell_types.h:22
 
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, const CellReorderFunction &reorder_fn=graph::reorder_gps)
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:1025