Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write and read mesh using ADIOS2 #3291

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
8c2756f
Add basic write function
ampdes Jun 19, 2024
42487b3
Update CMakeLists and dolfinx_io
ampdes Jun 19, 2024
bf40802
Add checkpointing demo
ampdes Jun 19, 2024
e66b319
Update dolfinx/io CMakeLists
ampdes Jun 20, 2024
df6ed6d
Merge branch 'main' into checkpointing
ampdes Jun 30, 2024
bf78637
Add implementation of mesh write
ampdes Jul 4, 2024
339c1f0
Remove const and clean
ampdes Jul 4, 2024
6ebac6e
Add checkpointing demo to regression tests
ampdes Jul 4, 2024
5a4d190
clang formatting
ampdes Jul 4, 2024
e644b5e
Merge branch 'main' into checkpointing
ampdes Jul 4, 2024
1d97e78
Minor cahnges in layout
jorgensd Jul 5, 2024
1b2a243
Cast adios2 variable creation args to unsigned long int
ampdes Jul 5, 2024
f2d3115
Declare with uint instead of cast later
ampdes Jul 5, 2024
5dd47c6
Fix offset computations
ampdes Jul 9, 2024
0e1b770
Add ADIOS2Engine class adapted from ADIOS2Writer
ampdes Jul 9, 2024
b8ebd93
Update documentation
ampdes Jul 9, 2024
76f018f
Include headers in demo
ampdes Jul 9, 2024
7ae238a
Remove demo from tests
ampdes Jul 9, 2024
eb0e813
Update copyright text
ampdes Jul 10, 2024
d3a965b
Clean up
ampdes Jul 21, 2024
729846b
Rename variables
ampdes Jul 26, 2024
6025802
Merge branch 'main' into checkpointing
ampdes Jul 26, 2024
b2f4bd0
Pass ADIOS2Engine writer to write function
ampdes Jul 26, 2024
011027b
Merge branch 'update' into checkpointing
ampdes Jul 26, 2024
d7e8e3a
Update docs
ampdes Jul 26, 2024
ca94e17
Update from review
ampdes Jul 27, 2024
55b9288
Pass IO and Engine shared ptrs
ampdes Jul 27, 2024
241fbef
ADIOS IO and Engine without ptrs
ampdes Jul 27, 2024
43f6481
Pass mesh without shared ptr
ampdes Jul 27, 2024
4d71e4c
Add python demo
ampdes Jul 28, 2024
659281a
Rename nodes to vertices
ampdes Jul 30, 2024
38bb3d5
Scope into sections
ampdes Jul 31, 2024
f8d5701
Remove python demo
ampdes Jul 31, 2024
0940f59
Remove ADIOS_utils
ampdes Jul 31, 2024
2a4a986
Switch to pass by reference
ampdes Jul 31, 2024
cbc6fb2
WIP-buggy code dump for mesh_read
ampdes Jul 31, 2024
d82a412
Fix errors in read_mesh, add version, git_hash
ampdes Jul 31, 2024
5f4a50d
Add read_mesh of one type
ampdes Aug 1, 2024
d537461
Fix docs
ampdes Aug 1, 2024
4178385
Templated read_mesh
ampdes Aug 1, 2024
6d34167
Add python demo for ADIOS2 wrapper write
ampdes Aug 1, 2024
3f6ce60
isort and formatting
ampdes Aug 1, 2024
8162339
Fix docs
ampdes Aug 1, 2024
b93cc2e
Add missing has_adios2
ampdes Aug 1, 2024
73a2643
Update docs
ampdes Aug 1, 2024
c81221d
Rename to ADIOS2Wrapper and engine_type
ampdes Aug 2, 2024
20708f5
Add python API for write_mesh
ampdes Aug 2, 2024
5f7d505
Fix includes and docs
ampdes Aug 2, 2024
55b266c
Add read_mesh
ampdes Aug 3, 2024
1c548db
Update from review
ampdes Aug 6, 2024
ff0fb62
Update from review
ampdes Aug 12, 2024
a3fe473
Add config file input to ADIOS2 wrapper
ampdes Aug 12, 2024
dd524c7
Merge branch 'main' into checkpointing
ampdes Aug 12, 2024
636f742
Refactor and clean
ampdes Aug 14, 2024
77f059f
Parametrize ghost_mode; add cpp demo to doc
ampdes Aug 14, 2024
64480e2
Add test; add ufl_domain to the read_mesh
ampdes Aug 16, 2024
61a8288
Fix import errors
ampdes Aug 16, 2024
2611b1b
Merge branch 'main' into checkpointing
ampdes Aug 16, 2024
e988a0a
Add version compatibility check
ampdes Aug 17, 2024
2cfebf2
Reorganize namespaces
ampdes Aug 19, 2024
2688e51
Redesign ADIOS2Wrapper to hold multiple IO and Engine
ampdes Aug 22, 2024
fb39916
Clean up and update for the new wrapper
ampdes Aug 25, 2024
8bec1a1
Add time dependent mesh write
ampdes Aug 26, 2024
24bab34
Add read_timestamps in readrandomaccess mode
ampdes Aug 29, 2024
a0543eb
Replace basic loops with std::ranges::transform
ampdes Aug 30, 2024
80265ae
Merge branch 'main' into checkpointing
ampdes Aug 30, 2024
9583a3b
Add tests for time dependent mesh
ampdes Aug 30, 2024
473bba4
Merge branch 'main' into checkpointing
ampdes Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions cpp/demo/checkpointing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This file was generated by running
#
# python cmake/scripts/generate-cmakefiles.py from dolfinx/cpp
#
cmake_minimum_required(VERSION 3.19)

