DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
assemble_vector_impl.h
1// Copyright (C) 2018-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 "DirichletBC.h"
11#include "DofMap.h"
12#include "Form.h"
13#include "assemble_matrix_impl.h"
14#include "traits.h"
15#include "utils.h"
16#include <algorithm>
17#include <basix/mdspan.hpp>
18#include <cstdint>
19#include <dolfinx/common/IndexMap.h>
20#include <dolfinx/mesh/Geometry.h>
21#include <dolfinx/mesh/Mesh.h>
22#include <dolfinx/mesh/Topology.h>
23#include <functional>
24#include <memory>
25#include <optional>
26#include <span>
27#include <stdexcept>
28#include <type_traits>
29#include <vector>
30
31namespace dolfinx::fem
32{
33template <dolfinx::scalar T, std::floating_point U>
34class DirichletBC;
35}
36
37namespace dolfinx::fem::impl
38{
40using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
42
72template <typename V, std::floating_point U,
73 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
74 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
75void assemble_cells(
76 const fem::DofTransformKernel<T> auto& P0, V&& b, mdspan2_t x_dofmap,
77 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
78 std::span<const std::int32_t> cells, const DofMapPackCells auto& dofmap,
79 const FEkernel<T, U> auto& kernel, std::span<const T> constants,
80 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
81 std::span<const std::uint32_t> cell_info0, std::span<T> be_b,
82 std::span<U> cdofs_b)
83{
84 if (cells.empty())
85 return;
86
87 const auto [dmap, bs, cells0] = dofmap;
88 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
89 assert(be_b.size() >= bs * dmap.extent(1));
90 auto be = be_b.first(bs * dmap.extent(1));
91
92 // Iterate over active cells
93 for (std::size_t index = 0; index < cells.size(); ++index)
94 {
95 // Integration domain cell and test function cell
96 std::int32_t c = cells[index];
97 std::int32_t c0 = cells0[index];
98
99 // Get cell coordinates/geometry
100 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
101 for (std::size_t i = 0; i < x_dofs.size(); ++i)
102 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs_b.begin(), 3 * i));
103
104 // Tabulate vector for cell
105 std::ranges::fill(be, 0);
106 kernel(be.data(), &coeffs(index, 0), constants.data(), cdofs_b.data(),
107 nullptr, nullptr, nullptr);
108 P0(be, cell_info0, c0, 1);
109
110 // Scatter cell vector to 'global' vector array
111 auto dofs = md::submdspan(dmap, c0, md::full_extent);
112 for (std::size_t i = 0; i < dofs.size(); ++i)
113 for (int k = 0; k < bs; ++k)
114 b[bs * dofs[i] + k] += be[bs * i + k];
115 }
116}
117
157template <typename V, std::floating_point U,
158 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
159 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
160void assemble_entities(
161 const fem::DofTransformKernel<T> auto& P0, V&& b, mdspan2_t x_dofmap,
162 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
163 md::mdspan<const std::int32_t,
164 std::extents<std::size_t, md::dynamic_extent, 2>>
165 entities,
166 const DofMapPackEntities auto& dofmap, const FEkernel<T, U> auto& kernel,
167 std::span<const T> constants,
168 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
169 std::span<const std::uint32_t> cell_info0,
170 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
171 std::span<T> be_b, std::span<U> cdofs_b)
172{
173 if (entities.empty())
174 return;
175
176 const auto [dmap, bs, entities0] = dofmap;
177
178 const int num_dofs = dmap.extent(1);
179 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
180 assert(be_b.size() >= static_cast<std::size_t>(bs) * num_dofs);
181 auto be = be_b.first(bs * num_dofs);
182 assert(entities0.size() == entities.size());
183 for (std::size_t f = 0; f < entities.extent(0); ++f)
184 {
185 // Cell in the integration domain, local facet index relative to the
186 // integration domain cell, and cell in the test function mesh
187 std::int32_t cell = entities(f, 0);
188 std::int32_t local_entity = entities(f, 1);
189 std::int32_t cell0 = entities0(f, 0);
190
191 // Get cell coordinates/geometry
192 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
193 for (std::size_t i = 0; i < x_dofs.size(); ++i)
194 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs_b.begin(), 3 * i));
195
196 // Permutations
197 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
198
199 // Tabulate element vector
200 std::ranges::fill(be, 0);
201 kernel(be.data(), &coeffs(f, 0), constants.data(), cdofs_b.data(),
202 &local_entity, &perm, nullptr);
203 P0(be, cell_info0, cell0, 1);
204
205 // Add to global vector
206 auto dofs = md::submdspan(dmap, cell0, md::full_extent);
207 for (std::size_t i = 0; i < dofs.size(); ++i)
208 {
209 std::int32_t dof = bs * dofs[i];
210 std::int32_t offset = bs * i;
211 for (int k = 0; k < bs; ++k)
212 b[dof + k] += be[offset + k];
213 }
214 }
215}
216
249template <typename V, std::floating_point U,
250 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
251 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
252void assemble_interior_facets(
253 const fem::DofTransformKernel<T> auto& P0, V&& b, mdspan2_t x_dofmap,
254 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
255 md::mdspan<const std::int32_t,
256 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
257 facets,
258 const DofMapPackFacets auto& dofmap, const FEkernel<T, U> auto& kernel,
259 std::span<const T> constants,
260 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
261 md::dynamic_extent>>
262 coeffs,
263 std::span<const std::uint32_t> cell_info0,
264 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
265 std::span<T> be_b, std::span<U> cdofs_b)
266{
267 if (facets.empty())
268 return;
269
270 const auto [dmap, bs, facets0] = dofmap;
271
272 assert(cdofs_b.size() >= 2 * x_dofmap.extent(1) * 3);
273 auto cdofs0 = cdofs_b.first(x_dofmap.extent(1) * 3);
274 auto cdofs1 = cdofs_b.subspan(x_dofmap.extent(1) * 3, x_dofmap.extent(1) * 3);
275
276 const std::size_t dmap_size = dmap.extent(1);
277 assert(be_b.size() >= static_cast<std::size_t>(bs) * 2 * dmap_size);
278 auto be = be_b.first(bs * 2 * dmap_size);
279
280 assert(facets0.size() == facets.size());
281 for (std::size_t f = 0; f < facets.extent(0); ++f)
282 {
283 // Cells in integration domain and test function domain meshes
284 std::array<std::int32_t, 2> cells{facets(f, 0, 0), facets(f, 1, 0)};
285 std::array<std::int32_t, 2> cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
286
287 // Local facet indices
288 std::array<std::int32_t, 2> local_facet{facets(f, 0, 1), facets(f, 1, 1)};
289
290 // Get cell geometry
291 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
292 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
293 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
294 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
295 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
296 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
297
298 // Get dofmaps for cells. When integrating over interfaces between
299 // two domains, the test function might only be defined on one side,
300 // so we check which cells exist in the test function domain.
301 std::span dmap0 = cells0[0] >= 0 ? std::span(&dmap(cells0[0], 0), dmap_size)
302 : std::span<const std::int32_t>();
303 std::span dmap1 = cells0[1] >= 0 ? std::span(&dmap(cells0[1], 0), dmap_size)
304 : std::span<const std::int32_t>();
305
306 // Tabulate element vector
307 std::ranges::fill(be, 0);
308 std::array perm = perms.empty()
309 ? std::array<std::uint8_t, 2>{0, 0}
310 : std::array{perms(cells[0], local_facet[0]),
311 perms(cells[1], local_facet[1])};
312 kernel(be.data(), &coeffs(f, 0, 0), constants.data(), cdofs_b.data(),
313 local_facet.data(), perm.data(), nullptr);
314
315 if (cells0[0] >= 0)
316 P0(be, cell_info0, cells0[0], 1);
317 if (cells0[1] >= 0)
318 {
319 std::span sub_be(be.data() + bs * dmap_size, bs * dmap_size);
320 P0(sub_be, cell_info0, cells0[1], 1);
321 }
322
323 // Add element vector to global vector
324 for (std::size_t i = 0; i < dmap0.size(); ++i)
325 {
326 std::int32_t dof = bs * dmap0[i];
327 std::int32_t offset = bs * i;
328 for (int k = 0; k < bs; ++k)
329 b[dof + k] += be[offset + k];
330 }
331 for (std::size_t i = 0; i < dmap1.size(); ++i)
332 {
333 std::int32_t dof = bs * dmap1[i];
334 std::int32_t offset = bs * (i + dmap_size);
335 for (int k = 0; k < bs; ++k)
336 b[dof + k] += be[offset + k];
337 }
338 }
339}
340
361template <dolfinx::scalar T, std::floating_point U, typename V>
362 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
363void lift_bc(V&& b, const Form<T, U>& a, auto bs0, auto bs1,
364 std::span<const T> constants,
365 const std::map<std::pair<IntegralType, int>,
366 std::pair<std::span<const T>, int>>& coefficients,
367 std::span<const T> bc_values1,
368 std::span<const std::int8_t> bc_markers1, std::span<const T> x0,
369 T alpha)
370{
371 // Deduce runtime block sizes as fallback when compile-time sizes
372 // not given. The block size of the dofmap and indexmap is the same
373 // on all sub-topologies.
374 assert(bs0 == a.function_spaces()[0]->dofmaps().front()->bs());
375 assert(bs1 == a.function_spaces()[1]->dofmaps().front()->bs());
376
377 auto lifting_fn = [bs0, bs1, alpha, &b, &bc_values1, &bc_markers1,
378 &x0](auto rows, auto cols, auto Ae)
379 {
380 const std::size_t nc = cols.size() * bs1;
381 for (std::size_t i = 0; i < cols.size(); ++i)
382 {
383 for (int k = 0; k < bs1; ++k)
384 {
385 const std::int32_t ii = cols[i] * bs1 + k;
386 if (bc_markers1[ii])
387 {
388 const T x_bc = bc_values1[ii];
389 const T _x0 = x0.empty() ? 0 : x0[ii];
390 for (std::size_t j = 0; j < rows.size(); ++j)
391 {
392 for (int m = 0; m < bs0; ++m)
393 {
394 const std::int32_t jj = rows[j] * bs0 + m;
395 b[jj] -= Ae[(j * bs0 + m) * nc + (i * bs1 + k)] * alpha
396 * (x_bc - _x0);
397 }
398 }
399 }
400 }
401 }
402 };
403
404 // Use dolfinx::fem::impl::assemble_matrix assembler to work on the
405 // vector b. With LiftingMode=true, the kernel is only called on cells
406 // that have BC-constrained DOFs in the column space.
407 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
408 assert(mesh);
409 std::span x = mesh->geometry().x();
410 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> _x(
411 x.data(), x.size() / 3, 3);
412 impl::assemble_matrix<true>(lifting_fn, a, _x, constants, coefficients, {},
413 bc_markers1);
414}
415
424template <typename V, std::floating_point U,
425 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
426 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
427void assemble_vector(
428 V&& b, const Form<T, U>& L,
429 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
430 std::span<const T> constants,
431 const std::map<std::pair<IntegralType, int>,
432 std::pair<std::span<const T>, int>>& coefficients)
433{
434 // Integration domain mesh
435 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
436 assert(mesh);
437
438 // Test function mesh
439 auto mesh0 = L.function_spaces().at(0)->mesh();
440 assert(mesh0);
441
442 const int num_cell_types = mesh->topology()->cell_types().size();
443 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
444 {
445 // Geometry dofmap and data
446 mdspan2_t x_dofmap = mesh->geometry().dofmaps().at(cell_type_idx);
447
448 // Get dofmap data
449 assert(L.function_spaces().at(0));
450 auto element = L.function_spaces().at(0)->elements(cell_type_idx);
451 assert(element);
452 std::shared_ptr<const fem::DofMap> dofmap
453 = L.function_spaces().at(0)->dofmaps().at(cell_type_idx);
454 assert(dofmap);
455 auto dofs = dofmap->map();
456 const int bs = dofmap->bs();
457
458 // Buffers reused across all integral kernels for this cell type,
459 // sized for the worst case (interior facets, which touch two cells).
460 std::vector<T> be_buffer(2 * bs * dofs.extent(1));
461 std::vector<U> cdofs_buffer(2 * 3 * x_dofmap.extent(1));
462 std::span be_b(be_buffer);
463 std::span cdofs_b(cdofs_buffer);
464
465 const fem::DofTransformKernel<T> auto& P0
466 = element->template dof_transformation_fn<T>(doftransform::standard);
467
468 std::span<const std::uint32_t> cell_info0;
469 if (element->needs_dof_transformations() or L.needs_facet_permutations())
470 {
471 mesh0->topology_mutable()->create_entity_permutations();
472 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
473 }
474
475 for (int i = 0; i < L.num_integrals(IntegralType::cell, 0); ++i)
476 {
477 auto fn = L.kernel(IntegralType::cell, i, cell_type_idx);
478 assert(fn);
479 std::span cells = L.domain(IntegralType::cell, i, cell_type_idx);
480 std::span cells0 = L.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
481 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
482 assert(cells.size() * cstride == coeffs.size());
483 if (bs == 1)
484 {
485 impl::assemble_cells(
486 P0, b, x_dofmap, x, cells,
487 std::tuple{dofs, std::integral_constant<int, 1>{}, cells0}, fn,
488 constants, md::mdspan(coeffs.data(), cells.size(), cstride),
489 cell_info0, be_b, cdofs_b);
490 }
491 else if (bs == 3)
492 {
493 impl::assemble_cells(
494 P0, b, x_dofmap, x, cells,
495 std::tuple{dofs, std::integral_constant<int, 3>(), cells0}, fn,
496 constants, md::mdspan(coeffs.data(), cells.size(), cstride),
497 cell_info0, be_b, cdofs_b);
498 }
499 else
500 {
501 impl::assemble_cells(P0, b, x_dofmap, x, cells,
502 std::tuple{dofs, bs, cells0}, fn, constants,
503 md::mdspan(coeffs.data(), cells.size(), cstride),
504 cell_info0, be_b, cdofs_b);
505 }
506 }
507
508 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
509 if (L.needs_facet_permutations())
510 {
511 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
512 int num_facets_per_cell
513 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
514 mesh->topology_mutable()->create_entity_permutations();
515 const std::vector<std::uint8_t>& p
516 = mesh->topology()->get_facet_permutations();
517 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
518 num_facets_per_cell);
519 }
520
521 using mdspanx2_t
522 = md::mdspan<const std::int32_t,
523 md::extents<std::size_t, md::dynamic_extent, 2>>;
524 using mdspanx22_t
525 = md::mdspan<const std::int32_t,
526 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
527 using mdspanx2x_t
528 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
529 md::dynamic_extent>>;
530
531 for (int i = 0; i < L.num_integrals(IntegralType::interior_facet, 0); ++i)
532 {
533 auto fn = L.kernel(IntegralType::interior_facet, i, 0);
534 assert(fn);
535 auto& [coeffs, cstride]
536 = coefficients.at({IntegralType::interior_facet, i});
537 std::span facets = L.domain(IntegralType::interior_facet, i, 0);
538 std::span facets1 = L.domain_arg(IntegralType::interior_facet, 0, i, 0);
539 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
540
541 mdspanx22_t facets_mdspan(facets.data(), facets.size() / 4, 2, 2);
542 mdspanx22_t facets1_mdspan(facets1.data(), facets1.size() / 4, 2, 2);
543 if (bs == 1)
544 {
545 impl::assemble_interior_facets(
546 P0, b, x_dofmap, x, facets_mdspan,
547 std::tuple{dofs, std::integral_constant<int, 1>{}, facets1_mdspan},
548 fn, constants,
549 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
550 cell_info0, facet_perms, be_b, cdofs_b);
551 }
552 else if (bs == 3)
553 {
554 impl::assemble_interior_facets(
555 P0, b, x_dofmap, x, facets_mdspan,
556 std::tuple{dofs, std::integral_constant<int, 3>{}, facets1_mdspan},
557 fn, constants,
558 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
559 cell_info0, facet_perms, be_b, cdofs_b);
560 }
561 else
562 {
563 impl::assemble_interior_facets(
564 P0, b, x_dofmap, x, facets_mdspan,
565 std::tuple{dofs, bs, facets1_mdspan}, fn, constants,
566 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
567 cell_info0, facet_perms, be_b, cdofs_b);
568 }
569 }
570
571 for (auto itg_type : {fem::IntegralType::exterior_facet,
573 {
574 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
576 ? facet_perms
577 : md::mdspan<const std::uint8_t,
578 md::dextents<std::size_t, 2>>{};
579 for (int i = 0; i < L.num_integrals(itg_type, 0); ++i)
580 {
581 auto fn = L.kernel(itg_type, i, 0);
582 assert(fn);
583 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
584 std::span e = L.domain(itg_type, i, 0);
585 mdspanx2_t entities(e.data(), e.size() / 2, 2);
586 std::span e1 = L.domain_arg(itg_type, 0, i, 0);
587 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
588 assert((entities.size() / 2) * cstride == coeffs.size());
589 if (bs == 1)
590 {
591 impl::assemble_entities(
592 P0, b, x_dofmap, x, entities,
593 std::tuple{dofs, std::integral_constant<int, 1>{}, entities1}, fn,
594 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
595 cell_info0, perms, be_b, cdofs_b);
596 }
597 else if (bs == 3)
598 {
599 impl::assemble_entities(
600 P0, b, x_dofmap, x, entities,
601 std::tuple{dofs, std::integral_constant<int, 3>{}, entities1}, fn,
602 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
603 cell_info0, perms, be_b, cdofs_b);
604 }
605 else
606 {
607 impl::assemble_entities(
608 P0, b, x_dofmap, x, entities, std::tuple{dofs, bs, entities1}, fn,
609 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
610 cell_info0, perms, be_b, cdofs_b);
611 }
612 }
613 }
614 }
615}
616
623template <typename V, std::floating_point U,
624 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
625 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
626void assemble_vector(
627 V&& b, const Form<T, U>& L, std::span<const T> constants,
628 const std::map<std::pair<IntegralType, int>,
629 std::pair<std::span<const T>, int>>& coefficients)
630{
631 using mdspanx3_t
632 = md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>>;
633
634 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
635 assert(mesh);
636 auto x = mesh->geometry().x();
637 impl::assemble_vector(b, L, mdspanx3_t(x.data(), x.size() / 3, 3), constants,
638 coefficients);
639}
640} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Definition DirichletBC.h:258
Functions supporting finite element method operations.
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
Finite element method functionality.
Definition assemble_expression_impl.h:23
@ standard
Standard.
Definition FiniteElement.h:27
@ vertex
Vertex.
Definition Form.h:45
@ interior_facet
Interior facet.
Definition Form.h:44
@ ridge
Ridge.
Definition Form.h:46
@ cell
Cell.
Definition Form.h:42
@ exterior_facet
Exterior facet.
Definition Form.h:43
CellType
Cell type identifier.
Definition cell_types.h:22
int cell_num_entities(CellType type, int dim)
Number of entities of dimension.
Definition cell_types.cpp:92