14#include <basix/mdspan.hpp>
18#include <dolfinx/common/IndexMap.h>
20#include <dolfinx/fem/FiniteElement.h>
21#include <dolfinx/fem/Function.h>
22#include <dolfinx/mesh/Geometry.h>
23#include <dolfinx/mesh/Mesh.h>
37template <dolfinx::scalar T, std::
floating_po
int U>
43namespace adios2_writer
46template <std::
floating_po
int T>
47using U = std::vector<std::variant<
48 std::shared_ptr<const fem::Function<float, T>>,
49 std::shared_ptr<const fem::Function<double, T>>,
50 std::shared_ptr<const fem::Function<std::complex<float>, T>>,
51 std::shared_ptr<const fem::Function<std::complex<double>, T>>>>;
64 ADIOS2Writer(MPI_Comm comm,
const std::filesystem::path& filename,
65 std::string tag, std::string engine);
68 ADIOS2Writer(ADIOS2Writer&& writer) =
default;
71 ADIOS2Writer(
const ADIOS2Writer&) =
delete;
77 ADIOS2Writer& operator=(ADIOS2Writer&& writer) =
default;
80 ADIOS2Writer& operator=(
const ADIOS2Writer&) =
delete;
87 std::unique_ptr<adios2::ADIOS> _adios;
88 std::unique_ptr<adios2::IO> _io;
89 std::unique_ptr<adios2::Engine> _engine;
97constexpr std::array field_ext = {
"_real",
"_imag"};
102adios2::Attribute<T> define_attribute(adios2::IO& io, std::string name,
103 const T& value, std::string var_name =
"",
104 std::string separator =
"/")
106 if (adios2::Attribute<T> attr = io.InquireAttribute<T>(name); attr)
109 return io.DefineAttribute<T>(name, value, var_name, separator);
115adios2::Variable<T> define_variable(adios2::IO& io, std::string name,
116 const adios2::Dims& shape = adios2::Dims(),
117 const adios2::Dims& start = adios2::Dims(),
118 const adios2::Dims& count = adios2::Dims())
120 if (adios2::Variable v = io.InquireVariable<T>(name); v)
122 if (v.Count() != count and v.ShapeID() == adios2::ShapeID::LocalArray)
123 v.SetSelection({start, count});
127 return io.DefineVariable<T>(name, shape, start, count);
131template <std::
floating_po
int T>
132std::shared_ptr<const mesh::Mesh<T>>
133extract_common_mesh(
const typename adios2_writer::U<T>& u)
137 auto mesh = std::visit([](
auto&& u) {
return u->function_space()->mesh(); },
147 if (mesh != u->function_space()->mesh())
149 throw std::runtime_error(
150 "ADIOS2Writer only supports functions sharing the same mesh");
166std::stringstream create_vtk_schema(
const std::vector<std::string>& point_data,
167 const std::vector<std::string>& cell_data);
170template <std::
floating_po
int T>
171std::tuple<std::vector<std::string>, std::vector<std::string>>
172extract_function_names(
const typename adios2_writer::U<T>& u)
174 std::vector<std::string> names, dg0_names;
178 [&names, &dg0_names](
auto&& u)
180 using U = std::decay_t<
decltype(u)>;
181 using X =
typename U::element_type;
182 std::vector<std::string>* fnames = &names;
183 if (impl::is_cellwise(*(u->function_space()->element())))
187 if constexpr (std::is_floating_point_v<typename X::value_type>)
188 fnames->push_back(u->name);
191 fnames->push_back(u->name + impl_adios2::field_ext[0]);
192 fnames->push_back(u->name + impl_adios2::field_ext[1]);
198 return {names, dg0_names};
210template <
typename T, std::
floating_po
int X>
211void vtx_write_data(adios2::IO& io, adios2::Engine& engine,
212 const fem::Function<T, X>& u)
216 std::span<const T> u_vector = u.x()->array();
220 std::span<const std::size_t> value_shape
221 = u.function_space()->element()->value_shape();
222 std::size_t
rank = value_shape.size();
223 std::size_t num_comp = std::reduce(value_shape.begin(), value_shape.end(), 1,
225 if (num_comp < std::pow(3, rank))
226 num_comp = std::pow(3, rank);
228 std::shared_ptr<const fem::DofMap> dofmap = u.function_space()->dofmap();
230 std::shared_ptr<const common::IndexMap> index_map = dofmap->index_map;
232 int index_map_bs = dofmap->index_map_bs();
233 int dofmap_bs = dofmap->bs();
234 std::uint32_t num_dofs = index_map_bs
235 * (index_map->size_local() + index_map->num_ghosts())
237 if constexpr (std::is_scalar_v<T>)
240 std::vector<T> data(num_dofs * num_comp, 0);
241 for (std::size_t i = 0; i < num_dofs; ++i)
242 for (
int j = 0; j < index_map_bs; ++j)
243 data[i * num_comp + j] = u_vector[i * index_map_bs + j];
245 adios2::Variable output = impl_adios2::define_variable<T>(
246 io, u.name, {}, {}, {num_dofs, num_comp});
247 engine.Put(output, data.data(), adios2::Mode::Sync);
252 using U =
typename T::value_type;
254 std::vector<U> data(num_dofs * num_comp, 0);
255 for (std::size_t i = 0; i < num_dofs; ++i)
256 for (
int j = 0; j < index_map_bs; ++j)
257 data[i * num_comp + j] = std::real(u_vector[i * index_map_bs + j]);
259 adios2::Variable output_real = impl_adios2::define_variable<U>(
260 io, u.name + impl_adios2::field_ext[0], {}, {}, {num_dofs, num_comp});
261 engine.Put(output_real, data.data(), adios2::Mode::Sync);
263 std::ranges::fill(data, 0);
264 for (std::size_t i = 0; i < num_dofs; ++i)
265 for (
int j = 0; j < index_map_bs; ++j)
266 data[i * num_comp + j] = std::imag(u_vector[i * index_map_bs + j]);
267 adios2::Variable output_imag = impl_adios2::define_variable<U>(
268 io, u.name + impl_adios2::field_ext[1], {}, {}, {num_dofs, num_comp});
269 engine.Put(output_imag, data.data(), adios2::Mode::Sync);
277template <std::
floating_po
int T>
278void vtx_write_mesh(adios2::IO& io, adios2::Engine& engine,
279 const mesh::Mesh<T>& mesh)
281 const mesh::Geometry<T>& geometry = mesh.geometry();
282 auto topology = mesh.topology();
286 std::shared_ptr<const common::IndexMap> x_map = geometry.index_map();
287 std::uint32_t num_vertices = x_map->size_local() + x_map->num_ghosts();
288 adios2::Variable local_geometry = impl_adios2::define_variable<T>(
289 io,
"geometry", {}, {}, {num_vertices, 3});
290 engine.Put(local_geometry, geometry.x().data());
294 adios2::Variable vertices = impl_adios2::define_variable<std::uint32_t>(
295 io,
"NumberOfNodes", {adios2::LocalValueDim});
296 engine.Put<std::uint32_t>(vertices, num_vertices);
298 auto [vtkcells, shape]
299 = io::extract_vtk_connectivity(geometry.dofmap(), topology->cell_type());
302 int tdim = topology->dim();
303 adios2::Variable cell_var = impl_adios2::define_variable<std::uint32_t>(
304 io,
"NumberOfCells", {adios2::LocalValueDim});
305 engine.Put<std::uint32_t>(cell_var, shape[0]);
306 adios2::Variable celltype_var
307 = impl_adios2::define_variable<std::uint32_t>(io,
"types");
308 engine.Put<std::uint32_t>(
309 celltype_var, cells::get_vtk_cell_type(topology->cell_type(), tdim));
314 std::vector<std::int64_t>
cells(shape[0] * (shape[1] + 1), shape[1]);
315 for (std::size_t c = 0; c < shape[0]; ++c)
317 std::span vtkcell(vtkcells.data() + c * shape[1], shape[1]);
318 std::span cell(
cells.data() + c * (shape[1] + 1), shape[1] + 1);
319 std::ranges::copy(vtkcell, std::next(cell.begin()));
323 adios2::Variable local_topology = impl_adios2::define_variable<std::int64_t>(
324 io,
"connectivity", {}, {}, {shape[0], shape[1] + 1});
325 engine.Put(local_topology,
cells.data());
328 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
329 io,
"vtkOriginalPointIds", {}, {}, {num_vertices});
330 engine.Put(orig_id, geometry.input_global_indices().data());
332 std::vector<std::uint8_t> x_ghost(num_vertices, 0);
333 std::fill(std::next(x_ghost.begin(), x_map->size_local()), x_ghost.end(), 1);
334 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
335 io,
"vtkGhostType", {}, {}, {x_ghost.size()});
336 engine.Put(ghost, x_ghost.data());
337 engine.PerformPuts();
347template <std::
floating_po
int T>
348std::pair<std::vector<std::int64_t>, std::vector<std::uint8_t>>
349vtx_write_mesh_from_space(adios2::IO& io, adios2::Engine& engine,
350 const fem::FunctionSpace<T>& V)
352 auto mesh = V.mesh();
354 auto topology = mesh->topology();
356 int tdim = topology->dim();
359 auto [x, xshape, x_id, x_ghost, vtk, vtkshape] = io::vtk_mesh_from_space(V);
361 std::uint32_t num_dofs = xshape[0];
368 std::vector<std::int64_t>
cells(vtkshape[0] * (vtkshape[1] + 1), vtkshape[1]);
371 for (std::size_t c = 0; c < vtkshape[0]; ++c)
373 std::span vtkcell(vtk.data() + c * vtkshape[1], vtkshape[1]);
374 std::span cell(
cells.data() + c * (vtkshape[1] + 1), vtkshape[1] + 1);
375 std::ranges::copy(vtkcell, std::next(cell.begin()));
380 adios2::Variable local_geometry
381 = impl_adios2::define_variable<T>(io,
"geometry", {}, {}, {num_dofs, 3});
382 adios2::Variable local_topology = impl_adios2::define_variable<std::int64_t>(
383 io,
"connectivity", {}, {}, {vtkshape[0], vtkshape[1] + 1});
384 adios2::Variable cell_type
385 = impl_adios2::define_variable<std::uint32_t>(io,
"types");
386 adios2::Variable vertices = impl_adios2::define_variable<std::uint32_t>(
387 io,
"NumberOfNodes", {adios2::LocalValueDim});
388 adios2::Variable elements = impl_adios2::define_variable<std::uint32_t>(
389 io,
"NumberOfEntities", {adios2::LocalValueDim});
392 engine.Put<std::uint32_t>(vertices, num_dofs);
393 engine.Put<std::uint32_t>(elements, vtkshape[0]);
394 engine.Put<std::uint32_t>(
395 cell_type, cells::get_vtk_cell_type(topology->cell_type(), tdim));
396 engine.Put(local_geometry, x.data());
397 engine.Put(local_topology,
cells.data());
400 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
401 io,
"vtkOriginalPointIds", {}, {}, {x_id.size()});
402 engine.Put(orig_id, x_id.data());
403 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
404 io,
"vtkGhostType", {}, {}, {x_ghost.size()});
405 engine.Put(ghost, x_ghost.data());
407 engine.PerformPuts();
408 return {std::move(x_id), std::move(x_ghost)};
413enum class VTXMeshPolicy : std::int8_t
425template <std::
floating_po
int T>
426class VTXWriter :
public ADIOS2Writer
440 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
441 std::shared_ptr<
const mesh::Mesh<T>> mesh,
442 std::string engine =
"BPFile")
443 : ADIOS2Writer(comm, filename,
"VTX mesh writer", engine), _mesh(mesh),
444 _mesh_reuse_policy(VTXMeshPolicy::update),
445 _has_piecewise_constant(false)
448 std::string vtk_scheme = impl_vtx::create_vtk_schema({}, {}).str();
449 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
467 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
468 const typename adios2_writer::U<T>& u, std::string engine,
469 VTXMeshPolicy mesh_policy = VTXMeshPolicy::update)
470 : ADIOS2Writer(comm, filename,
"VTX function writer", engine),
471 _mesh(impl_adios2::extract_common_mesh<T>(u)), _u(u),
472 _mesh_reuse_policy(mesh_policy), _has_piecewise_constant(false)
475 throw std::runtime_error(
"VTXWriter fem::Function list is empty.");
478 auto V0 = std::visit([](
auto& u) { return u->function_space().get(); },
483 bool has_V0_changed =
false;
487 [&V0, &has_V0_changed](
auto& u)
489 auto V = u->function_space().get();
491 if (!impl::is_cellwise(*V->element()))
494 has_V0_changed =
true;
501 auto element0 = V0->element().get();
505 if (element0->is_mixed())
507 throw std::runtime_error(
508 "Mixed functions are not supported by VTXWriter.");
514 if (!element0->interpolation_ident())
516 throw std::runtime_error(
517 "Only (discontinuous) Lagrange functions are "
518 "supported. Interpolate Functions before output.");
527 auto element = u->function_space()->element();
529 bool is_piecewise_constant = impl::is_cellwise(*element);
530 _has_piecewise_constant
531 = _has_piecewise_constant || is_piecewise_constant;
532 if (*element != *V0->element().get() and !is_piecewise_constant)
534 throw std::runtime_error(
"All functions in VTXWriter must have "
535 "the same element type.");
538 auto dmap0 = V0->dofmap()->map();
539 auto dmap = u->function_space()->dofmap()->map();
540 if ((dmap0.size() != dmap.size()
541 or !std::equal(dmap0.data_handle(),
542 dmap0.data_handle() + dmap0.size(),
544 and !is_piecewise_constant)
546 throw std::runtime_error(
547 "All functions must have the same dofmap for VTXWriter.");
555 auto [names, dg0_names] = impl_vtx::extract_function_names<T>(u);
556 std::string vtk_scheme;
557 vtk_scheme = impl_vtx::create_vtk_schema(names, dg0_names).str();
559 impl_adios2::define_attribute<std::string>(*_io,
"vtk.xml", vtk_scheme);
576 VTXWriter(MPI_Comm comm,
const std::filesystem::path& filename,
577 const typename adios2_writer::U<T>& u,
578 VTXMeshPolicy mesh_policy = VTXMeshPolicy::update)
579 : VTXWriter(comm, filename, u,
"BPFile", mesh_policy)
584 VTXWriter(
const VTXWriter&) =
delete;
587 VTXWriter(VTXWriter&& file) =
default;
590 ~VTXWriter() =
default;
593 VTXWriter& operator=(VTXWriter&&) =
default;
596 VTXWriter& operator=(
const VTXWriter&) =
delete;
603 adios2::Variable var_step
604 = impl_adios2::define_variable<double>(*_io,
"step");
607 _engine->BeginStep();
608 _engine->template Put<double>(var_step, t);
611 auto [names, dg0_names] = impl_vtx::extract_function_names<T>(_u);
612 if ((names.size() == 0) or _u.empty())
614 impl_vtx::vtx_write_mesh(*_io, *_engine, *_mesh);
618 if (_mesh_reuse_policy == VTXMeshPolicy::update
619 or !(_io->template InquireVariable<std::int64_t>(
"connectivity")))
623 std::tie(_x_id, _x_ghost) = std::visit(
626 return impl_vtx::vtx_write_mesh_from_space(*_io, *_engine,
627 *u->function_space());
634 adios2::Variable orig_id = impl_adios2::define_variable<std::int64_t>(
635 *_io,
"vtkOriginalPointIds", {}, {}, {_x_id.size()});
636 _engine->Put(orig_id, _x_id.data());
637 adios2::Variable ghost = impl_adios2::define_variable<std::uint8_t>(
638 *_io,
"vtkGhostType", {}, {}, {_x_ghost.size()});
639 _engine->Put(ghost, _x_ghost.data());
640 _engine->PerformPuts();
646 std::visit([&](
auto& u) { impl_vtx::vtx_write_data(*_io, *_engine, *u); },
654 std::shared_ptr<const mesh::Mesh<T>> _mesh;
655 adios2_writer::U<T> _u;
659 VTXMeshPolicy _mesh_reuse_policy;
660 std::vector<std::int64_t> _x_id;
661 std::vector<std::uint8_t> _x_ghost;
664 bool _has_piecewise_constant;
668template <
typename U,
typename T>
669VTXWriter(MPI_Comm comm, U filename, T mesh)
670 -> VTXWriter<
typename std::remove_cvref<
671 typename T::element_type>::type::geometry_type::value_type>;
Degree-of-freedom map representations and tools.
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
void cells(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:16
Finite element method functionality.
Definition assemble_expression_impl.h:23
Support for file IO.
Definition cells.h:119