42namespace adios2_writer
45template <std::
floating_po
int T>
46using U = std::vector<std::variant<
47 std::shared_ptr<const fem::Function<float, T>>,
48 std::shared_ptr<const fem::Function<double, T>>,
49 std::shared_ptr<const fem::Function<std::complex<float>, T>>,
50 std::shared_ptr<const fem::Function<std::complex<double>, T>>>>;
63 ADIOS2Writer(MPI_Comm comm,
const std::filesystem::path& filename,
64 std::string tag, std::string engine);
86 std::unique_ptr<adios2::ADIOS> _adios;
87 std::unique_ptr<adios2::IO> _io;
88 std::unique_ptr<adios2::Engine> _engine;
102 const T& value, std::string var_name =
"",
103 std::string separator =
"/")
105 if (adios2::Attribute<T> attr = io.InquireAttribute<T>(name); attr)
108 return io.DefineAttribute<T>(name, value, var_name, separator);
115 const adios2::Dims& shape = adios2::Dims(),
116 const adios2::Dims& start = adios2::Dims(),
117 const adios2::Dims& count = adios2::Dims())
119 if (adios2::Variable v = io.InquireVariable<T>(name); v)
121 if (v.Count() != count and v.ShapeID() == adios2::ShapeID::LocalArray)
122 v.SetSelection({start, count});
126 return io.DefineVariable<T>(name, shape, start, count);
130template <std::
floating_po
int T>
131std::shared_ptr<const mesh::Mesh<T>>
136 auto mesh = std::visit([](
auto&& u) {
return u->function_space()->mesh(); },
146 if (mesh != u->function_space()->mesh())
148 throw std::runtime_error(
149 "ADIOS2Writer only supports functions sharing the same mesh");
167void initialize_mesh_attributes(adios2::IO& io,
mesh::CellType type);
173template <std::
floating_po
int T>
175 const typename adios2_writer::U<T>& u)
179 std::vector<std::array<std::string, 2>> u_data;
185 using U = std::decay_t<
decltype(u)>;
186 using X =
typename U::element_type;
187 if constexpr (std::is_floating_point_v<typename X::value_type>)
188 u_data.push_back({u->name,
"points"});
191 u_data.push_back({u->name + impl_adios2::field_ext[0],
"points"});
192 u_data.push_back({u->name + impl_adios2::field_ext[1],
"points"});
199 if (adios2::Attribute<std::string> assc
200 = io.InquireAttribute<std::string>(
"Fides_Variable_Associations");
203 std::vector<std::string> u_type;
204 std::transform(u_data.cbegin(), u_data.cend(), std::back_inserter(u_type),
205 [](
auto& f) { return f[1]; });
206 io.DefineAttribute<std::string>(
"Fides_Variable_Associations",
207 u_type.data(), u_type.size());
211 if (adios2::Attribute<std::string> fields
212 = io.InquireAttribute<std::string>(
"Fides_Variable_List");
215 std::vector<std::string> names;
216 std::transform(u_data.cbegin(), u_data.cend(), std::back_inserter(names),
217 [](
auto& f) { return f[0]; });
218 io.DefineAttribute<std::string>(
"Fides_Variable_List", names.data(),
225template <
typename T, std::
floating_po
int U>
230 auto dofmap = V->dofmap();
232 auto mesh = V->mesh();
235 auto topology = mesh->topology();
240 assert(dofmap->element_dof_layout()
241 == geometry.
cmaps()[0].create_dof_layout());
243 int tdim = topology->dim();
244 auto cell_map = topology->index_map(tdim);
246 std::int32_t num_cells = cell_map->size_local() + cell_map->num_ghosts();
248 auto vertex_map = topology->index_map(0);
250 std::uint32_t num_vertices
251 = vertex_map->size_local() + vertex_map->num_ghosts();
253 int rank = V->element()->value_shape().size();
254 std::uint32_t num_components = std::pow(3, rank);
257 auto dofmap_x = geometry.
dofmap();
258 const int bs = dofmap->bs();
259 std::span<const T> u_data = u.
x()->array();
260 std::vector<T> data(num_vertices * num_components, 0);
261 for (std::int32_t c = 0; c < num_cells; ++c)
263 auto dofs = dofmap->cell_dofs(c);
265 = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::
266 submdspan(dofmap_x, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
267 assert(dofs.size() == dofs_x.size());
268 for (std::size_t i = 0; i < dofs.size(); ++i)
269 for (
int j = 0; j < bs; ++j)
270 data[num_components * dofs_x[i] + j] = u_data[bs * dofs[i] + j];
282template <
typename T, std::
floating_po
int U>
290 auto dofmap = V->dofmap();
292 auto mesh = V->mesh();
294 const int gdim = mesh->geometry().dim();
297 int rank = V->element()->value_shape().size();
298 bool need_padding = rank > 0 and gdim != 3 ? true :
false;
302 std::span<const T> data;
303 std::vector<T> _data;
304 auto eq_check = [](
auto x,
auto y) ->
bool
306 return x.extents() == y.extents()
307 and std::equal(x.data_handle(), x.data_handle() + x.size(),
311 if (!need_padding and eq_check(mesh->geometry().dofmap(), dofmap->map()))
312 data = u.
x()->array();
315 _data = impl_fides::pack_function_data(u);
316 data = std::span<const T>(_data);
319 auto vertex_map = mesh->topology()->index_map(0);
321 std::uint32_t num_vertices
322 = vertex_map->size_local() + vertex_map->num_ghosts();
325 std::uint32_t num_components = std::pow(3, rank);
326 assert(data.size() % num_components == 0);
327 if constexpr (std::is_scalar_v<T>)
330 adios2::Variable local_output = impl_adios2::define_variable<T>(
331 io, u.
name, {}, {}, {num_vertices, num_components});
334 engine.Put(local_output, data.data());
335 engine.PerformPuts();
340 using X =
typename T::value_type;
342 std::vector<X> data_real(data.size()), data_imag(data.size());
344 adios2::Variable local_output_r = impl_adios2::define_variable<X>(
345 io, u.
name + impl_adios2::field_ext[0], {}, {},
346 {num_vertices, num_components});
347 std::transform(data.begin(), data.end(), data_real.begin(),
348 [](
auto x) -> X { return std::real(x); });
349 engine.Put(local_output_r, data_real.data());
351 adios2::Variable local_output_c = impl_adios2::define_variable<X>(
352 io, u.
name + impl_adios2::field_ext[1], {}, {},
353 {num_vertices, num_components});
354 std::transform(data.begin(), data.end(), data_imag.begin(),
355 [](
auto x) -> X { return std::imag(x); });
356 engine.Put(local_output_c, data_imag.data());
357 engine.PerformPuts();
365template <std::
floating_po
int T>
375 std::uint32_t num_vertices = x_map->size_local() + x_map->num_ghosts();
376 adios2::Variable local_geometry = impl_adios2::define_variable<T>(
377 io,
"points", {}, {}, {num_vertices, 3});
378 engine.Put(local_geometry, geometry.
x().data());
385 int tdim = topology->dim();
386 std::int32_t num_cells = topology->index_map(tdim)->size_local();
387 int num_nodes = geometry.
cmaps()[0].dim();
388 auto [cells, shape] = io::extract_vtk_connectivity(mesh.
geometry().dofmap(),
389 topology->cell_types()[0]);
392 adios2::Variable local_topology = impl_adios2::define_variable<std::int64_t>(
393 io,
"connectivity", {}, {}, {std::size_t(num_cells * num_nodes)});
394 engine.Put(local_topology, cells.data());
395 engine.PerformPuts();
401enum class FidesMeshPolicy
411template <std::
floating_po
int T>
412class FidesWriter :
public ADIOS2Writer
423 FidesWriter(MPI_Comm comm,
const std::filesystem::path& filename,
424 std::shared_ptr<
const mesh::Mesh<T>> mesh,
425 std::string engine =
"BPFile")
426 : ADIOS2Writer(comm, filename,
"Fides mesh writer", engine),
427 _mesh_reuse_policy(FidesMeshPolicy::update), _mesh(mesh)
431 auto topology = mesh->topology();
433 mesh::CellType type = topology->cell_types()[0];
434 if (mesh->geometry().cmaps()[0].dim() != mesh::cell_num_entities(type, 0))
435 throw std::runtime_error(
"Fides only supports lowest-order meshes.");
436 impl_fides::initialize_mesh_attributes(*_io, type);
450 FidesWriter(MPI_Comm comm,
const std::filesystem::path& filename,
451 const typename adios2_writer::U<T>& u, std::string engine,
452 const FidesMeshPolicy mesh_policy = FidesMeshPolicy::update)
453 : ADIOS2Writer(comm, filename,
"Fides function writer", engine),
454 _mesh_reuse_policy(mesh_policy),
458 throw std::runtime_error(
"FidesWriter fem::Function list is empty");
461 auto mesh = std::visit([](
auto& u) {
return u->function_space()->mesh(); },
466 auto element0 = std::visit([](
auto& e)
467 {
return e->function_space()->element().get(); },
472 if (element0->is_mixed())
474 throw std::runtime_error(
475 "Mixed functions are not supported by FidesWriter");
480 if (!element0->interpolation_ident())
482 throw std::runtime_error(
"Only Lagrange functions are supported. "
483 "Interpolate Functions before output.");
487 if (element0->space_dimension() / element0->block_size() == 1)
489 throw std::runtime_error(
490 "Piecewise constants are not (yet) supported by FidesWriter");
494 auto cell_types = mesh->topology()->cell_types();
495 if (cell_types.size() > 1)
496 throw std::runtime_error(
"Multiple cell types in IO.");
497 int num_vertices_per_cell = mesh::cell_num_entities(cell_types.back(), 0);
501 [num_vertices_per_cell, element0](
auto&& u)
503 auto element = u->function_space()->element();
505 if (*element != *element0)
507 throw std::runtime_error(
"All functions in FidesWriter must have "
508 "the same element type");
511 = u->function_space()->dofmap()->element_dof_layout();
512 int num_vertex_dofs = dof_layout.num_entity_dofs(0);
513 int num_dofs = element->space_dimension() / element->block_size();
514 if (num_dofs != num_vertices_per_cell or num_vertex_dofs != 1)
516 throw std::runtime_error(
"Only first order Lagrange spaces are "
517 "supported by FidesWriter");
523 auto topology = mesh->topology();
525 mesh::CellType type = topology->cell_types()[0];
526 if (mesh->geometry().cmaps()[0].dim() != mesh::cell_num_entities(type, 0))
527 throw std::runtime_error(
"Fides only supports lowest-order meshes.");
528 impl_fides::initialize_mesh_attributes(*_io, type);
529 impl_fides::initialize_function_attributes<T>(*_io, u);
542 FidesWriter(MPI_Comm comm,
const std::filesystem::path& filename,
543 const typename adios2_writer::U<T>& u,
544 const FidesMeshPolicy mesh_policy = FidesMeshPolicy::update)
545 : FidesWriter(comm, filename, u,
"BPFile", mesh_policy)
550 FidesWriter(
const FidesWriter&) =
delete;
553 FidesWriter(FidesWriter&& file) =
default;
556 ~FidesWriter() =
default;
559 FidesWriter& operator=(FidesWriter&&) =
default;
562 FidesWriter& operator=(
const FidesWriter&) =
delete;
570 _engine->BeginStep();
571 adios2::Variable var_step
572 = impl_adios2::define_variable<double>(*_io,
"step");
573 _engine->template Put<double>(var_step, t);
574 if (
auto v = _io->template InquireVariable<std::int64_t>(
"connectivity");
575 !v or _mesh_reuse_policy == FidesMeshPolicy::update)
577 impl_fides::write_mesh(*_io, *_engine, *_mesh);
583 [
this](
auto&& u) { impl_fides::write_data(*_io, *_engine, *u); }, v);
592 FidesMeshPolicy _mesh_reuse_policy;
594 std::shared_ptr<const mesh::Mesh<T>> _mesh;
595 adios2_writer::U<T> _u;
603std::stringstream create_vtk_schema(
const std::vector<std::string>& point_data,
604 const std::vector<std::string>& cell_data);
607template <std::
floating_po
int T>
608std::vector<std::string>
609extract_function_names(
const typename adios2_writer::U<T>& u)
611 std::vector<std::string> names;
617 using U = std::decay_t<
decltype(u)>;
618 using X =
typename U::element_type;
619 if constexpr (std::is_floating_point_v<typename X::value_type>)
620 names.push_back(u->name);
623 names.push_back(u->name + impl_adios2::field_ext[0]);
624 names.push_back(u->name + impl_adios2::field_ext[1]);
642template <
typename T, std::
floating_po
int X>
643void vtx_write_data(adios2::IO& io, adios2::Engine& engine,
644 const fem::Function<T, X>& u)
648 std::span<const T> u_vector = u.x()->array();
649 int rank = u.function_space()->element()->value_shape().size();
650 std::uint32_t num_comp = std::pow(3, rank);
651 std::shared_ptr<const fem::DofMap> dofmap = u.function_space()->dofmap();
653 std::shared_ptr<const common::IndexMap> index_map = dofmap->index_map;
655 int index_map_bs = dofmap->index_map_bs();
656 int dofmap_bs = dofmap->bs();
657 std::uint32_t num_dofs = index_map_bs
658 * (index_map->size_local() + index_map->num_ghosts())
660 if constexpr (std::is_scalar_v<T>)
663 std::vector<T> data(num_dofs * num_comp, 0);
664 for (std::size_t i = 0; i < num_dofs; ++i)
665 for (
int j = 0; j < index_map_bs; ++j)
666 data[i * num_comp + j] = u_vector[i * index_map_bs + j];
668 adios2::Variable output = impl_adios2::define_variable<T>(
669 io, u.name, {}, {}, {num_dofs, num_comp});
670 engine.Put(output, data.data(), adios2::Mode::Sync);
675 using U =
typename T::value_type;
677 std::vector<U> data(num_dofs * num_comp, 0);
678 for (std::size_t i = 0; i < num_dofs; ++i)
679 for (
int j = 0; j < index_map_bs; ++j)
680 data[i * num_comp + j] = std::real(u_vector[i * index_map_bs + j]);
682 adios2::Variable output_real = impl_adios2::define_variable<U>(
683 io, u.name + impl_adios2::field_ext[0], {}, {}, {num_dofs, num_comp});
684 engine.Put(output_real, data.data(), adios2::Mode::Sync);
686 std::fill(data.begin(), data.end(), 0);
687 for (std::size_t i = 0; i < num_dofs; ++i)
688 for (
int j = 0; j < index_map_bs; ++j)
689 data[i * num_comp + j] = std::imag(u_vector[i * index_map_bs + j]);
690 adios2::Variable output_imag = impl_adios2::define_variable<U>(
691 io, u.name + impl_adios2::field_ext[1], {}, {}, {num_dofs, num_comp});
692 engine.Put(output_imag, data.data(), adios2::Mode::Sync);
700template <std::
floating_po
int T>
701void vtx_write_mesh(adios2::IO& io, adios2::Engine& engine,
702 const mesh::Mesh<T>& mesh)
704 const mesh::Geometry<T>& geometry = mesh.geometry();
705 auto topology = mesh.topology();
709 std::shared_ptr<const common::IndexMap> x_map = geometry.index_map();
710 std::uint32_t num_vertices = x_map->size_local() + x_map->num_ghosts();
711 adios2::Variable local_geometry = impl_adios2::define_variable<T>(
712 io,
"geometry", {}, {}, {num_vertices, 3});
713 engine.Put(local_geometry, geometry.x().data());
717 adios2::Variable vertices = impl_adios2::define_variable<std::uint32_t>(
718 io,
"NumberOfNodes", {adios2::LocalValueDim});
719 engine.Put<std::uint32_t>(vertices, num_vertices);
721 auto [vtkcells, shape] = io::extract_vtk_connectivity(
722 geometry.dofmap(), topology->cell_types()[0]);
725 int tdim = topology->dim();
726 adios2::Variable cell_var = impl_adios2::define_variable<std::uint32_t>(
727 io,
"NumberOfCells", {adios2::LocalValueDim});
728 engine.Put<std::uint32_t>(cell_var, shape[0]);
729 adios2::Variable celltype_var
730 = impl_adios2::define_variable<std::uint32_t>(io,
"types");
731 engine.Put<std::uint32_t>(
732 celltype_var, cells::get_vtk_cell_type(topology->cell_types()[0], tdim));
737 std::vector<std::int64_t>
cells(shape[0] * (shape[1] + 1), shape[1]);
738 for (std::size_t c = 0; c < shape[0]; ++c)
740 std::span vtkcell(vtkcells.data() + c * shape[1], shape[1]);
741 std::span
cell(
cells.data() + c * (shape[1] + 1), shape[1] + 1);
742 std::copy(vtkcell.begin(), vtkcell.end(), std::next(
cell.begin()));
746 adios2::Variable local_topology = impl_adios2::define_variable<std::int64_t>(
747 io,
"connectivity", {}, {}, {shape[0], shape[1] + 1});
748 engine.Put(local_topology,
cells.data());
751 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
752 io,
"vtkOriginalPointIds", {}, {}, {num_vertices});
753 engine.Put(orig_id, geometry.input_global_indices().data());
755 std::vector<std::uint8_t> x_ghost(num_vertices, 0);
756 std::fill(std::next(x_ghost.begin(), x_map->size_local()), x_ghost.end(), 1);
757 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
758 io,
"vtkGhostType", {}, {}, {x_ghost.size()});
759 engine.Put(ghost, x_ghost.data());
760 engine.PerformPuts();
770template <std::
floating_po
int T>
771void vtx_write_mesh_from_space(adios2::IO& io, adios2::Engine& engine,
772 const fem::FunctionSpace<T>& V)
774 auto mesh = V.mesh();
776 auto topology = mesh->topology();
778 int tdim = topology->dim();
781 auto [x, xshape, x_id, x_ghost, vtk, vtkshape] = io::vtk_mesh_from_space(V);
783 std::uint32_t num_dofs = xshape[0];
790 std::vector<std::int64_t>
cells(vtkshape[0] * (vtkshape[1] + 1), vtkshape[1]);
793 for (std::size_t c = 0; c < vtkshape[0]; ++c)
795 std::span vtkcell(vtk.data() + c * vtkshape[1], vtkshape[1]);
796 std::span
cell(
cells.data() + c * (vtkshape[1] + 1), vtkshape[1] + 1);
797 std::copy(vtkcell.begin(), vtkcell.end(), std::next(
cell.begin()));
802 adios2::Variable local_geometry
803 = impl_adios2::define_variable<T>(io,
"geometry", {}, {}, {num_dofs, 3});
804 adios2::Variable local_topology = impl_adios2::define_variable<std::int64_t>(
805 io,
"connectivity", {}, {}, {vtkshape[0], vtkshape[1] + 1});
806 adios2::Variable cell_type
807 = impl_adios2::define_variable<std::uint32_t>(io,
"types");
808 adios2::Variable vertices = impl_adios2::define_variable<std::uint32_t>(
809 io,
"NumberOfNodes", {adios2::LocalValueDim});
810 adios2::Variable elements = impl_adios2::define_variable<std::uint32_t>(
811 io,
"NumberOfEntities", {adios2::LocalValueDim});
814 engine.Put<std::uint32_t>(vertices, num_dofs);
815 engine.Put<std::uint32_t>(elements, vtkshape[0]);
816 engine.Put<std::uint32_t>(
817 cell_type, cells::get_vtk_cell_type(topology->cell_types()[0], tdim));
818 engine.Put(local_geometry, x.data());
819 engine.Put(local_topology,
cells.data());
822 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
823 io,
"vtkOriginalPointIds", {}, {}, {x_id.size()});
824 engine.Put(orig_id, x_id.data());
825 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
826 io,
"vtkGhostType", {}, {}, {x_ghost.size()});
827 engine.Put(ghost, x_ghost.data());
829 engine.PerformPuts();
838template <std::
floating_po
int T>
839class VTXWriter :
public ADIOS2Writer
853 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
854 std::shared_ptr<
const mesh::Mesh<T>> mesh,
855 std::string engine =
"BPFile")
856 : ADIOS2Writer(comm, filename,
"VTX mesh writer", engine), _mesh(mesh)
859 std::string vtk_scheme = impl_vtx::create_vtk_schema({}, {}).str();
860 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
874 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
875 const typename adios2_writer::U<T>& u,
876 std::string engine =
"BPFile")
877 : ADIOS2Writer(comm, filename,
"VTX function writer", engine),
881 throw std::runtime_error(
"VTXWriter fem::Function list is empty.");
884 auto element0 = std::visit([](
auto& u)
885 {
return u->function_space()->element().get(); },
890 if (element0->is_mixed())
892 throw std::runtime_error(
893 "Mixed functions are not supported by VTXWriter.");
899 if (!element0->interpolation_ident())
901 throw std::runtime_error(
902 "Only (discontinuous) Lagrange functions are "
903 "supported. Interpolate Functions before output.");
907 if (element0->space_dimension() / element0->block_size() == 1)
909 throw std::runtime_error(
910 "VTK does not support cell-wise fields. See "
911 "https://gitlab.kitware.com/vtk/vtk/-/issues/18458.");
920 auto element = u->function_space()->element();
922 if (*element != *element0)
924 throw std::runtime_error(
"All functions in VTXWriter must have "
925 "the same element type.");
932 std::vector<std::string> names = impl_vtx::extract_function_names<T>(u);
933 std::string vtk_scheme = impl_vtx::create_vtk_schema(names, {}).str();
934 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
938 VTXWriter(
const VTXWriter&) =
delete;
941 VTXWriter(VTXWriter&& file) =
default;
944 ~VTXWriter() =
default;
947 VTXWriter& operator=(VTXWriter&&) =
default;
950 VTXWriter& operator=(
const VTXWriter&) =
delete;
958 adios2::Variable var_step
959 = impl_adios2::define_variable<double>(*_io,
"step");
960 _engine->BeginStep();
961 _engine->template Put<double>(var_step, t);
965 impl_vtx::vtx_write_mesh(*_io, *_engine, *_mesh);
971 impl_vtx::vtx_write_mesh_from_space(*_io, *_engine,
972 *u->function_space());
979 [&](auto& u) { impl_vtx::vtx_write_data(*_io, *_engine, *u); }, v);
986 std::shared_ptr<const mesh::Mesh<T>> _mesh;
987 adios2_writer::U<T> _u;
991template <
typename U,
typename T>
992VTXWriter(MPI_Comm comm, U filename, T mesh)
993 -> VTXWriter<
typename std::remove_cvref<
994 typename T::element_type>::type::geometry_type::value_type>;
997template <
typename U,
typename T>
998FidesWriter(MPI_Comm comm, U filename, T mesh)
999 -> FidesWriter<
typename std::remove_cvref<
1000 typename T::element_type>::type::geometry_type::value_type>;