set(PROJECT_NAME demo_checkpointing)
project(${PROJECT_NAME} LANGUAGES C CXX)

# Set C++20 standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(NOT TARGET dolfinx)
find_package(DOLFINX REQUIRED)
endif()

set(CMAKE_INCLUDE_CURRENT_DIR ON)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} dolfinx)

# Do not throw error for 'multi-line comments' (these are typical in rst which
# includes LaTeX)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-Wno-comment" HAVE_NO_MULTLINE)
set_source_files_properties(
main.cpp
PROPERTIES
COMPILE_FLAGS
"$<$<BOOL:${HAVE_NO_MULTLINE}>:-Wno-comment -Wall -Wextra -pedantic -Werror>"
)

# Test targets (used by DOLFINx testing system)
set(TEST_PARAMETERS2 -np 2 ${MPIEXEC_PARAMS} "./${PROJECT_NAME}")
set(TEST_PARAMETERS3 -np 3 ${MPIEXEC_PARAMS} "./${PROJECT_NAME}")
add_test(NAME ${PROJECT_NAME}_mpi_2 COMMAND "mpirun" ${TEST_PARAMETERS2})
add_test(NAME ${PROJECT_NAME}_mpi_3 COMMAND "mpirun" ${TEST_PARAMETERS3})
add_test(NAME ${PROJECT_NAME}_serial COMMAND ${PROJECT_NAME})
37 changes: 37 additions & 0 deletions cpp/demo/checkpointing/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// ```text
// Copyright (C) 2024 Abdullah Mujahid
// This file is part of DOLFINx (https://www.fenicsproject.org)
// SPDX-License-Identifier: LGPL-3.0-or-later
// ```

// # Checkpointing
//

#include <adios2.h>
#include <dolfinx.h>
#include <dolfinx/io/ADIOS2_utils.h>
#include <dolfinx/io/checkpointing.h>
#include <mpi.h>

using namespace dolfinx;
jhale marked this conversation as resolved.
Show resolved Hide resolved
using namespace dolfinx::io;

int main(int argc, char* argv[])
{
dolfinx::init_logging(argc, argv);
MPI_Init(&argc, &argv);

// Create mesh and function space
auto part = mesh::create_cell_partitioner(mesh::GhostMode::shared_facet);
auto mesh = std::make_shared<mesh::Mesh<float>>(mesh::create_rectangle<float>(
MPI_COMM_WORLD, {{{0.0, 0.0}, {1.0, 1.0}}}, {4, 4},
mesh::CellType::quadrilateral, part));

auto writer = ADIOS2Engine(mesh->comm(), "mesh.bp", "mesh-write", "BP5",
adios2::Mode::Write);

io::checkpointing::write(writer, mesh);

MPI_Finalize();
return 0;
}
36 changes: 36 additions & 0 deletions cpp/dolfinx/io/ADIOS2_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (C) 2024 Abdullah Mujahid, Jørgen S. Dokken and Garth N. Wells
jhale marked this conversation as resolved.
Show resolved Hide resolved
//
// This file is part of DOLFINX (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifdef HAS_ADIOS2

