Skip to content

Commit 7367d65

Browse files
Allow expressions with submeshes (#4140)
* Make submesh expressions work (codim 0 for now, missing test for codim 1). * Minor cleanup * Add codim 1 test and fix bug * Change fffcx ref * Ruff formatting * Mypy * Sort imports * Remove file * Fix C++ demo and test and minor logic change for when to check for coordinate element hash * Try fixing create_expression * Reorder attributes * Add docs * Fix import for docs * Copy data as it is strided when extracted from python * Fix for parallel test * Further parallel fixes * Revert to main branch of FFCx Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Update API * Add some typing * Improve docstrings Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Docstring improvement part 2 Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Add constexpr to else if Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Clang format * Use switch * Refactor to use extraction of coefficient cells. * Add docs * Minor doc change to rerun CI Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Use reference Co-authored-by: Paul T. Kühner <56360279+schnellerhase@users.noreply.github.com> * Address comments by @schnellerhase * Simpler to vector trafo * Fix: left facet marking * Fix: test use fdim and analytical expression * Check me: use extent not size * Expose facet permutations to expression on facets * Ruff format * More ruff * Extend tests * Test ffcx branch * Reformat imports in test * Apply suggestions from code review Co-authored-by: Jørgen Schartum Dokken <dokken92@gmail.com> * Merge with main * remove new file --------- Co-authored-by: Paul T. Kühner <56360279+schnellerhase@users.noreply.github.com>
1 parent 055b199 commit 7367d65

11 files changed

Lines changed: 485 additions & 97 deletions

File tree

cpp/demo/hyperelasticity/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ int main(int argc, char* argv[])
326326
// Compute Cauchy stress. Construct appropriate Basix element for
327327
// stress.
328328
fem::Expression sigma_expression = fem::create_expression<T, U>(
329-
*expression_hyperelasticity_sigma, {{"u", u}}, {});
329+
*expression_hyperelasticity_sigma, {{"u", u}}, {}, {});
330330

331331
constexpr auto family = basix::element::family::P;
332332
auto cell_type

cpp/dolfinx/fem/Expression.h

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// Copyright (C) 2020-2025 Jack S. Hale, Michal Habera and Garth N. Wells
1+
// Copyright (C) 2020-2026 Jack S. Hale, Michal Habera, Garth N. Wells and
2+
// Jørgen S. Dokken
23
//
34
// This file is part of DOLFINx (https://www.fenicsproject.org)
45
//
@@ -12,6 +13,7 @@
1213
#include <array>
1314
#include <concepts>
1415
#include <dolfinx/common/types.h>
16+
#include <dolfinx/mesh/EntityMap.h>
1517
#include <dolfinx/mesh/Mesh.h>
1618
#include <functional>
1719
#include <memory>
@@ -62,38 +64,35 @@ class Expression
6264
/// @param[in] fn Function for tabulating the Expression.
6365
/// @param[in] value_shape Shape of Expression evaluated at single
6466
/// point.
67+
/// @param[in] entity_maps Maps between entities of different meshes.
68+
/// @param[in] coordinate_element_hash Hash for coordinate element used
69+
/// to create the expression.
6570
/// @param[in] argument_space Function space for Argument. Used to
6671
/// computed a 1-form expression, e.g. can be used to create a matrix
6772
/// that when applied to a degree-of-freedom vector gives the
6873
/// expression values at the evaluation points.
69-
Expression(const std::vector<std::shared_ptr<
70-
const Function<scalar_type, geometry_type>>>& coefficients,
71-
const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
72-
constants,
73-
std::span<const geometry_type> X,
74-
std::array<std::size_t, 2> Xshape,
75-
std::function<void(scalar_type*, const scalar_type*,
76-
const scalar_type*, const geometry_type*,
77-
const int*, const uint8_t*, void*)>
78-
fn,
79-
const std::vector<std::size_t>& value_shape,
80-
std::shared_ptr<const FunctionSpace<geometry_type>> argument_space
81-
= nullptr)
74+
Expression(
75+
const std::vector<std::shared_ptr<
76+
const Function<scalar_type, geometry_type>>>& coefficients,
77+
const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
78+
constants,
79+
std::span<const geometry_type> X, std::array<std::size_t, 2> Xshape,
80+
std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
81+
const geometry_type*, const int*, const uint8_t*,
82+
void*)>
83+
fn,
84+
const std::vector<std::size_t>& value_shape,
85+
const std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>&
86+
entity_maps,
87+
88+
std::uint64_t coordinate_element_hash,
89+
std::shared_ptr<const FunctionSpace<geometry_type>> argument_space
90+
= nullptr)
8291
: _argument_space(argument_space), _coefficients(coefficients),
8392
_constants(constants), _fn(fn), _value_shape(value_shape),
84-
_x_ref(std::vector<geometry_type>(X.begin(), X.end()), Xshape)
85-
86-
{
87-
for (auto& c : _coefficients)
88-
{
89-
assert(c);
90-
if (c->function_space()->mesh()
91-
!= _coefficients.front()->function_space()->mesh())
92-
{
93-
throw std::runtime_error("Coefficients not all defined on same mesh.");
94-
}
95-
}
96-
}
93+
_x_ref(std::vector<geometry_type>(X.begin(), X.end()), Xshape),
94+
_entity_maps(entity_maps),
95+
_coordinate_element_hash(coordinate_element_hash) {};
9796

