44namespace adios2_writer
47template <std::
floating_po
int T>
48using U = std::vector<std::variant<
49 std::shared_ptr<const fem::Function<float, T>>,
50 std::shared_ptr<const fem::Function<double, T>>,
51 std::shared_ptr<const fem::Function<std::complex<float>, T>>,
52 std::shared_ptr<const fem::Function<std::complex<double>, T>>>>;
65 ADIOS2Writer(MPI_Comm comm,
const std::filesystem::path& filename,
66 std::string tag, std::string engine);
69 ADIOS2Writer(ADIOS2Writer&& writer) =
default;
72 ADIOS2Writer(
const ADIOS2Writer&) =
delete;
78 ADIOS2Writer& operator=(ADIOS2Writer&& writer) =
default;
81 ADIOS2Writer& operator=(
const ADIOS2Writer&) =
delete;
88 std::unique_ptr<adios2::ADIOS> _adios;
89 std::unique_ptr<adios2::IO> _io;
90 std::unique_ptr<adios2::Engine> _engine;
104 const T& value, std::string var_name =
"",
105 std::string separator =
"/")
107 if (adios2::Attribute<T> attr =
io.InquireAttribute<T>(name); attr)
110 return io.DefineAttribute<T>(name, value, var_name, separator);
117 const adios2::Dims& shape = adios2::Dims(),
118 const adios2::Dims& start = adios2::Dims(),
119 const adios2::Dims& count = adios2::Dims())
121 if (adios2::Variable v =
io.InquireVariable<T>(name); v)
123 if (v.Count() != count and v.ShapeID() == adios2::ShapeID::LocalArray)
124 v.SetSelection({start, count});
128 return io.DefineVariable<T>(name, shape, start, count);
132template <std::
floating_po
int T>
133std::shared_ptr<const mesh::Mesh<T>>
138 auto mesh = std::visit([](
auto&& u) {
return u->function_space()->mesh(); },
148 if (
mesh != u->function_space()->mesh())
150 throw std::runtime_error(
151 "ADIOS2Writer only supports functions sharing the same mesh");
167std::stringstream create_vtk_schema(
const std::vector<std::string>& point_data,
168 const std::vector<std::string>& cell_data);
171template <std::
floating_po
int T>
172std::tuple<std::vector<std::string>, std::vector<std::string>>
175 std::vector<std::string> names, dg0_names;
176 std::ranges::for_each(
178 [&names, &dg0_names](
auto&& v)
181 [&names, &dg0_names](
auto&& v)
183 using U = std::decay_t<
decltype(v)>;
184 using X =
typename U::element_type;
186 if (impl::is_cellwise(*(v->function_space()->element())))
189 if constexpr (std::is_floating_point_v<typename X::value_type>)
190 names.push_back(v->name);
203 std::ranges::sort(sorted);
204 if (std::ranges::unique(sorted).begin() != sorted.end())
206 throw std::runtime_error(
207 "Function names in VTX output need to be unique.");
211 return {names, dg0_names};
223template <
typename T, std::
floating_po
int X>
229 std::span<const T> u_vector = u.
x()->array();
233 std::span<const std::size_t> value_shape
235 std::size_t rank = value_shape.size();
236 std::size_t num_comp = std::reduce(value_shape.begin(), value_shape.end(), 1,
238 if (num_comp < std::pow(3, rank))
239 num_comp = std::pow(3, rank);
241 std::shared_ptr<const fem::DofMap> dofmap = u.
function_space()->dofmap();
243 std::shared_ptr<const common::IndexMap> index_map = dofmap->index_map;
245 int index_map_bs = dofmap->index_map_bs();
246 int dofmap_bs = dofmap->bs();
247 std::uint32_t num_dofs = index_map_bs
248 * (index_map->size_local() + index_map->num_ghosts())
250 if constexpr (std::is_scalar_v<T>)
253 std::vector<T> data(num_dofs * num_comp, 0);
254 for (std::size_t i = 0; i < num_dofs; ++i)
255 for (
int j = 0; j < index_map_bs; ++j)
256 data[i * num_comp + j] = u_vector[i * index_map_bs + j];
259 io, u.
name, {}, {}, {num_dofs, num_comp});
260 spdlog::debug(
"Output data size={}", data.size());
261 engine.Put(output, data.data(), adios2::Mode::Sync);
266 using U =
typename T::value_type;
268 std::vector<U> data(num_dofs * num_comp, 0);
269 for (std::size_t i = 0; i < num_dofs; ++i)
270 for (
int j = 0; j < index_map_bs; ++j)
271 data[i * num_comp + j] = std::real(u_vector[i * index_map_bs + j]);
275 engine.Put(output_real, data.data(), adios2::Mode::Sync);
277 std::ranges::fill(data, 0);
278 for (std::size_t i = 0; i < num_dofs; ++i)
279 for (
int j = 0; j < index_map_bs; ++j)
280 data[i * num_comp + j] = std::imag(u_vector[i * index_map_bs + j]);
283 engine.Put(output_imag, data.data(), adios2::Mode::Sync);
291template <std::
floating_po
int T>
296 auto topology =
mesh.topology();
300 std::shared_ptr<const common::IndexMap> x_map =
geometry.index_map();
301 std::uint32_t num_vertices = x_map->size_local() + x_map->num_ghosts();
303 io,
"geometry", {}, {}, {num_vertices, 3});
304 spdlog::debug(
"Put local_geometry: {}x3", num_vertices);
305 engine.Put(local_geometry,
geometry.x().data());
310 io,
"NumberOfNodes", {adios2::LocalValueDim});
311 engine.Put<std::uint32_t>(vertices, num_vertices);
313 auto [vtkcells, shape]
317 int tdim = topology->dim();
319 io,
"NumberOfCells", {adios2::LocalValueDim});
320 engine.Put<std::uint32_t>(cell_var, shape[0]);
321 spdlog::debug(
"Put local_cells: {}", shape[0]);
323 adios2::Variable celltype_var
325 engine.Put<std::uint32_t>(
331 std::vector<std::int64_t>
cells(shape[0] * (shape[1] + 1), shape[1]);
332 for (std::size_t c = 0; c < shape[0]; ++c)
334 std::span vtkcell(vtkcells.data() + c * shape[1], shape[1]);
335 std::span cell(
cells.data() + c * (shape[1] + 1), shape[1] + 1);
336 std::ranges::copy(vtkcell, std::next(cell.begin()));
341 io,
"connectivity", {}, {}, {shape[0], shape[1] + 1});
342 spdlog::debug(
"Put local_topology: {}x{}", shape[0], shape[1] + 1);
344 engine.Put(local_topology,
cells.data());
348 io,
"vtkOriginalPointIds", {}, {}, {num_vertices});
349 engine.Put(orig_id,
geometry.input_global_indices().data());
351 std::vector<std::uint8_t> x_ghost(num_vertices, 0);
352 std::fill(std::next(x_ghost.begin(), x_map->size_local()), x_ghost.end(), 1);
354 io,
"vtkGhostType", {}, {}, {x_ghost.size()});
355 engine.Put(ghost, x_ghost.data());
356 engine.PerformPuts();
366template <std::
floating_po
int T>
367std::pair<std::vector<std::int64_t>, std::vector<std::uint8_t>>
373 auto topology =
mesh->topology();
375 int tdim = topology->dim();
380 spdlog::debug(
"x={}, xshape={}x{}, x_id={}, x_ghost={}", x.size(), xshape[0],
381 xshape[1], x_id.size(), x_ghost.size());
383 std::uint32_t num_dofs = xshape[0];
389 spdlog::debug(
"Create cells: [{}x{}]", vtkshape[0], vtkshape[1]);
392 std::vector<std::int64_t>
cells(vtkshape[0] * (vtkshape[1] + 1), vtkshape[1]);
395 for (std::size_t c = 0; c < vtkshape[0]; ++c)
397 std::span vtkcell(vtk.data() + c * vtkshape[1], vtkshape[1]);
398 std::span cell(
cells.data() + c * (vtkshape[1] + 1), vtkshape[1] + 1);
399 std::ranges::copy(vtkcell, std::next(cell.begin()));
404 adios2::Variable local_geometry
407 io,
"connectivity", {}, {}, {vtkshape[0], vtkshape[1] + 1});
408 adios2::Variable cell_type
411 io,
"NumberOfNodes", {adios2::LocalValueDim});
413 io,
"NumberOfEntities", {adios2::LocalValueDim});
416 spdlog::debug(
"vertices={}, elements={}, local_geom={}, local_cells={}",
417 num_dofs, vtkshape[0], x.size(),
cells.size());
418 engine.Put<std::uint32_t>(vertices, num_dofs);
419 engine.Put<std::uint32_t>(elements, vtkshape[0]);
420 engine.Put<std::uint32_t>(
422 engine.Put(local_geometry, x.data());
423 engine.Put(local_topology,
cells.data());
427 io,
"vtkOriginalPointIds", {}, {}, {x_id.size(), 1});
428 engine.Put(orig_id, x_id.data());
430 io,
"vtkGhostType", {}, {}, {x_ghost.size(), 1});
431 engine.Put(ghost, x_ghost.data());
433 engine.PerformPuts();
434 return {std::move(x_id), std::move(x_ghost)};
439enum class VTXMeshPolicy : std::int8_t
451template <std::
floating_po
int T>
452class VTXWriter :
public ADIOS2Writer
466 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
467 std::shared_ptr<
const mesh::Mesh<T>> mesh,
468 std::string engine =
"BPFile")
469 : ADIOS2Writer(comm, filename,
"VTX mesh writer", engine), _mesh(mesh),
470 _mesh_reuse_policy(VTXMeshPolicy::update),
471 _has_piecewise_constant(false)
474 std::string vtk_scheme = impl_vtx::create_vtk_schema({}, {}).str();
475 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
493 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
494 const typename adios2_writer::U<T>& u, std::string engine,
495 VTXMeshPolicy mesh_policy = VTXMeshPolicy::update)
496 : ADIOS2Writer(comm, filename,
"VTX function writer", engine),
498 _mesh_reuse_policy(mesh_policy), _has_piecewise_constant(false)
501 throw std::runtime_error(
"VTXWriter fem::Function list is empty.");
504 auto V0 = std::visit([](
auto& u) { return u->function_space().get(); },
509 bool has_V0_changed =
false;
513 [&V0, &has_V0_changed](
auto& u)
515 auto V = u->function_space().get();
517 if (!impl::is_cellwise(*V->element()))
520 has_V0_changed =
true;
527 auto element0 = V0->element().get();
531 if (element0->is_mixed())
533 throw std::runtime_error(
534 "Mixed functions are not supported by VTXWriter.");
540 if (!element0->interpolation_ident())
542 throw std::runtime_error(
543 "Only (discontinuous) Lagrange functions are "
544 "supported. Interpolate Functions before output.");
553 auto element = u->function_space()->element();
555 bool is_piecewise_constant = impl::is_cellwise(*element);
556 _has_piecewise_constant
557 = _has_piecewise_constant || is_piecewise_constant;
558 if (*element != *V0->element().get() and !is_piecewise_constant)
560 throw std::runtime_error(
"All functions in VTXWriter must have "
561 "the same element type.");
564 auto dmap0 = V0->dofmap()->map();
565 auto dmap = u->function_space()->dofmap()->map();
566 if ((dmap0.size() != dmap.size()
567 or !std::equal(dmap0.data_handle(),
568 dmap0.data_handle() + dmap0.size(),
570 and !is_piecewise_constant)
572 throw std::runtime_error(
573 "All functions must have the same dofmap for VTXWriter.");
581 auto [names, dg0_names] = impl_vtx::extract_function_names<T>(u);
582 std::string vtk_scheme;
583 vtk_scheme = impl_vtx::create_vtk_schema(names, dg0_names).str();
585 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
602 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
603 const typename adios2_writer::U<T>& u,
604 VTXMeshPolicy mesh_policy = VTXMeshPolicy::update)
605 : VTXWriter(comm, filename, u,
"BPFile", mesh_policy)
610 VTXWriter(
const VTXWriter&) =
delete;
613 VTXWriter(VTXWriter&& file) =
default;
616 ~VTXWriter() =
default;
619 VTXWriter& operator=(VTXWriter&&) =
default;
622 VTXWriter& operator=(
const VTXWriter&) =
delete;
629 adios2::Variable var_step
630 = impl_adios2::define_variable<double>(*_io,
"step");
632 spdlog::debug(
"ADIOS2: step");
634 _engine->BeginStep();
635 _engine->template Put<double>(var_step, t);
638 auto [names, dg0_names] = impl_vtx::extract_function_names<T>(_u);
639 if ((names.size() == 0) or _u.empty())
641 spdlog::debug(
"ADIOS2: write_mesh");
642 impl_vtx::vtx_write_mesh(*_io, *_engine, *_mesh);
646 if (_mesh_reuse_policy == VTXMeshPolicy::update
647 or !(_io->template InquireVariable<std::int64_t>(
"connectivity")))
651 std::tie(_x_id, _x_ghost) = std::visit(
654 spdlog::debug(
"ADIOS2: write_mesh_from_space");
655 return impl_vtx::vtx_write_mesh_from_space(*_io, *_engine,
656 *u->function_space());
663 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
664 *_io,
"vtkOriginalPointIds", {}, {}, {_x_id.size()});
665 _engine->Put(orig_id, _x_id.data());
666 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
667 *_io,
"vtkGhostType", {}, {}, {_x_ghost.size()});
668 _engine->Put(ghost, _x_ghost.data());
669 _engine->PerformPuts();
673 spdlog::debug(
"Write function data");
677 std::visit([&](
auto& u) { impl_vtx::vtx_write_data(*_io, *_engine, *u); },
685 std::shared_ptr<const mesh::Mesh<T>> _mesh;
686 adios2_writer::U<T> _u;
690 VTXMeshPolicy _mesh_reuse_policy;
691 std::vector<std::int64_t> _x_id;
692 std::vector<std::uint8_t> _x_ghost;
695 bool _has_piecewise_constant;
699template <
typename U,
typename T>
700VTXWriter(MPI_Comm comm, U filename, T mesh)
701 -> VTXWriter<
typename std::remove_cvref<
702 typename T::element_type>::type::geometry_type::value_type>;