#include "ADIOS2_utils.h"
#include <adios2.h>
#include <mpi.h>

using namespace dolfinx::io;

//-----------------------------------------------------------------------------
ADIOS2Engine::ADIOS2Engine(MPI_Comm comm, const std::filesystem::path& filename,
std::string tag, std::string engine,
const adios2::Mode mode)

: _adios(std::make_unique<adios2::ADIOS>(comm)),
_io(std::make_unique<adios2::IO>(_adios->DeclareIO(tag)))
{
_io->SetEngine(engine);
_engine = std::make_unique<adios2::Engine>(_io->Open(filename, mode));
}
//-----------------------------------------------------------------------------
ADIOS2Engine::~ADIOS2Engine() { close(); }
//-----------------------------------------------------------------------------
void ADIOS2Engine::close()
{
assert(_engine);
if (*_engine)
_engine->Close();
}

#endif
69 changes: 69 additions & 0 deletions cpp/dolfinx/io/ADIOS2_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) 2024 Abdullah Mujahid, Jørgen S. Dokken and Garth N. Wells
//
// This file is part of DOLFINX (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#pragma once

#ifdef HAS_ADIOS2

#include <adios2.h>
#include <cassert>
#include <filesystem>
#include <mpi.h>

/// @file ADIOS2_utils.h
/// @brief Utils for ADIOS2

namespace dolfinx::io
{

/// ADIOS2-based writers/readers
class ADIOS2Engine
ampdes marked this conversation as resolved.
Show resolved Hide resolved
{
public:
/// @brief Create an ADIOS2-based engine writer/reader
/// @param[in] comm The MPI communicator
/// @param[in] filename Name of output file
/// @param[in] tag The ADIOS2 object name
/// @param[in] engine ADIOS2 engine type. See
/// https://adios2.readthedocs.io/en/latest/engines/engines.html.
/// @param[in] mode ADIOS2 mode, default is Write or Read
ADIOS2Engine(MPI_Comm comm, const std::filesystem::path& filename,
jhale marked this conversation as resolved.
Show resolved Hide resolved
std::string tag, std::string engine = "BP5",
const adios2::Mode mode = adios2::Mode::Write);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implicit write seems odd; no default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default mode is adios2::Mode::Write

For a reader, we need to specify adios2::Mode::Read explicitly.

Did you mean this or did I not understand your comment?


/// @brief Move constructor
ADIOS2Engine(ADIOS2Engine&& engine) = default;

/// @brief Copy constructor
ADIOS2Engine(const ADIOS2Engine&) = delete;

/// @brief Destructor
~ADIOS2Engine();

/// @brief Move assignment
ADIOS2Engine& operator=(ADIOS2Engine&& engine) = default;

// Copy assignment
ADIOS2Engine& operator=(const ADIOS2Engine&) = delete;

/// @brief Close the file
void close();

/// @brief Get the IO object
std::shared_ptr<adios2::IO> io() { return _io; }

/// @brief Close the Engine object
ampdes marked this conversation as resolved.
Show resolved Hide resolved
std::shared_ptr<adios2::Engine> engine() { return _engine; }

protected:
std::shared_ptr<adios2::ADIOS> _adios;
jhale marked this conversation as resolved.
Show resolved Hide resolved
std::shared_ptr<adios2::IO> _io;
jhale marked this conversation as resolved.
Show resolved Hide resolved
std::shared_ptr<adios2::Engine> _engine;
};

} // namespace dolfinx::io

