Skip to content

Commit 3a3d8dd

Browse files
committed
Add functions to write CG1 and DG0 functions to VTKHDF
1 parent 100c21c commit 3a3d8dd

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

cpp/dolfinx/io/VTKHDF.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66

77
#include "HDF5Interface.h"
88
#include <algorithm>
9+
#include <basix/element-families.h>
10+
#include <basix/finite-element.h>
911
#include <concepts>
1012
#include <dolfinx/common/IndexMap.h>
13+
#include <dolfinx/fem/Function.h>
1114
#include <dolfinx/io/cells.h>
15+
#include <dolfinx/io/utils.h>
1216
#include <dolfinx/mesh/Mesh.h>
1317
#include <dolfinx/mesh/Topology.h>
1418
#include <dolfinx/mesh/utils.h>
@@ -298,6 +302,130 @@ void write_data(std::string point_or_cell, std::string filename,
298302
hdf5::close_file(h5file);
299303
}
300304

305+
/// @brief Write a function to VTKHDF.
306+
///
307+
/// Adds a function to an existing VTKHDF file, which already contains a mesh.
308+
///
309+
/// @tparam U Scalar type.
310+
/// @param[in] filename File for output.
311+
/// @param[in] mesh Mesh, which must be the same as the original mesh
312+
/// used in the file.
313+
/// @param[in] u Function to write to file.
314+
/// @param[in] time Timestamp.
315+
///
316+
/// @note Mesh must be written to file first using `VTKHDF::write_mesh`.
317+
/// @note Only one dataset "u" can be written per file at present, with
318+
/// multiple timesteps.
319+
/// @note Limited support for floating point types at present (no
320+
/// complex number support). This function only supports DG0 and CG1 functions.
321+
template <std::floating_point U>
322+
void write_function(std::string filename, const mesh::Mesh<U>& mesh,
323+
const fem::Function<U>& u, double time)
324+
{
325+
auto dofmap = u.function_space()->dofmap();
326+
assert(dofmap);
327+
const int bs = dofmap->bs();
328+
329+
auto map_c = mesh.topology()->index_map(mesh.topology()->dim());
330+
assert(map_c);
331+
332+
std::shared_ptr<const fem::FiniteElement<U>> element
333+
= u.function_space()->element();
334+
assert(element);
335+
336+
std::span<const std::size_t> value_shape = element->value_shape();
337+
int rank = value_shape.size();
338+
std::int32_t num_components = std::reduce(
339+
value_shape.begin(), value_shape.end(), 1, std::multiplies{});
340+
341+
std::span<const U> x = u.x()->array();
342+
343+
// Check that it is a Lagrange family element
344+
if (element->basix_element().family() != basix::element::family::P)
345+
{
346+
throw std::runtime_error("Only Lagrange finite elements are supported");
347+
}
348+
349+
// DG0
350+
if (element->basix_element().degree() == 0)
351+
{
352+
const std::int32_t num_local_cells = map_c->size_local();
353+
std::vector<U> data(num_local_cells * num_components);
354+
355+
for (std::int32_t c = 0; c < num_local_cells; ++c)
356+
{
357+
auto dofs = dofmap->cell_dofs(c);
358+
assert(dofs.size() == 1);
359+
for (std::size_t i = 0; i < dofs.size(); ++i)
360+
{
361+
std::copy_n(std::cbegin(x) + bs * dofs[i], bs,
362+
std::begin(data) + num_components * c);
363+
}
364+
}
365+
366+
io::VTKHDF::write_data<U>("Cell", filename, mesh, data, time);
367+
}
368+
// CG1
369+
else if (element->basix_element().discontinuous() == false
370+
and element->basix_element().degree() == 1)
371+
{
372+
auto map_x = mesh.geometry().index_map();
373+
assert(map_x);
374+
375+
auto& geometry = mesh.geometry();
376+
auto& cmap = geometry.cmap();
377+
int cmap_dim = cmap.dim();
378+
int cell_dim = element->space_dimension() / element->block_size();
379+
if (cmap_dim != cell_dim)
380+
{
381+
throw std::runtime_error("Degree of output Function must be the same as "
382+
"mesh degree. Maybe the "
383+
"Function needs to be interpolated?");
384+
}
385+
386+
// Check that dofmap layouts are equal and check Lagrange variants
387+
if (dofmap->element_dof_layout() != cmap.create_dof_layout())
388+
{
389+
throw std::runtime_error("Function and Mesh dof layouts do not match. "
390+
"Maybe the Function needs to be interpolated?");
391+
}
392+
if (cmap.degree() > 2
393+
and element->basix_element().lagrange_variant() != cmap.variant())
394+
{
395+
throw std::runtime_error("Mismatch in Lagrange family. Maybe the "
396+
"Function needs to be interpolated?");
397+
}
398+
399+
std::int32_t num_cells = map_c->size_local() + map_c->num_ghosts();
400+
std::int32_t num_local_points = map_x->size_local();
401+
402+
// Get dof array and pack into array (padded where appropriate)
403+
auto dofmap_x = geometry.dofmap();
404+
std::vector<U> data(num_local_points * num_components);
405+
for (std::int32_t c = 0; c < num_cells; ++c)
406+
{
407+
auto dofs = dofmap->cell_dofs(c);
408+
auto dofs_x = md::submdspan(dofmap_x, c, md::full_extent);
409+
assert(dofs.size() == dofs_x.size());
410+
for (std::size_t i = 0; i < dofs.size(); ++i)
411+
{
412+
if (dofs_x[i] < num_local_points)
413+
{
414+
std::copy_n(std::cbegin(x) + bs * dofs[i], bs,
415+
std::begin(data) + num_components * dofs_x[i]);
416+
}
417+
}
418+
}
419+
420+
io::VTKHDF::write_data<U>("Point", filename, mesh, data, time);
421+
}
422+
else
423+
{
424+
throw std::runtime_error("Unsupported function space. Only DG0 and CG1 are "
425+
"supported at the moment.");
426+
}
427+
}
428+
301429
/// @brief Read a mesh from a VTKHDF format file.
302430
///
303431
/// @tparam U Scalar type of mesh