9897
/// Move constructor
9998
Expression(Expression&& e) = default;
@@ -168,6 +167,19 @@ class Expression
168167
return _x_ref;
169168
}
170169

170+
/// @brief Maps between entities of different meshes.
171+
const std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>&
172+
entity_maps() const
173+
{
174+
return _entity_maps;
175+
}
176+
177+
/// @brief Hash for coordinate element used to create the expression.
178+
std::uint64_t coordinate_element_hash() const
179+
{
180+
return _coordinate_element_hash;
181+
}
182+
171183
private:
172184
// Function space for Argument
173185
std::shared_ptr<const FunctionSpace<geometry_type>> _argument_space;
@@ -189,5 +201,13 @@ class Expression
189201

190202
// Evaluation points on reference cell
191203
std::pair<std::vector<geometry_type>, std::array<std::size_t, 2>> _x_ref;
204+
205+
// Set of bidirectional maps of mesh entities (cells, facets, ridges or peaks)
206+
// between meshes and submeshes of any codimension
207+
std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>
208+
_entity_maps;
209+
210+
// Hash for coordinate element used to create the expression
211+
std::uint64_t _coordinate_element_hash;
192212
};
193213
} // namespace dolfinx::fem

cpp/dolfinx/fem/assemble_expression_impl.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ namespace dolfinx::fem::impl
5858
/// @param[in] P0 Degree-of-freedom transformation function. Applied when
5959
/// expressions includes an argument function that requires a
6060
/// transformation.
61+
/// @param[in] perms Entity permutation information for use in `fn`.
6162
template <dolfinx::scalar T, std::floating_point U>
6263
void tabulate_expression(
6364
std::span<T> values, fem::FEkernel<T> auto fn,
@@ -68,7 +69,8 @@ void tabulate_expression(
6869
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
6970
std::span<const T> constants, fem::MDSpan2 auto entities,
7071
std::span<const std::uint32_t> cell_info,
71-
fem::DofTransformKernel<T> auto P0)
72+
fem::DofTransformKernel<T> auto P0,
73+
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
7274
{
7375
static_assert(entities.rank() == 1 or entities.rank() == 2);
7476

@@ -99,15 +101,16 @@ void tabulate_expression(
99101
else
100102
{
101103
std::int32_t entity = entities(e, 0);
104+
std::int32_t local_entity = entities(e, 1);
105+
std::uint8_t perm = perms.empty() ? 0 : perms(entity, local_entity);
102106
auto x_dofs = md::submdspan(x_dofmap, entity, md::full_extent);
103107
for (std::size_t i = 0; i < x_dofs.size(); ++i)
104108
{
105109
std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
106110
std::next(coord_dofs.begin(), 3 * i));
107111
}
108112
fn(values_local.data(), &coeffs(e, 0), constants.data(),
109-
coord_dofs.data(), &entities(e, 1), nullptr, nullptr);
110-
113+
coord_dofs.data(), &local_entity, &perm, nullptr);
111114
P0(values_local, cell_info, entity, size0);
112115
}
113116

@@ -183,10 +186,22 @@ void tabulate_expression(
183186
doftransform::transpose);
184187
}
185188
}
186-
189+
// An expression has no notion of requiring a facet permutation.
190+
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
191+
if constexpr (entities.rank() == 2)
192+
{
193+
mesh::CellType cell_type = mesh.topology()->cell_types()[0];
194+
int num_facets_per_cell
195+
= mesh::cell_num_entities(cell_type, mesh.topology()->dim() - 1);
196+
mesh.topology_mutable()->create_entity_permutations();
197+
const std::vector<std::uint8_t>& p
198+
= mesh.topology()->get_facet_permutations();
199+
facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
200+
num_facets_per_cell);
201+
}
187202
tabulate_expression<T, U>(values, fn, Xshape, value_size, num_argument_dofs,
188203
mesh.geometry().dofmaps().front(),
189204
mesh.geometry().x(), coeffs, constants, entities,
190-
cell_info, post_dof_transform);
205+
cell_info, post_dof_transform, facet_perms);
191206
}
192207
} // namespace dolfinx::fem::impl