#endif
6 changes: 5 additions & 1 deletion cpp/dolfinx/io/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
set(HEADERS_io
${CMAKE_CURRENT_SOURCE_DIR}/dolfinx_io.h
${CMAKE_CURRENT_SOURCE_DIR}/ADIOS2_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/ADIOS2Writers.h
${CMAKE_CURRENT_SOURCE_DIR}/cells.h
${CMAKE_CURRENT_SOURCE_DIR}/checkpointing.h
${CMAKE_CURRENT_SOURCE_DIR}/HDF5Interface.h
${CMAKE_CURRENT_SOURCE_DIR}/vtk_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/VTKFile.h
Expand All @@ -14,8 +16,10 @@ set(HEADERS_io

target_sources(
dolfinx
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ADIOS2Writers.cpp
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ADIOS2_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ADIOS2Writers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cells.cpp
${CMAKE_CURRENT_SOURCE_DIR}/checkpointing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/HDF5Interface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/VTKFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/vtk_utils.cpp
Expand Down
151 changes: 151 additions & 0 deletions cpp/dolfinx/io/checkpointing.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (C) 2024 Abdullah Mujahid
//
// This file is part of DOLFINX (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifdef HAS_ADIOS2

#include "checkpointing.h"
#include "ADIOS2_utils.h"
#include <adios2.h>
#include <basix/finite-element.h>
#include <dolfinx/mesh/Mesh.h>
#include <mpi.h>

using namespace dolfinx;
ampdes marked this conversation as resolved.
Show resolved Hide resolved
using namespace dolfinx::io;

/// @file checkpointing.h
/// @brief ADIOS2 based checkpointing
namespace
{
std::map<basix::element::lagrange_variant, std::string> lagrange_variants{
jhale marked this conversation as resolved.
Show resolved Hide resolved
{basix::element::lagrange_variant::unset, "unset"},
{basix::element::lagrange_variant::equispaced, "equispaced"},
{basix::element::lagrange_variant::gll_warped, "gll_warped"},
};

template <std::floating_point T>
void _write(ADIOS2Engine& adios2engine,
std::shared_ptr<dolfinx::mesh::Mesh<T>> mesh)
ampdes marked this conversation as resolved.
Show resolved Hide resolved
{

auto io = adios2engine.io();
auto writer = adios2engine.engine();

const dolfinx::mesh::Geometry<T>& geometry = mesh->geometry();
auto topology = mesh->topology();
ampdes marked this conversation as resolved.
Show resolved Hide resolved

std::int16_t mesh_dim = geometry.dim();
ampdes marked this conversation as resolved.
Show resolved Hide resolved

auto imap = mesh->geometry().index_map();
std::uint64_t num_nodes_global = imap->size_global();
std::uint32_t num_nodes_local = imap->size_local();
std::uint64_t offset = imap->local_range()[0];

const std::shared_ptr<const dolfinx::common::IndexMap> topo_imap
= topology->index_map(mesh_dim);
std::uint64_t num_cells_global = topo_imap->size_global();
std::uint32_t num_cells_local = topo_imap->size_local();
std::uint64_t cell_offset = topo_imap->local_range()[0];

auto cmap = mesh->geometry().cmap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a clear idea of how this will extend to the mixed topology mesh case?

Copy link
Contributor Author

@ampdes ampdes Jul 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisrichardson can comment on the data structure of how mixed topology is implemented.

Instead of constant num_dofs_per_cell and hence a fixed array of shape (num_cells, num_dofs_per_cell), we can use AdjacencyList.
Similar to the example from meshtags, offsets and global size can be computed as

// Compute the global offset for owned (local) vertex indices
  std::uint64_t local_start = 0;
  {
    MPI_Exscan(&num_saved_tag_entities, &local_start, 1, MPI_UINT64_T, MPI_SUM,
               mesh->comm());
  }

  std::uint64_t num_tag_entities_global = 0;
  MPI_Allreduce(&num_saved_tag_entities, &num_tag_entities_global, 1,
                MPI_UINT64_T, MPI_SUM, mesh->comm());

auto geom_layout = cmap.create_dof_layout();
std::uint32_t num_dofs_per_cell
= geom_layout.num_entity_closure_dofs(mesh_dim);

const std::vector<int64_t> input_global_indices
= geometry.input_global_indices();
const std::span<const int64_t> input_global_indices_span(
input_global_indices.begin(), num_nodes_local);
const std::span<const T> mesh_x = geometry.x();

auto connectivity = topology->connectivity(mesh_dim, 0);
ampdes marked this conversation as resolved.
Show resolved Hide resolved
auto topology_array = connectivity->array();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use types in variable names, clear from right hand side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meaning use array, offsets instead of topology_array, topology_offsets?

auto topology_offsets = connectivity->offsets();

const std::span<const int32_t> topology_array_span(
topology_array.begin(), topology_offsets[num_cells_local]);
std::vector<std::int64_t> topology_array_global(
topology_offsets[num_cells_local]);

std::iota(topology_array_global.begin(), topology_array_global.end(), 0);

imap->local_to_global(topology_array_span, topology_array_global);

for (std::size_t i = 0; i < num_cells_local + 1; ++i)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible with standard library iterators?

topology_offsets[i] += cell_offset * num_dofs_per_cell;

const std::span<const int32_t> topology_offsets_span(topology_offsets.begin(),
num_cells_local + 1);

io->DefineAttribute<std::string>("name", mesh->name);
ampdes marked this conversation as resolved.
Show resolved Hide resolved
io->DefineAttribute<std::int16_t>("dim", geometry.dim());
io->DefineAttribute<std::string>("cell_type",
dolfinx::mesh::to_string(cmap.cell_shape()));
io->DefineAttribute<std::int32_t>("degree", cmap.degree());
io->DefineAttribute<std::string>("lagrange_variant",
lagrange_variants[cmap.variant()]);

adios2::Variable<std::uint64_t> var_num_nodes
= io->DefineVariable<std::uint64_t>("num_nodes");
adios2::Variable<std::uint64_t> var_num_cells
= io->DefineVariable<std::uint64_t>("num_cells");
adios2::Variable<std::uint32_t> var_num_dofs_per_cell
= io->DefineVariable<std::uint32_t>("num_dofs_per_cell");

adios2::Variable<std::int64_t> var_input_global_indices
= io->DefineVariable<std::int64_t>(
"input_global_indices", {num_nodes_global}, {offset},
{num_nodes_local}, adios2::ConstantDims);

adios2::Variable<T> var_x
= io->DefineVariable<T>("x", {num_nodes_global, 3}, {offset, 0},
{num_nodes_local, 3}, adios2::ConstantDims);

adios2::Variable<std::int64_t> var_topology_array
= io->DefineVariable<std::int64_t>(
"topology_array", {num_cells_global * num_dofs_per_cell},
{cell_offset * num_dofs_per_cell},
{num_cells_local * num_dofs_per_cell}, adios2::ConstantDims);

adios2::Variable<std::int32_t> var_topology_offsets
= io->DefineVariable<std::int32_t>(
"topology_offsets", {num_cells_global + 1}, {cell_offset},
{num_cells_local + 1}, adios2::ConstantDims);

writer->BeginStep();
writer->Put(var_num_nodes, num_nodes_global);
writer->Put(var_num_cells, num_cells_global);
writer->Put(var_num_dofs_per_cell, num_dofs_per_cell);
writer->Put(var_input_global_indices, input_global_indices_span.data());
writer->Put(var_x, mesh_x.subspan(0, num_nodes_local * 3).data());
writer->Put(var_topology_array, topology_array_global.data());
writer->Put(var_topology_offsets, topology_offsets_span.data());
writer->EndStep();
}

} // namespace

using namespace dolfinx::io::checkpointing;

//-----------------------------------------------------------------------------
void dolfinx::io::checkpointing::write(
ADIOS2Engine& adios2engine,
std::shared_ptr<dolfinx::mesh::Mesh<float>> mesh)
{

_write(adios2engine, mesh);
}

//-----------------------------------------------------------------------------
void dolfinx::io::checkpointing::write(
ampdes marked this conversation as resolved.
Show resolved Hide resolved
ADIOS2Engine& adios2engine,
std::shared_ptr<dolfinx::mesh::Mesh<double>> mesh)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about use of shared pointer here.

{

_write(adios2engine, mesh);
}

#endif
39 changes: 39 additions & 0 deletions cpp/dolfinx/io/checkpointing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (C) 2024 Abdullah Mujahid
//
// This file is part of DOLFINX (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#pragma once

#ifdef HAS_ADIOS2

#include "ADIOS2_utils.h"
#include <adios2.h>
#include <basix/finite-element.h>
#include <dolfinx/mesh/Mesh.h>
#include <mpi.h>

/// @file checkpointing.h
/// @brief ADIOS2 based checkpointing

namespace dolfinx::io::checkpointing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to discuss checkpointing terminology. Native IO could be a better description.

{

/// @brief Write mesh to a file.
///
/// @param[in] adios2engine ADIOS2Engine
/// @param[in] mesh Mesh of type float to write to the file
void write(ADIOS2Engine& adios2engine,
std::shared_ptr<dolfinx::mesh::Mesh<float>> mesh);

/// @brief Write mesh to a file.
///
/// @param[in] adios2engine ADIOS2Engine
/// @param[in] mesh Mesh of type double to write to the file
void write(ADIOS2Engine& adios2engine,
std::shared_ptr<dolfinx::mesh::Mesh<double>> mesh);

} // namespace dolfinx::io::checkpointing

#endif
Loading
Loading