DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
assemble_scalar_impl.h
1// Copyright (C) 2019-2025 Garth N. Wells and Paul T. Kühner
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "Constant.h"
10#include "Form.h"
11#include "FunctionSpace.h"
12#include "traits.h"
13#include <algorithm>
14#include <basix/mdspan.hpp>
15#include <concepts>
16#include <dolfinx/common/IndexMap.h>
17#include <dolfinx/mesh/Geometry.h>
18#include <dolfinx/mesh/Mesh.h>
19#include <dolfinx/mesh/Topology.h>
20#include <memory>
21#include <vector>
22
23namespace dolfinx::fem::impl
24{
45template <dolfinx::scalar T, MDSpan2Int32 XD, std::floating_point U>
46T assemble_cells_scalar(
47 GeometryPack<XD, U> geometry, const IndexList auto& cells,
48 const FEkernel<T, U> auto& kernel, std::span<const T> constants,
49 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
50 ScratchBuffer<U> auto cdofs_b)
51{
52 T value(0);
53 if (std::ranges::empty(cells))
54 return value;
55
56 const auto x_dofmap = geometry.dofmap;
57 const auto ndofs_x = x_dofmap.extent(1);
58 assert(cdofs_b.size() == 3 * static_cast<std::size_t>(ndofs_x));
59
60 const T* coeffs_data = coeffs.data_handle();
61 const auto cstride = coeffs.extent(1);
62
63 // Iterate over all cells
64 const std::size_t num_cells = std::ranges::size(cells);
65 for (std::size_t index = 0; index < num_cells; ++index)
66 {
67 std::int32_t c = cells[index];
68
69 gather_cell_coordinates(geometry, c, cdofs_b.data());
70 kernel(&value, coeffs_data + index * cstride, constants.data(),
71 cdofs_b.data(), nullptr, nullptr, nullptr);
72 }
73
74 return value;
75}
76
108template <dolfinx::scalar T, MDSpan2Int32 XD, std::floating_point U>
109T assemble_entities_scalar(
110 GeometryPack<XD, U> geometry,
111 md::mdspan<const std::int32_t,
112 md::extents<std::size_t, md::dynamic_extent, 2>>
113 entities,
114 const FEkernel<T, U> auto& kernel, std::span<const T> constants,
115 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
116 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
117 ScratchBuffer<U> auto cdofs_b)
118{
119 T value(0);
120 if (entities.empty())
121 return value;
122
123 const auto x_dofmap = geometry.dofmap;
124 const auto ndofs_x = x_dofmap.extent(1);
125 assert(cdofs_b.size() == 3 * static_cast<std::size_t>(ndofs_x));
126
127 const T* coeffs_data = coeffs.data_handle();
128 const auto cstride = coeffs.extent(1);
129
130 // Iterate over all facets
131 for (std::size_t f = 0; f < entities.extent(0); ++f)
132 {
133 std::int32_t cell = entities(f, 0);
134 std::int32_t local_entity = entities(f, 1);
135
136 gather_cell_coordinates(geometry, cell, cdofs_b.data());
137
138 // Permutations
139 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
140 kernel(&value, coeffs_data + f * cstride, constants.data(), cdofs_b.data(),
141 &local_entity, &perm, nullptr);
142 }
143
144 return value;
145}
146
170template <dolfinx::scalar T, MDSpan2Int32 XD, std::floating_point U>
171T assemble_interior_facets_scalar(
172 GeometryPack<XD, U> geometry,
173 md::mdspan<const std::int32_t,
174 md::extents<std::size_t, md::dynamic_extent, 2, 2>>
175 facets,
176 const FEkernel<T, U> auto& kernel, std::span<const T> constants,
177 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
178 md::dynamic_extent>>
179 coeffs,
180 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
181 ScratchBuffer<U> auto cdofs_b)
182{
183 T value(0);
184 if (facets.empty())
185 return value;
186
187 // Create data structures used in assembly
188 const auto x_dofmap = geometry.dofmap;
189 const auto ndofs_x = x_dofmap.extent(1);
190 assert(cdofs_b.size() == 2 * 3 * static_cast<std::size_t>(ndofs_x));
191 U* cdofs0 = cdofs_b.data();
192 U* cdofs1 = cdofs_b.data() + 3 * ndofs_x;
193
194 const T* coeffs_data = coeffs.data_handle();
195 const auto cstride = 2 * coeffs.extent(2);
196
197 // Iterate over all facets
198 for (std::size_t f = 0; f < facets.extent(0); ++f)
199 {
200 std::array cells = {facets(f, 0, 0), facets(f, 1, 0)};
201 std::array local_facet = {facets(f, 0, 1), facets(f, 1, 1)};
202
203 gather_cell_coordinates(geometry, cells[0], cdofs0);
204 gather_cell_coordinates(geometry, cells[1], cdofs1);
205
206 std::array perm = perms.empty()
207 ? std::array<std::uint8_t, 2>{0, 0}
208 : std::array{perms(cells[0], local_facet[0]),
209 perms(cells[1], local_facet[1])};
210 kernel(&value, coeffs_data + f * cstride, constants.data(), cdofs_b.data(),
211 local_facet.data(), perm.data(), nullptr);
212 }
213
214 return value;
215}
216
231template <dolfinx::scalar T, std::floating_point U>
232T assemble_scalar(
233 const fem::Form<T, U>& M, mdspan2_t x_dofmap,
234 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
235 std::span<const T> constants,
236 const std::map<std::pair<IntegralType, int>,
237 std::pair<std::span<const T>, int>>& coefficients,
238 std::size_t cell_type_idx)
239{
240 std::shared_ptr<const mesh::Mesh<U>> mesh = M.mesh();
241 assert(mesh);
242
243 // Sized for the worst case (interior facets, which touch two cells).
244 // The kernels require an exactly-sized buffer, so the one-cell
245 // integrals get the leading half.
246 std::vector<U> cdofs_b(2 * 3 * x_dofmap.extent(1));
247 std::span cdofs_b1 = std::span(cdofs_b).first(3 * x_dofmap.extent(1));
248 GeometryPack geometry{x_dofmap, x};
249
250 T value = 0;
251 for (int i = 0; i < M.num_integrals(IntegralType::cell, cell_type_idx); ++i)
252 {
253 auto fn = M.kernel(IntegralType::cell, i, cell_type_idx);
254 assert(fn);
255 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
256 std::span<const std::int32_t> cells
257 = M.domain(IntegralType::cell, i, cell_type_idx);
258 assert(cells.size() * cstride == coeffs.size());
259 value += impl::assemble_cells_scalar(
260 geometry, cells, fn, constants,
261 md::mdspan(coeffs.data(), cells.size(), cstride), cdofs_b1);
262 }
263
264 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
265 if (M.needs_facet_permutations())
266 {
267 facet_perms = impl::entity_permutations(
268 *mesh->topology_mutable(), IntegralType::interior_facet,
269 mesh->topology()->cell_types()[cell_type_idx]);
270 }
271
272 for (int i = 0;
273 i < M.num_integrals(IntegralType::interior_facet, cell_type_idx); ++i)
274 {
275 auto fn = M.kernel(IntegralType::interior_facet, i, cell_type_idx);
276 assert(fn);
277 auto& [coeffs, cstride]
278 = coefficients.at({IntegralType::interior_facet, i});
279 std::span facets = M.domain(IntegralType::interior_facet, i, cell_type_idx);
280
281 constexpr std::size_t num_adjacent_cells = 2;
282 // Two values per each adj. cell (cell index and local facet index).
283 constexpr std::size_t shape1 = 2 * num_adjacent_cells;
284
285 assert((facets.size() / shape1) * 2 * cstride == coeffs.size());
286 value += impl::assemble_interior_facets_scalar(
287 geometry,
288 md::mdspan<const std::int32_t,
289 md::extents<std::size_t, md::dynamic_extent, 2, 2>>(
290 facets.data(), facets.size() / shape1, 2, 2),
291 fn, constants,
292 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
293 md::dynamic_extent>>(
294 coeffs.data(), facets.size() / shape1, 2, cstride),
295 facet_perms, std::span(cdofs_b));
296 }
297
298 for (auto itg_type : {fem::IntegralType::exterior_facet,
300 {
301 const int num_itg = M.num_integrals(itg_type, cell_type_idx);
302 if (num_itg == 0)
303 continue;
304
305 // Each integral type is over entities of a different
306 // codimension, so only the permutations this form actually
307 // integrates over are computed.
308 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms;
309 if (M.needs_facet_permutations())
310 {
311 perms = impl::entity_permutations(
312 *mesh->topology_mutable(), itg_type,
313 mesh->topology()->cell_types()[cell_type_idx]);
314 }
315
316 for (int i = 0; i < num_itg; ++i)
317 {
318 auto fn = M.kernel(itg_type, i, cell_type_idx);
319 assert(fn);
320 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
321
322 std::span entities = M.domain(itg_type, i, cell_type_idx);
323
324 // Two values per each adj. cell (cell index and local entity index).
325 assert((entities.size() / 2) * cstride == coeffs.size());
326 value += impl::assemble_entities_scalar(
327 geometry,
328 md::mdspan<const std::int32_t,
329 md::extents<std::size_t, md::dynamic_extent, 2>>(
330 entities.data(), entities.size() / 2, 2),
331 fn, constants,
332 md::mdspan(coeffs.data(), entities.size() / 2, cstride), perms,
333 cdofs_b1);
334 }
335 }
336
337 return value;
338}
339
340} // namespace dolfinx::fem::impl
void cells(la::SparsityPattern &pattern, const std::pair< R0, R1 > &cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.h:37
@ vertex
Vertex.
Definition Form.h:47
@ interior_facet
Interior facet.
Definition Form.h:46
@ ridge
Ridge.
Definition Form.h:48
@ cell
Cell.
Definition Form.h:44
@ exterior_facet
Exterior facet.
Definition Form.h:45