cpp/dolfinx/fem/assembler.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2018-2025 Garth N. Wells
1+
// Copyright (C) 2018-2026 Garth N. Wells and Jørgen S. Dokken
22
//
33
// This file is part of DOLFINx (https://www.fenicsproject.org)
44
//
@@ -19,6 +19,7 @@
1919
#include <basix/mdspan.hpp>
2020
#include <cstdint>
2121
#include <dolfinx/common/types.h>
22+
#include <dolfinx/mesh/EntityMap.h>
2223
#include <memory>
2324
#include <optional>
2425
#include <span>
@@ -70,6 +71,12 @@ void tabulate_expression(
7071
std::pair<std::reference_wrapper<const FiniteElement<U>>, std::size_t>>
7172
element)
7273
{
74+
// Check that domain is the same as mesh of the expression
75+
if (e.coordinate_element_hash() != mesh.geometry().cmaps().front().hash())
76+
{
77+
throw std::runtime_error(
78+
"Expression was created on a different mesh. Cannot tabulate.");
79+
}
7380
auto [X, Xshape] = e.X();
7481
impl::tabulate_expression(values, e.kernel(), Xshape, e.value_size(), coeffs,
7582
constants, mesh, entities, element);
@@ -95,6 +102,13 @@ template <dolfinx::scalar T, std::floating_point U>
95102
void tabulate_expression(std::span<T> values, const fem::Expression<T, U>& e,
96103
const mesh::Mesh<U>& mesh, fem::MDSpan2 auto entities)
97104
{
105+
// Check that domain is the same as mesh of the expression
106+
if (e.coordinate_element_hash() != mesh.geometry().cmaps().front().hash())
107+
{
108+
throw std::runtime_error(
109+
"Expression was created on a different mesh. Cannot tabulate.");
110+
}
111+
98112
std::optional<
99113
std::pair<std::reference_wrapper<const FiniteElement<U>>, std::size_t>>
100114
element = std::nullopt;
@@ -115,12 +129,13 @@ void tabulate_expression(std::span<T> values, const fem::Expression<T, U>& e,
115129
std::vector<std::reference_wrapper<const Function<T, U>>> c;
116130
std::ranges::transform(coefficients, std::back_inserter(c),
117131
[](auto c) -> const Function<T, U>& { return *c; });
118-
fem::pack_coefficients(c, coffsets, entities, std::span(coeffs));
132+
fem::pack_coefficients(c, mesh, entities, e.entity_maps(), coffsets,
133+
std::span(coeffs));
119134
}
120135
std::vector<T> constants = fem::pack_constants(e);
121136

122137
tabulate_expression<T, U>(
123-
values, e, md::mdspan(coeffs.data(), entities.size(), cstride),
138+
values, e, md::mdspan(coeffs.data(), entities.extent(0), cstride),
124139
std::span<const T>(constants), mesh, entities, element);
125140
}
126141

0 commit comments

Comments
 (0)