Skip to content

Commit 547df9f

Browse files
jorgensdgarth-wellsmichalhaberaschnellerhase
authored
Unification of one-sided sub-entity integrals + ridge integrals (#3900)
* Unify assemblers of vertex, exterior facet and ridge integrals (new) * Assemble scalar added * Use vertex_perms * Fix docs * Fix correct usage of perms * add 3D vector test * Remove code duplication * Huge duplication removal * Remove more duplication * Even more duplication * Use real dtype for basix element * Use switch * Use more switches * Add default * add vertex and ridge matrix integrals + lifting * Adapt lifting test to check matrix and lifting of ridge and vertex integrals * Merge main into branch * Improve name of inner-most assembly function * Apply suggestions from code review Co-authored-by: Garth N. Wells <gnw20@cam.ac.uk> * Rename "IntegralType::exterior_facet" to "IntegralType::facet" + address other comments by Garth * Fix spacing * Use renaming * Simplify * Rename lifting * Remove outdated comment * Fix typo * Explicit lambda function declaration * Revert IntegralType::facet backt to IntegralType::exterior_facet * More reverting * More reverting * Revert spacing * Revert spacing * Further reverting * Yet another revert * Apply suggestions from code review Co-authored-by: Michal Habera <michal.habera@gmail.com> * Apply suggestions from code review Co-authored-by: Paul T. Kühner <56360279+schnellerhase@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Paul T. Kühner <56360279+schnellerhase@users.noreply.github.com> --------- Co-authored-by: Garth N. Wells <gnw20@cam.ac.uk> Co-authored-by: Michal Habera <michal.habera@gmail.com> Co-authored-by: Paul T. Kühner <56360279+schnellerhase@users.noreply.github.com>
1 parent 443d2b1 commit 547df9f

11 files changed

Lines changed: 682 additions & 569 deletions

File tree

cpp/dolfinx/common/sort.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ struct __radix_sort
6767
/// @code
6868
/// std::array<std::int16_t, 3> a{2, 3, 1};
6969
/// std::array<std::int16_t, 3> i{0, 1, 2};
70-
/// dolfixn::radix_sort(i, [&](auto i){ return a[i]; }); // yields i = {2, 0,
70+
/// dolfinx::radix_sort(i, [&](auto i){ return a[i]; }); // yields i = {2, 0,
7171
/// 1} and a[i] = {1, 2, 3};
7272
/// @endcode
7373
/// @tparam R Type of range to be sorted.

cpp/dolfinx/fem/Form.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ class Function;
3838
enum class IntegralType : std::int8_t
3939
{
4040
cell = 0, ///< Cell
41-
exterior_facet = 1, ///< Exterior facet
41+
exterior_facet = 1, ///< Facet
4242
interior_facet = 2, ///< Interior facet
43-
vertex = 3 ///< Vertex
43+
vertex = 3, ///< Vertex
44+
ridge = 4 ///< Ridge
4445
};
4546

4647
/// @brief Represents integral data, containing the kernel, and a list

cpp/dolfinx/fem/assemble_matrix_impl.h

Lines changed: 72 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,24 @@ void assemble_cells_matrix(
156156
}
157157
}
158158

