Skip to content

Commit 222bb1e

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

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

cpp/dolfinx/io/VTKHDF.h

Lines changed: 127 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,129 @@ void write_data(std::string point_or_cell, std::string filename,
298302
hdf5::close_file(h5file);
299303
}
300304

305+
/// @brief Write a CG1 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).
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::span<const std::size_t> value_shape
333+
= u.function_space()->element()->value_shape();
334+
int rank = value_shape.size();
335+
int num_components = std::reduce(value_shape.begin(), value_shape.end(), 1,
336+
std::multiplies{});
337+
338+
std::span<const U> x = u.x()->array();
339+
340+
const auto fs = u.function_space();
341+
342+
// Check that it is a Lagrange family element
343+
if (fs->element().basix_element().family() != basix::element::family::P)
344+
{
345+
throw std::runtime_error("Only Lagrange finite elements are supported");
346+
}
347+
348+
if (fs->element().basix_element().degree() == 0)
349+
{
350+
const std::int32_t num_local_cells = map_c->size_local();
351+
352+
std::vector<U> data(num_local_cells * num_components);
353+
354+
for (std::int32_t c = 0; c < num_local_cells; ++c)
355+
{
356+
auto dofs = dofmap->cell_dofs(c);
357+
assert(dofs.size() == 1);
358+
for (std::size_t i = 0; i < dofs.size(); ++i)
359+
{
360+
std::copy_n(data + num_components * c, x + bs * dofs[i], bs);
361+
}
362+
}
363+
364+
io::VTKHDF::write_data<U>("Cell", filename, mesh, data, time);
365+
}
366+
else if (fs->element().basix_element().discontinuous() == true
367+
and fs->element().basix_element().degree() == 1)
368+
{
369+
auto map_x = mesh->geometry().index_map();
370+
assert(map_x);
371+
372+
std::shared_ptr<const fem::FiniteElement<U>> element
373+
= u.function_space()->element();
374+
assert(element);
375+
376+
auto& geometry = mesh->geometry();
377+
auto& cmap = geometry.cmap();
378+
int cmap_dim = cmap.dim();
379+
int cell_dim = element->space_dimension() / element->block_size();
380+
if (cmap_dim != cell_dim)
381+
{
382+
throw std::runtime_error(
383+
"Degree of output Function must be same as mesh degree. Maybe the "
384+
"Function needs to be interpolated?");
385+
}
386+
387+
// Check that dofmap layouts are equal and check Lagrange variants
388+
if (dofmap->element_dof_layout() != cmap.create_dof_layout())
389+
{
390+
throw std::runtime_error("Function and Mesh dof layouts do not match. "
391+
"Maybe the Function needs to be interpolated?");
392+
}
393+
if (cmap.degree() > 2
394+
and element->basix_element().lagrange_variant() != cmap.variant())
395+
{
396+
throw std::runtime_error("Mismatch in Lagrange family. Maybe the "
397+
"Function needs to be interpolated?");
398+
}
399+
400+
std::int32_t num_cells = map_c->size_local() + map_c->num_ghosts();
401+
std::int32_t num_local_points = map_x->size_local();
402+
403+
// Get dof array and pack into array (padded where appropriate)
404+
auto dofmap_x = geometry.dofmap();
405+
std::vector<U> data(num_local_points * num_components);
406+
for (std::int32_t c = 0; c < num_cells; ++c)
407+
{
408+
auto dofs = dofmap->cell_dofs(c);
409+
auto dofs_x = md::submdspan(dofmap_x, c, md::full_extent);
410+
assert(dofs.size() == dofs_x.size());
411+
for (std::size_t i = 0; i < dofs.size(); ++i)
412+
{
413+
if (dofs_x[i] < num_local_points)
414+
{
415+
std::copy_n(data + num_components * dofs_x[i], x + bs * dofs[i], bs);
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...");
425+
}
426+
}
427+
301428
/// @brief Read a mesh from a VTKHDF format file.
302429
///
303430
/// @tparam U Scalar type of mesh

python/dolfinx/io/vtkhdf.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,22 @@
1616
from dolfinx.cpp.io import (
1717
read_vtkhdf_mesh_float32,
1818
read_vtkhdf_mesh_float64,
19+
write_vtkhdf_cg1_function,
1920
write_vtkhdf_data,
21+
write_vtkhdf_dg0_function,
2022
write_vtkhdf_mesh,
2123
)
24+
from dolfinx.fem import Function
2225
from dolfinx.mesh import Mesh
2326

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

2636

2737
def read_mesh(
@@ -100,3 +110,27 @@ def write_cell_data(filename: str | Path, mesh: Mesh, data: npt.NDArray, time: f
100110
time: Timestamp.
101111
"""
102112
write_vtkhdf_data("Cell", filename, mesh._cpp_object, data, time)
113+
114+
115+
def write_cg1_function(filename: str | Path, mesh: Mesh, u: Function, time: float):
116+
"""Write a CG1 function to file.
117+
118+
Args:
119+
filename: File to write to.
120+
mesh: Mesh.
121+
u: Function to write.
122+
time: Timestamp.
123+
"""
124+
write_vtkhdf_cg1_function(filename, mesh._cpp_object, u._cpp_object, time)
125+
126+
127+
def write_dg0_function(filename: str | Path, mesh: Mesh, u: Function, time: float):
128+
"""Write a DG0 function to file.
129+
130+
Args:
131+
filename: File to write to.
132+
mesh: Mesh.
133+
u: Function to write.
134+
time: Timestamp.
135+
"""
136+
write_vtkhdf_dg0_function(filename, mesh._cpp_object, u._cpp_object, time)

python/dolfinx/wrappers/io.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ 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_cg1_function", &dolfinx::io::VTKHDF::write_CG1_function<float>);
229+
m.def("write_vtkhdf_cg1_function", &dolfinx::io::VTKHDF::write_CG1_function<double>);
230+
m.def("write_vtkhdf_dg0_function", &dolfinx::io::VTKHDF::write_DG0_function<float>);
231+
m.def("write_vtkhdf_dg0_function", &dolfinx::io::VTKHDF::write_DG0_function<double>);
228232
m.def("read_vtkhdf_mesh_float64",
229233
[](MPICommWrapper comm, const std::string& filename, std::size_t gdim,
230234
std::optional<std::int32_t> max_facet_to_cell_links)

0 commit comments

Comments
 (0)