python/dolfinx/io/vtkhdf.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@
1717
read_vtkhdf_mesh_float32,
1818
read_vtkhdf_mesh_float64,
1919
write_vtkhdf_data,
20+
write_vtkhdf_function,
2021
write_vtkhdf_mesh,
2122
)
23+
from dolfinx.fem import Function
2224
from dolfinx.mesh import Mesh
2325

24-
__all__ = ["read_mesh", "write_cell_data", "write_mesh", "write_point_data"]
26+
__all__ = [
27+
"read_mesh",
28+
"write_cell_data",
29+
"write_cg1_function",
30+
"write_dg0_function",
31+
"write_mesh",
32+
"write_point_data",
33+
]
2534

2635

2736
def read_mesh(
@@ -100,3 +109,15 @@ def write_cell_data(filename: str | Path, mesh: Mesh, data: npt.NDArray, time: f
100109
time: Timestamp.
101110
"""
102111
write_vtkhdf_data("Cell", filename, mesh._cpp_object, data, time)
112+
113+
114+
def write_function(filename: str | Path, mesh: Mesh, u: Function, time: float):
115+
"""Write a function to file. Only CG1 and DG0 functions are supported.
116+
117+
Args:
118+
filename: File to write to.
119+
mesh: Mesh.
120+
u: Function to write.
121+
time: Timestamp.
122+
"""
123+
write_vtkhdf_function(filename, mesh._cpp_object, u._cpp_object, time)

python/dolfinx/wrappers/io.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ void io(nb::module_& m)
225225
.def("write_vtkhdf_mesh", &dolfinx::io::VTKHDF::write_mesh<float>);
226226
m.def("write_vtkhdf_data", &dolfinx::io::VTKHDF::write_data<double>);
227227
m.def("write_vtkhdf_data", &dolfinx::io::VTKHDF::write_data<float>);
228+
m.def("write_vtkhdf_function", &dolfinx::io::VTKHDF::write_function<float>);
229+
m.def("write_vtkhdf_function", &dolfinx::io::VTKHDF::write_function<double>);
228230
m.def("read_vtkhdf_mesh_float64",
229231
[](MPICommWrapper comm, const std::string& filename, std::size_t gdim,
230232
std::optional<std::int32_t> max_facet_to_cell_links)

0 commit comments

Comments
 (0)