159-
/// @brief Execute kernel over exterior facets and accumulate result in
160-
/// a matrix.
159+
/// @brief Execute kernel over entities of codimension ≥ 1 and accumulate result
160+
/// in a matrix.
161+
///
162+
/// Each entity is represented by (i) a cell that the entity is attached to
163+
/// and (ii) the local index of the entity with respect to the cell. The
164+
/// kernel is executed for each entity. The kernel can access data
165+
/// (e.g., coefficients, basis functions) associated with the attached cell.
166+
/// However, entities may be attached to more than one cell. This function
167+
/// therefore computes 'one-sided' integrals, i.e. evaluates integrals as seen
168+
/// from cell used to define the entity.
161169
///
162170
/// @tparam T Matrix/form scalar type.
163171
/// @param[in] mat_set Function that accumulates computed entries into a
164172
/// matrix.
165173
/// @param[in] x_dofmap Dofmap for the mesh geometry.
166174
/// @param[in] x Mesh geometry (coordinates).
167-
/// @param[in] facets Facet indices (in the integration domain mesh) to
168-
/// execute the kernel over.
175+
/// @param[in] entities Integration entities (in the integration domain mesh) to
176+
/// execute the kernel over. These are pairs (cell, local entity index)
169177
/// @param[in] dofmap0 Test function (row) degree-of-freedom data
170178
/// holding the (0) dofmap, (1) dofmap block size and (2) dofmap cell
171179
/// indices.
@@ -188,17 +196,17 @@ void assemble_cells_matrix(
188196
/// function mesh.
189197
/// @param[in] cell_info1 Cell permutation information for the trial
190198
/// function mesh.
191-
/// @param[in] perms Facet permutation integer. Empty if facet
199+
/// @param[in] perms Entity permutation integer. Empty if entity
192200
/// permutations are not required.
193201
template <dolfinx::scalar T>
194-
void assemble_exterior_facets(
202+
void assemble_entities(
195203
la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
196204
md::mdspan<const scalar_value_t<T>,
197205
md::extents<std::size_t, md::dynamic_extent, 3>>
198206
x,
199207
md::mdspan<const std::int32_t,
200208
std::extents<std::size_t, md::dynamic_extent, 2>>
201-
facets,
209+
entities,
202210
std::tuple<mdspan2_t, int,
203211
md::mdspan<const std::int32_t,
204212
std::extents<std::size_t, md::dynamic_extent, 2>>>
@@ -215,11 +223,11 @@ void assemble_exterior_facets(
215223
std::span<const std::uint32_t> cell_info1,
216224
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
217225
{
218-
if (facets.empty())
226+
if (entities.empty())
219227
return;
220228

221-
const auto [dmap0, bs0, facets0] = dofmap0;
222-
const auto [dmap1, bs1, facets1] = dofmap1;
229+
const auto [dmap0, bs0, entities0] = dofmap0;
230+
const auto [dmap1, bs1, entities1] = dofmap1;
223231

224232
// Data structures used in assembly
225233
std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
@@ -228,30 +236,30 @@ void assemble_exterior_facets(
228236
const int ndim0 = bs0 * num_dofs0;
229237
const int ndim1 = bs1 * num_dofs1;
230238
std::vector<T> Ae(ndim0 * ndim1);
231-
assert(facets0.size() == facets.size());
232-
assert(facets1.size() == facets.size());
233-
for (std::size_t f = 0; f < facets.extent(0); ++f)
239+
assert(entities0.size() == entities.size());
240+
assert(entities1.size() == entities.size());
241+
for (std::size_t f = 0; f < entities.extent(0); ++f)
234242
{
235-
// Cell in the integration domain, local facet index relative to the
243+
// Cell in the integration domain, local entity index relative to the
236244
// integration domain cell, and cells in the test and trial function
237245
// meshes
238-
std::int32_t cell = facets(f, 0);
239-
std::int32_t local_facet = facets(f, 1);
240-
std::int32_t cell0 = facets0(f, 0);
241-
std::int32_t cell1 = facets1(f, 0);
246+
std::int32_t cell = entities(f, 0);
247+
std::int32_t local_entity = entities(f, 1);
248+
std::int32_t cell0 = entities0(f, 0);
249+
std::int32_t cell1 = entities1(f, 0);
242250

243251
// Get cell coordinates/geometry
244252
auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
245253
for (std::size_t i = 0; i < x_dofs.size(); ++i)
246254
std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
247255

248256
// Permutations
249-
std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_facet);
257+
std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
250258

251259
// Tabulate tensor
252260
std::ranges::fill(Ae, 0);
253261
kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
254-
&local_facet, &perm, nullptr);
262+
&local_entity, &perm, nullptr);
255263
P0(Ae, cell_info0, cell0, ndim1);
256264
P1T(Ae, cell_info1, cell1, ndim0);
257265

@@ -605,7 +613,7 @@ void assemble_matrix(
605613
cell_info0, cell_info1);
606614
}
607615

608-
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms;
616+
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
609617
if (a.needs_facet_permutations())
610618
{
611619
mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
@@ -614,40 +622,8 @@ void assemble_matrix(
614622
mesh->topology_mutable()->create_entity_permutations();
615623
const std::vector<std::uint8_t>& p
616624
= mesh->topology()->get_facet_permutations();
617-
perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
618-
num_facets_per_cell);
619-
}
620-
621-
for (int i = 0;
622-
i < a.num_integrals(IntegralType::exterior_facet, cell_type_idx); ++i)
623-
{
624-
if (num_cell_types > 1)
625-
{
626-
throw std::runtime_error("Exterior facet integrals with mixed "
627-
"topology aren't supported yet");
628-
}
629-
630-
using mdspanx2_t
631-
= md::mdspan<const std::int32_t,
632-
md::extents<std::size_t, md::dynamic_extent, 2>>;
633-
634-
auto fn = a.kernel(IntegralType::exterior_facet, i, 0);
635-
assert(fn);
636-
auto& [coeffs, cstride]
637-
= coefficients.at({IntegralType::exterior_facet, i});
638-
639-
std::span f = a.domain(IntegralType::exterior_facet, i, 0);
640-
mdspanx2_t facets(f.data(), f.size() / 2, 2);
641-
std::span f0 = a.domain_arg(IntegralType::exterior_facet, 0, i, 0);
642-
mdspanx2_t facets0(f0.data(), f0.size() / 2, 2);
643-
std::span f1 = a.domain_arg(IntegralType::exterior_facet, 1, i, 0);
644-
mdspanx2_t facets1(f1.data(), f1.size() / 2, 2);
645-
assert((facets.size() / 2) * cstride == coeffs.size());
646-
impl::assemble_exterior_facets(
647-
mat_set, x_dofmap, x, facets, {dofs0, bs0, facets0}, P0,
648-
{dofs1, bs1, facets1}, P1T, bc0, bc1, fn,
649-
md::mdspan(coeffs.data(), facets.extent(0), cstride), constants,
650-
cell_info0, cell_info1, perms);
625+
facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
626+
num_facets_per_cell);
651627
}
652628

653629
for (int i = 0;
@@ -685,7 +661,47 @@ void assemble_matrix(
685661
mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
686662
P1T, bc0, bc1, fn,
687663
mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride), constants,
688-
cell_info0, cell_info1, perms);
664+
cell_info0, cell_info1, facet_perms);
665+
}
666+
667+
for (auto itg_type : {fem::IntegralType::exterior_facet,
668+
fem::IntegralType::vertex, fem::IntegralType::ridge})
669+
{
670+
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
671+
= itg_type == fem::IntegralType::exterior_facet
672+
? facet_perms
673+
: md::mdspan<const std::uint8_t,
674+
md::dextents<std::size_t, 2>>{};
675+
676+
for (int i = 0; i < a.num_integrals(itg_type, cell_type_idx); ++i)
677+
{
678+
if (num_cell_types > 1)
679+
{
680+
throw std::runtime_error("Exterior facet integrals with mixed "
681+
"topology aren't supported yet");
682+
}
683+
684+
using mdspanx2_t
685+
= md::mdspan<const std::int32_t,
686+
md::extents<std::size_t, md::dynamic_extent, 2>>;
687+
688+
auto fn = a.kernel(itg_type, i, 0);
689+
assert(fn);
690+
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
691+
692+
std::span e = a.domain(itg_type, i, 0);
693+
mdspanx2_t entities(e.data(), e.size() / 2, 2);
694+
std::span e0 = a.domain_arg(itg_type, 0, i, 0);
695+
mdspanx2_t entities0(e0.data(), e0.size() / 2, 2);
696+
std::span e1 = a.domain_arg(itg_type, 1, i, 0);
697+
mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
698+
assert((entities.size() / 2) * cstride == coeffs.size());
699+
impl::assemble_entities(
700+
mat_set, x_dofmap, x, entities, {dofs0, bs0, entities0}, P0,
701+
{dofs1, bs1, entities1}, P1T, bc0, bc1, fn,
702+
md::mdspan(coeffs.data(), entities.extent(0), cstride), constants,
703+
cell_info0, cell_info1, perms);
704+
}
689705
}
690706
}
691707
}

0 commit comments

Comments
 (0)