DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
assemble_vector_impl.h
1// Copyright (C) 2018-2021 Garth N. Wells
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 "traits.h"
14#include "utils.h"
15#include <algorithm>
16#include <basix/mdspan.hpp>
17#include <cstdint>
18#include <dolfinx/common/IndexMap.h>
19#include <dolfinx/mesh/Geometry.h>
20#include <dolfinx/mesh/Mesh.h>
21#include <dolfinx/mesh/Topology.h>
22#include <functional>
23#include <memory>
24#include <optional>
25#include <span>
26#include <vector>
27
28namespace dolfinx::fem
29{
30template <dolfinx::scalar T, std::floating_point U>
31class DirichletBC;
32
33}
34namespace dolfinx::fem::impl
35{
37using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
39
79template <dolfinx::scalar T, int _bs0 = -1, int _bs1 = -1>
80void _lift_bc_cells(
81 std::span<T> b, mdspan2_t x_dofmap,
82 md::mdspan<const scalar_value_t<T>,
83 md::extents<std::size_t, md::dynamic_extent, 3>>
84 x,
85 FEkernel<T> auto kernel, std::span<const std::int32_t> cells,
86 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
87 fem::DofTransformKernel<T> auto P0,
88 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
89 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
90 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
91 std::span<const std::uint32_t> cell_info0,
92 std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
93 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha)
94{
95 if (cells.empty())
96 return;
97
98 const auto [dmap0, bs0, cells0] = dofmap0;
99 const auto [dmap1, bs1, cells1] = dofmap1;
100 assert(_bs0 < 0 or _bs0 == bs0);
101 assert(_bs1 < 0 or _bs1 == bs1);
102
103 // Data structures used in bc application
104 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
105 std::vector<T> Ae, be;
106 assert(cells0.size() == cells.size());
107 assert(cells1.size() == cells.size());
108 for (std::size_t index = 0; index < cells.size(); ++index)
109 {
110 // Cell index in integration domain mesh, test function mesh, and trial
111 // function mesh
112 std::int32_t c = cells[index];
113 std::int32_t c0 = cells0[index];
114 std::int32_t c1 = cells1[index];
115
116 // Get dof maps for cell
117 auto dofs1 = md::submdspan(dmap1, c1, md::full_extent);
118
119 // Check if bc is applied to cell
120 bool has_bc = false;
121 for (std::size_t j = 0; j < dofs1.size(); ++j)
122 {
123 if constexpr (_bs1 > 0)
124 {
125 for (int k = 0; k < _bs1; ++k)
126 {
127 assert(_bs1 * dofs1[j] + k < (int)bc_markers1.size());
128 if (bc_markers1[_bs1 * dofs1[j] + k])
129 {
130 has_bc = true;
131 break;
132 }
133 }
134 }
135 else
136 {
137 for (int k = 0; k < bs1; ++k)
138 {
139 assert(bs1 * dofs1[j] + k < (int)bc_markers1.size());
140 if (bc_markers1[bs1 * dofs1[j] + k])
141 {
142 has_bc = true;
143 break;
144 }
145 }
146 }
147 }
148
149 if (!has_bc)
150 continue;
151
152 // Get cell coordinates/geometry
153 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
154 for (std::size_t i = 0; i < x_dofs.size(); ++i)
155 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
156
157 // Size data structure for assembly
158 auto dofs0 = md::submdspan(dmap0, c0, md::full_extent);
159
160 const int num_rows = bs0 * dofs0.size();
161 const int num_cols = bs1 * dofs1.size();
162
163 Ae.resize(num_rows * num_cols);
164 std::ranges::fill(Ae, 0);
165 kernel(Ae.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
166 nullptr, nullptr, nullptr);
167 P0(Ae, cell_info0, c0, num_cols);
168 P1T(Ae, cell_info1, c1, num_rows);
169
170 // Size data structure for assembly
171 be.resize(num_rows);
172 std::ranges::fill(be, 0);
173 for (std::size_t j = 0; j < dofs1.size(); ++j)
174 {
175 if constexpr (_bs1 > 0)
176 {
177 for (int k = 0; k < _bs1; ++k)
178 {
179 const std::int32_t jj = _bs1 * dofs1[j] + k;
180 assert(jj < (int)bc_markers1.size());
181 if (bc_markers1[jj])
182 {
183 const T bc = bc_values1[jj];
184 const T _x0 = x0.empty() ? 0 : x0[jj];
185 // const T _x0 = 0;
186 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
187 for (int m = 0; m < num_rows; ++m)
188 be[m] -= Ae[m * num_cols + _bs1 * j + k] * alpha * (bc - _x0);
189 }
190 }
191 }
192 else
193 {
194 for (int k = 0; k < bs1; ++k)
195 {
196 const std::int32_t jj = bs1 * dofs1[j] + k;
197 assert(jj < (int)bc_markers1.size());
198 if (bc_markers1[jj])
199 {
200 const T bc = bc_values1[jj];
201 const T _x0 = x0.empty() ? 0 : x0[jj];
202 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
203 for (int m = 0; m < num_rows; ++m)
204 be[m] -= Ae[m * num_cols + bs1 * j + k] * alpha * (bc - _x0);
205 }
206 }
207 }
208 }
209
210 for (std::size_t i = 0; i < dofs0.size(); ++i)
211 {
212 if constexpr (_bs0 > 0)
213 {
214 for (int k = 0; k < _bs0; ++k)
215 b[_bs0 * dofs0[i] + k] += be[_bs0 * i + k];
216 }
217 else
218 {
219 for (int k = 0; k < bs0; ++k)
220 b[bs0 * dofs0[i] + k] += be[bs0 * i + k];
221 }
222 }
223 }
224}
225
263template <dolfinx::scalar T>
264void _lift_bc_exterior_facets(
265 std::span<T> b, mdspan2_t x_dofmap,
266 md::mdspan<const scalar_value_t<T>,
267 md::extents<std::size_t, md::dynamic_extent, 3>>
268 x,
269 FEkernel<T> auto kernel,
270 md::mdspan<const std::int32_t,
271 md::extents<std::size_t, md::dynamic_extent, 2>>
272 facets,
273 std::tuple<mdspan2_t, int,
274 md::mdspan<const std::int32_t,
275 md::extents<std::size_t, md::dynamic_extent, 2>>>
276 dofmap0,
277 fem::DofTransformKernel<T> auto P0,
278 std::tuple<mdspan2_t, int,
279 md::mdspan<const std::int32_t,
280 md::extents<std::size_t, md::dynamic_extent, 2>>>
281 dofmap1,
282 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
283 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
284 std::span<const std::uint32_t> cell_info0,
285 std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
286 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
287 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
288{
289 if (facets.empty())
290 return;
291
292 const auto [dmap0, bs0, facets0] = dofmap0;
293 const auto [dmap1, bs1, facets1] = dofmap1;
294
295 // Data structures used in bc application
296 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
297 std::vector<T> Ae, be;
298 assert(facets0.size() == facets.size());
299 assert(facets1.size() == facets.size());
300 for (std::size_t index = 0; index < facets.extent(0); ++index)
301 {
302 // Cell in integration domain, test function and trial function
303 // meshes
304 std::int32_t cell = facets(index, 0);
305 std::int32_t cell0 = facets0(index, 0);
306 std::int32_t cell1 = facets1(index, 0);
307
308 // Local facet index
309 std::int32_t local_facet = facets(index, 1);
310
311 // Get dof maps for cell
312 auto dofs1 = md::submdspan(dmap1, cell1, md::full_extent);
313
314 // Check if bc is applied to cell
315 bool has_bc = false;
316 for (std::size_t j = 0; j < dofs1.size(); ++j)
317 {
318 for (int k = 0; k < bs1; ++k)
319 {
320 if (bc_markers1[bs1 * dofs1[j] + k])
321 {
322 has_bc = true;
323 break;
324 }
325 }
326 }
327
328 if (!has_bc)
329 continue;
330
331 // Get cell coordinates/geometry
332 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
333 for (std::size_t i = 0; i < x_dofs.size(); ++i)
334 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
335
336 // Size data structure for assembly
337 auto dofs0 = md::submdspan(dmap0, cell0, md::full_extent);
338
339 const int num_rows = bs0 * dofs0.size();
340 const int num_cols = bs1 * dofs1.size();
341
342 // Permutations
343 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_facet);
344
345 Ae.resize(num_rows * num_cols);
346 std::ranges::fill(Ae, 0);
347 kernel(Ae.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
348 &local_facet, &perm, nullptr);
349 P0(Ae, cell_info0, cell0, num_cols);
350 P1T(Ae, cell_info1, cell1, num_rows);
351
352 // Size data structure for assembly
353 be.resize(num_rows);
354 std::ranges::fill(be, 0);
355 for (std::size_t j = 0; j < dofs1.size(); ++j)
356 {
357 for (int k = 0; k < bs1; ++k)
358 {
359 const std::int32_t jj = bs1 * dofs1[j] + k;
360 if (bc_markers1[jj])
361 {
362 const T bc = bc_values1[jj];
363 const T _x0 = x0.empty() ? 0 : x0[jj];
364 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
365 for (int m = 0; m < num_rows; ++m)
366 be[m] -= Ae[m * num_cols + bs1 * j + k] * alpha * (bc - _x0);
367 }
368 }
369 }
370
371 for (std::size_t i = 0; i < dofs0.size(); ++i)
372 for (int k = 0; k < bs0; ++k)
373 b[bs0 * dofs0[i] + k] += be[bs0 * i + k];
374 }
375}
376
419template <dolfinx::scalar T>
420void _lift_bc_interior_facets(
421 std::span<T> b, mdspan2_t x_dofmap,
422 md::mdspan<const scalar_value_t<T>,
423 md::extents<std::size_t, md::dynamic_extent, 3>>
424 x,
425 FEkernel<T> auto kernel,
426 md::mdspan<const std::int32_t,
427 md::extents<std::size_t, md::dynamic_extent, 2, 2>>
428 facets,
429 std::tuple<mdspan2_t, int,
430 md::mdspan<const std::int32_t,
431 md::extents<std::size_t, md::dynamic_extent, 2, 2>>>
432 dofmap0,
433 fem::DofTransformKernel<T> auto P0,
434 std::tuple<mdspan2_t, int,
435 md::mdspan<const std::int32_t,
436 md::extents<std::size_t, md::dynamic_extent, 2, 2>>>
437 dofmap1,
438 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
439 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
440 md::dynamic_extent>>
441 coeffs,
442 std::span<const std::uint32_t> cell_info0,
443 std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
444 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
445 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
446{
447 if (facets.empty())
448 return;
449
450 const auto [dmap0, bs0, facets0] = dofmap0;
451 const auto [dmap1, bs1, facets1] = dofmap1;
452
453 // Data structures used in assembly
454 using X = scalar_value_t<T>;
455 std::vector<X> cdofs(2 * x_dofmap.extent(1) * 3);
456 std::span<X> cdofs0(cdofs.data(), x_dofmap.extent(1) * 3);
457 std::span<X> cdofs1(cdofs.data() + x_dofmap.extent(1) * 3,
458 x_dofmap.extent(1) * 3);
459 std::vector<T> Ae, be;
460
461 // Temporaries for joint dofmaps
462 std::vector<std::int32_t> dmapjoint0, dmapjoint1;
463
464 const int num_dofs0 = dmap0.extent(1);
465 const int num_dofs1 = dmap1.extent(1);
466 assert(facets0.size() == facets.size());
467 assert(facets1.size() == facets.size());
468 for (std::size_t f = 0; f < facets.extent(0); ++f)
469 {
470 // Cells in integration domain, test function domain and trial
471 // function domain meshes
472 std::array cells{facets(f, 0, 0), facets(f, 1, 0)};
473 std::array cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
474 std::array cells1{facets1(f, 0, 0), facets1(f, 1, 0)};
475
476 // Local facet indices
477 std::array local_facet = {facets(f, 0, 1), facets(f, 1, 1)};
478
479 // Get cell geometry
480 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
481 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
482 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
483 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
484 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
485 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
486
487 // Get dof maps for cells and pack
488 // When integrating over interfaces between two domains, the test function
489 // might only be defined on one side, so we check which cells exist in the
490 // test function domain
491 std::span<const std::int32_t> dmap0_cell0
492 = cells0[0] >= 0
493 ? std::span(dmap0.data_handle() + cells0[0] * num_dofs0,
494 num_dofs0)
495 : std::span<const std::int32_t>();
496 std::span<const std::int32_t> dmap0_cell1
497 = cells0[1] >= 0
498 ? std::span(dmap0.data_handle() + cells0[1] * num_dofs0,
499 num_dofs0)
500 : std::span<const std::int32_t>();
501
502 dmapjoint0.resize(2 * num_dofs0);
503 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
504 std::ranges::copy(dmap0_cell1, std::next(dmapjoint0.begin(), num_dofs0));
505
506 // Check which cells exist in the trial function domain
507 std::span<const std::int32_t> dmap1_cell0
508 = cells1[0] >= 0
509 ? std::span(dmap1.data_handle() + cells1[0] * num_dofs1,
510 num_dofs1)
511 : std::span<const std::int32_t>();
512 std::span<const std::int32_t> dmap1_cell1
513 = cells1[1] >= 0
514 ? std::span(dmap1.data_handle() + cells1[1] * num_dofs1,
515 num_dofs1)
516 : std::span<const std::int32_t>();
517
518 dmapjoint1.resize(2 * num_dofs1);
519 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
520 std::ranges::copy(dmap1_cell1, std::next(dmapjoint1.begin(), num_dofs1));
521
522 // Check if bc is applied to cell0
523 bool has_bc = false;
524 for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
525 {
526 for (int k = 0; k < bs1; ++k)
527 {
528 if (bc_markers1[bs1 * dmap1_cell0[j] + k])
529 {
530 has_bc = true;
531 break;
532 }
533 }
534 }
535
536 // Check if bc is applied to cell1
537 for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
538 {
539 for (int k = 0; k < bs1; ++k)
540 {
541 if (bc_markers1[bs1 * dmap1_cell1[j] + k])
542 {
543 has_bc = true;
544 break;
545 }
546 }
547 }
548
549 if (!has_bc)
550 continue;
551
552 const int num_rows = bs0 * 2 * num_dofs0;
553 const int num_cols = bs1 * 2 * num_dofs1;
554
555 // Tabulate tensor
556 Ae.resize(num_rows * num_cols);
557 std::ranges::fill(Ae, 0);
558 std::array perm = perms.empty()
559 ? std::array<std::uint8_t, 2>{0, 0}
560 : std::array{perms(cells[0], local_facet[0]),
561 perms(cells[1], local_facet[1])};
562 kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
563 local_facet.data(), perm.data(), nullptr);
564
565 std::span<T> _Ae(Ae);
566 std::span<T> sub_Ae0
567 = _Ae.subspan(bs0 * num_dofs0 * num_cols, bs0 * num_dofs1 * num_cols);
568
569 if (cells0[0] >= 0)
570 P0(_Ae, cell_info0, cells0[0], num_cols);
571 if (cells0[1] >= 0)
572 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
573 if (cells1[0] >= 0)
574 P1T(_Ae, cell_info1, cells1[0], num_rows);
575
576 if (cells1[1] >= 0)
577 {
578 for (int row = 0; row < num_rows; ++row)
579 {
580 // DOFs for dmap1 and cell1 are not stored contiguously in
581 // the block matrix, so each row needs a separate span access
582 std::span<T> sub_Ae1
583 = _Ae.subspan(row * num_cols + bs1 * num_dofs1, bs1 * num_dofs1);
584 P1T(sub_Ae1, cell_info1, cells1[1], 1);
585 }
586 }
587
588 be.resize(num_rows);
589 std::ranges::fill(be, 0);
590
591 // Compute b = b - A*b for cell0
592 for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
593 {
594 for (int k = 0; k < bs1; ++k)
595 {
596 const std::int32_t jj = bs1 * dmap1_cell0[j] + k;
597 if (bc_markers1[jj])
598 {
599 const T bc = bc_values1[jj];
600 const T _x0 = x0.empty() ? 0 : x0[jj];
601 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
602 for (int m = 0; m < num_rows; ++m)
603 be[m] -= Ae[m * num_cols + bs1 * j + k] * alpha * (bc - _x0);
604 }
605 }
606 }
607
608 // Compute b = b - A*b for cell1
609 const int offset = bs1 * num_dofs1;
610 for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
611 {
612 for (int k = 0; k < bs1; ++k)
613 {
614 const std::int32_t jj = bs1 * dmap1_cell1[j] + k;
615 if (bc_markers1[jj])
616 {
617 const T bc = bc_values1[jj];
618 const T _x0 = x0.empty() ? 0 : x0[jj];
619 // be -= Ae.col(offset + bs1 * j + k) * alpha * (bc - x0[jj]);
620 for (int m = 0; m < num_rows; ++m)
621 {
622 be[m]
623 -= Ae[m * num_cols + offset + bs1 * j + k] * alpha * (bc - _x0);
624 }
625 }
626 }
627 }
628
629 for (std::size_t i = 0; i < dmap0_cell0.size(); ++i)
630 for (int k = 0; k < bs0; ++k)
631 b[bs0 * dmap0_cell0[i] + k] += be[bs0 * i + k];
632
633 const int offset_be = bs0 * num_dofs0;
634 for (std::size_t i = 0; i < dmap0_cell1.size(); ++i)
635 for (int k = 0; k < bs0; ++k)
636 b[bs0 * dmap0_cell1[i] + k] += be[offset_be + bs0 * i + k];
637 }
638}
639
663template <dolfinx::scalar T, int _bs = -1>
664void assemble_cells(
665 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
666 md::mdspan<const scalar_value_t<T>,
667 md::extents<std::size_t, md::dynamic_extent, 3>>
668 x,
669 std::span<const std::int32_t> cells,
670 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap,
671 FEkernel<T> auto kernel, std::span<const T> constants,
672 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
673 std::span<const std::uint32_t> cell_info0)
674{
675 if (cells.empty())
676 return;
677
678 const auto [dmap, bs, cells0] = dofmap;
679 assert(_bs < 0 or _bs == bs);
680
681 // Create data structures used in assembly
682 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
683 std::vector<T> be(bs * dmap.extent(1));
684 std::span<T> _be(be);
685
686 // Iterate over active cells
687 for (std::size_t index = 0; index < cells.size(); ++index)
688 {
689 // Integration domain celland test function cell
690 std::int32_t c = cells[index];
691 std::int32_t c0 = cells0[index];
692
693 // Get cell coordinates/geometry
694 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
695 for (std::size_t i = 0; i < x_dofs.size(); ++i)
696 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
697
698 // Tabulate vector for cell
699 std::ranges::fill(be, 0);
700 kernel(be.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
701 nullptr, nullptr, nullptr);
702 P0(_be, cell_info0, c0, 1);
703
704 // Scatter cell vector to 'global' vector array
705 auto dofs = md::submdspan(dmap, c0, md::full_extent);
706 if constexpr (_bs > 0)
707 {
708 for (std::size_t i = 0; i < dofs.size(); ++i)
709 for (int k = 0; k < _bs; ++k)
710 b[_bs * dofs[i] + k] += be[_bs * i + k];
711 }
712 else
713 {
714 for (std::size_t i = 0; i < dofs.size(); ++i)
715 for (int k = 0; k < bs; ++k)
716 b[bs * dofs[i] + k] += be[bs * i + k];
717 }
718 }
719}
720
745template <dolfinx::scalar T, int _bs = -1>
746void assemble_exterior_facets(
747 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
748 md::mdspan<const scalar_value_t<T>,
749 md::extents<std::size_t, md::dynamic_extent, 3>>
750 x,
751 md::mdspan<const std::int32_t,
752 std::extents<std::size_t, md::dynamic_extent, 2>>
753 facets,
754 std::tuple<mdspan2_t, int,
755 md::mdspan<const std::int32_t,
756 std::extents<std::size_t, md::dynamic_extent, 2>>>
757 dofmap,
758 FEkernel<T> auto fn, std::span<const T> constants,
759 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
760 std::span<const std::uint32_t> cell_info0,
761 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
762{
763 if (facets.empty())
764 return;
765
766 const auto [dmap, bs, facets0] = dofmap;
767 assert(_bs < 0 or _bs == bs);
768
769 // Create data structures used in assembly
770 const int num_dofs = dmap.extent(1);
771 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
772 std::vector<T> be(bs * num_dofs);
773 std::span<T> _be(be);
774 assert(facets0.size() == facets.size());
775 for (std::size_t f = 0; f < facets.extent(0); ++f)
776 {
777 // Cell in the integration domain, local facet index relative to the
778 // integration domain cell, and cell in the test function mesh
779 std::int32_t cell = facets(f, 0);
780 std::int32_t local_facet = facets(f, 1);
781 std::int32_t cell0 = facets0(f, 0);
782
783 // Get cell coordinates/geometry
784 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
785 for (std::size_t i = 0; i < x_dofs.size(); ++i)
786 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
787
788 // Permutations
789 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_facet);
790
791 // Tabulate element vector
792 std::ranges::fill(be, 0);
793 fn(be.data(), &coeffs(f, 0), constants.data(), cdofs.data(), &local_facet,
794 &perm, nullptr);
795
796 P0(_be, cell_info0, cell0, 1);
797
798 // Add element vector to global vector
799 auto dofs = md::submdspan(dmap, cell0, md::full_extent);
800 if constexpr (_bs > 0)
801 {
802 for (std::size_t i = 0; i < dofs.size(); ++i)
803 for (int k = 0; k < _bs; ++k)
804 b[_bs * dofs[i] + k] += be[_bs * i + k];
805 }
806 else
807 {
808 for (std::size_t i = 0; i < dofs.size(); ++i)
809 for (int k = 0; k < bs; ++k)
810 b[bs * dofs[i] + k] += be[bs * i + k];
811 }
812 }
813}
814
840template <dolfinx::scalar T, int _bs = -1>
841void assemble_interior_facets(
842 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
843 md::mdspan<const scalar_value_t<T>,
844 md::extents<std::size_t, md::dynamic_extent, 3>>
845 x,
846 md::mdspan<const std::int32_t,
847 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
848 facets,
849 std::tuple<const DofMap&, int,
850 md::mdspan<const std::int32_t,
851 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
852 dofmap,
853 FEkernel<T> auto fn, std::span<const T> constants,
854 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
855 md::dynamic_extent>>
856 coeffs,
857 std::span<const std::uint32_t> cell_info0,
858 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
859{
860 if (facets.empty())
861 return;
862
863 const auto [dmap, bs, facets0] = dofmap;
864 assert(_bs < 0 or _bs == bs);
865
866 // Create data structures used in assembly
867 using X = scalar_value_t<T>;
868 std::vector<X> cdofs(2 * x_dofmap.extent(1) * 3);
869 std::span<X> cdofs0(cdofs.data(), x_dofmap.extent(1) * 3);
870 std::span<X> cdofs1(cdofs.data() + x_dofmap.extent(1) * 3,
871 x_dofmap.extent(1) * 3);
872 std::vector<T> be;
873
874 const std::size_t dmap_size = dmap.cell_dofs(0).size();
875
876 assert(facets0.size() == facets.size());
877 for (std::size_t f = 0; f < facets.extent(0); ++f)
878 {
879 // Cells in integration domain and test function domain meshes
880 std::array<std::int32_t, 2> cells{facets(f, 0, 0), facets(f, 1, 0)};
881 std::array<std::int32_t, 2> cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
882
883 // Local facet indices
884 std::array<std::int32_t, 2> local_facet{facets(f, 0, 1), facets(f, 1, 1)};
885
886 // Get cell geometry
887 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
888 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
889 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
890 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
891 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
892 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
893
894 // Get dofmaps for cells
895 // When integrating over interfaces between two domains, the test function
896 // might only be defined on one side, so we check which cells exist in the
897 // test function domain
898 std::span<const std::int32_t> dmap0 = cells0[0] >= 0
899 ? dmap.cell_dofs(cells0[0])
900 : std::span<const std::int32_t>();
901 std::span<const std::int32_t> dmap1 = cells0[1] >= 0
902 ? dmap.cell_dofs(cells0[1])
903 : std::span<const std::int32_t>();
904
905 // Tabulate element vector
906 be.resize(bs * 2 * dmap_size);
907 std::ranges::fill(be, 0);
908 std::array perm = perms.empty()
909 ? std::array<std::uint8_t, 2>{0, 0}
910 : std::array{perms(cells[0], local_facet[0]),
911 perms(cells[1], local_facet[1])};
912 fn(be.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
913 local_facet.data(), perm.data(), nullptr);
914
915 std::span<T> _be(be);
916 std::span<T> sub_be = _be.subspan(bs * dmap_size, bs * dmap_size);
917
918 if (cells0[0] >= 0)
919 P0(be, cell_info0, cells0[0], 1);
920 if (cells0[1] >= 0)
921 P0(sub_be, cell_info0, cells0[1], 1);
922
923 // Add element vector to global vector
924 if constexpr (_bs > 0)
925 {
926 for (std::size_t i = 0; i < dmap0.size(); ++i)
927 for (int k = 0; k < _bs; ++k)
928 b[_bs * dmap0[i] + k] += be[_bs * i + k];
929 for (std::size_t i = 0; i < dmap1.size(); ++i)
930 for (int k = 0; k < _bs; ++k)
931 b[_bs * dmap1[i] + k] += be[_bs * (i + dmap_size) + k];
932 }
933 else
934 {
935 for (std::size_t i = 0; i < dmap0.size(); ++i)
936 for (int k = 0; k < bs; ++k)
937 b[bs * dmap0[i] + k] += be[bs * i + k];
938 for (std::size_t i = 0; i < dmap1.size(); ++i)
939 for (int k = 0; k < bs; ++k)
940 b[bs * dmap1[i] + k] += be[bs * (i + dmap_size) + k];
941 }
942 }
943}
944
961template <dolfinx::scalar T, std::floating_point U>
962void lift_bc(std::span<T> b, const Form<T, U>& a, mdspan2_t x_dofmap,
963 md::mdspan<const scalar_value_t<T>,
964 md::extents<std::size_t, md::dynamic_extent, 3>>
965 x,
966 std::span<const T> constants,
967 const std::map<std::pair<IntegralType, int>,
968 std::pair<std::span<const T>, int>>& coefficients,
969 std::span<const T> bc_values1,
970 std::span<const std::int8_t> bc_markers1, std::span<const T> x0,
971 T alpha)
972{
973 // Integration domain mesh
974 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
975 assert(mesh);
976
977 // Test function mesh
978 auto mesh0 = a.function_spaces().at(0)->mesh();
979 assert(mesh0);
980
981 // Trial function mesh
982 auto mesh1 = a.function_spaces().at(1)->mesh();
983 assert(mesh1);
984
985 // Get dofmap for columns and rows of a
986 assert(a.function_spaces().at(0));
987 assert(a.function_spaces().at(1));
988 auto dofmap0 = a.function_spaces()[0]->dofmap()->map();
989 const int bs0 = a.function_spaces()[0]->dofmap()->bs();
990 auto element0 = a.function_spaces()[0]->element();
991 auto dofmap1 = a.function_spaces()[1]->dofmap()->map();
992 const int bs1 = a.function_spaces()[1]->dofmap()->bs();
993 auto element1 = a.function_spaces()[1]->element();
994 assert(element0);
995
996 std::span<const std::uint32_t> cell_info0;
997 std::span<const std::uint32_t> cell_info1;
998 // TODO: Check for each element instead
999 if (element0->needs_dof_transformations()
1000 or element1->needs_dof_transformations() or a.needs_facet_permutations())
1001 {
1002 mesh0->topology_mutable()->create_entity_permutations();
1003 mesh1->topology_mutable()->create_entity_permutations();
1004 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
1005 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
1006 }
1007
1008 fem::DofTransformKernel<T> auto P0
1009 = element0->template dof_transformation_fn<T>(doftransform::standard);
1010 fem::DofTransformKernel<T> auto P1T
1011 = element1->template dof_transformation_right_fn<T>(
1013
1014 for (int i : a.integral_ids(IntegralType::cell))
1015 {
1016 auto kernel = a.kernel(IntegralType::cell, i, 0);
1017 assert(kernel);
1018 auto& [_coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1019 std::span cells = a.domain(IntegralType::cell, i, 0);
1020 std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, 0);
1021 std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, 0);
1022 assert(_coeffs.size() == cells.size() * cstride);
1023 auto coeffs = md::mdspan(_coeffs.data(), cells.size(), cstride);
1024 if (bs0 == 1 and bs1 == 1)
1025 {
1026 _lift_bc_cells<T, 1, 1>(
1027 b, x_dofmap, x, kernel, cells, {dofmap0, bs0, cells0}, P0,
1028 {dofmap1, bs1, cells1}, P1T, constants, coeffs, cell_info0,
1029 cell_info1, bc_values1, bc_markers1, x0, alpha);
1030 }
1031 else if (bs0 == 3 and bs1 == 3)
1032 {
1033 _lift_bc_cells<T, 3, 3>(
1034 b, x_dofmap, x, kernel, cells, {dofmap0, bs0, cells0}, P0,
1035 {dofmap1, bs1, cells1}, P1T, constants, coeffs, cell_info0,
1036 cell_info1, bc_values1, bc_markers1, x0, alpha);
1037 }
1038 else
1039 {
1040 _lift_bc_cells(b, x_dofmap, x, kernel, cells, {dofmap0, bs0, cells0}, P0,
1041 {dofmap1, bs1, cells1}, P1T, constants, coeffs, cell_info0,
1042 cell_info1, bc_values1, bc_markers1, x0, alpha);
1043 }
1044 }
1045
1046 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms;
1047 if (a.needs_facet_permutations())
1048 {
1049 mesh::CellType cell_type = mesh->topology()->cell_type();
1050 int num_facets_per_cell
1051 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
1052 mesh->topology_mutable()->create_entity_permutations();
1053 const std::vector<std::uint8_t>& p
1054 = mesh->topology()->get_facet_permutations();
1055 perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
1056 num_facets_per_cell);
1057 }
1058
1059 for (int i : a.integral_ids(IntegralType::exterior_facet))
1060 {
1061 auto kernel = a.kernel(IntegralType::exterior_facet, i, 0);
1062 assert(kernel);
1063 auto& [coeffs, cstride]
1064 = coefficients.at({IntegralType::exterior_facet, i});
1065
1066 using mdspanx2_t
1067 = md::mdspan<const std::int32_t,
1068 md::extents<std::size_t, md::dynamic_extent, 2>>;
1069 std::span f = a.domain(IntegralType::exterior_facet, i, 0);
1070 mdspanx2_t facets(f.data(), f.size() / 2, 2);
1071 std::span f0 = a.domain_arg(IntegralType::exterior_facet, 0, i, 0);
1072 mdspanx2_t facets0(f0.data(), f0.size() / 2, 2);
1073 std::span f1 = a.domain_arg(IntegralType::exterior_facet, 1, i, 0);
1074 mdspanx2_t facets1(f1.data(), f1.size() / 2, 2);
1075 assert(coeffs.size() == facets.extent(0) * cstride);
1076 _lift_bc_exterior_facets(
1077 b, x_dofmap, x, kernel, facets, {dofmap0, bs0, facets0}, P0,
1078 {dofmap1, bs1, facets1}, P1T, constants,
1079 md::mdspan(coeffs.data(), facets.extent(0), cstride), cell_info0,
1080 cell_info1, bc_values1, bc_markers1, x0, alpha, perms);
1081 }
1082
1083 for (int i : a.integral_ids(IntegralType::interior_facet))
1084 {
1085 auto kernel = a.kernel(IntegralType::interior_facet, i, 0);
1086 assert(kernel);
1087 auto& [coeffs, cstride]
1088 = coefficients.at({IntegralType::interior_facet, i});
1089
1090 using mdspanx22_t
1091 = md::mdspan<const std::int32_t,
1092 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
1093 using mdspanx2x_t
1094 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
1095 md::dynamic_extent>>;
1096 std::span f = a.domain(IntegralType::interior_facet, i, 0);
1097 mdspanx22_t facets(f.data(), f.size() / 4, 2, 2);
1098 std::span f0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
1099 mdspanx22_t facets0(f0.data(), f0.size() / 4, 2, 2);
1100 std::span f1 = a.domain_arg(IntegralType::interior_facet, 1, i, 0);
1101 mdspanx22_t facets1(f1.data(), f1.size() / 4, 2, 2);
1102 _lift_bc_interior_facets(
1103 b, x_dofmap, x, kernel, facets, {dofmap0, bs0, facets0}, P0,
1104 {dofmap1, bs1, facets1}, P1T, constants,
1105 mdspanx2x_t(coeffs.data(), facets.extent(0), 2, cstride), cell_info0,
1106 cell_info1, bc_values1, bc_markers1, x0, alpha, perms);
1107 }
1108}
1109
1130template <dolfinx::scalar T, std::floating_point U>
1131void apply_lifting(
1132 std::span<T> b,
1133 std::vector<std::optional<std::reference_wrapper<const Form<T, U>>>> a,
1134 const std::vector<std::span<const T>>& constants,
1135 const std::vector<std::map<std::pair<IntegralType, int>,
1136 std::pair<std::span<const T>, int>>>& coeffs,
1137 const std::vector<
1138 std::vector<std::reference_wrapper<const DirichletBC<T, U>>>>& bcs1,
1139 const std::vector<std::span<const T>>& x0, T alpha)
1140{
1141 if (!x0.empty() and x0.size() != a.size())
1142 {
1143 throw std::runtime_error(
1144 "Mismatch in size between x0 and bilinear form in assembler.");
1145 }
1146
1147 if (a.size() != bcs1.size())
1148 {
1149 throw std::runtime_error(
1150 "Mismatch in size between a and bcs in assembler.");
1151 }
1152
1153 for (std::size_t j = 0; j < a.size(); ++j)
1154 {
1155 std::vector<std::int8_t> bc_markers1;
1156 std::vector<T> bc_values1;
1157 if (a[j] and !bcs1[j].empty())
1158 {
1159 // Extract data from mesh
1160 std::shared_ptr<const mesh::Mesh<U>> mesh = a[j]->get().mesh();
1161 if (!mesh)
1162 throw std::runtime_error("Unable to extract a mesh.");
1163 mdspan2_t x_dofmap = mesh->geometry().dofmap();
1164 std::span _x = mesh->geometry().x();
1165 md::mdspan<const scalar_value_t<T>,
1166 md::extents<std::size_t, md::dynamic_extent, 3>>
1167 x(_x.data(), _x.size() / 3, 3);
1168
1169 assert(a[j]->get().function_spaces().at(0));
1170 auto V1 = a[j]->get().function_spaces()[1];
1171 assert(V1);
1172 auto map1 = V1->dofmap()->index_map;
1173 const int bs1 = V1->dofmap()->index_map_bs();
1174 assert(map1);
1175 const int crange = bs1 * (map1->size_local() + map1->num_ghosts());
1176 bc_markers1.assign(crange, false);
1177 bc_values1.assign(crange, 0);
1178 for (auto& bc : bcs1[j])
1179 {
1180 bc.get().mark_dofs(bc_markers1);
1181 bc.get().set(bc_values1, std::nullopt, 1);
1182 }
1183
1184 if (!x0.empty())
1185 {
1186 lift_bc<T>(b, a[j]->get(), x_dofmap, x, constants[j], coeffs[j],
1187 bc_values1, bc_markers1, x0[j], alpha);
1188 }
1189 else
1190 {
1191 lift_bc<T>(b, a[j]->get(), x_dofmap, x, constants[j], coeffs[j],
1192 bc_values1, bc_markers1, std::span<const T>(), alpha);
1193 }
1194 }
1195 }
1196}
1197
1205template <dolfinx::scalar T, std::floating_point U>
1206void assemble_vector(
1207 std::span<T> b, const Form<T, U>& L,
1208 md::mdspan<const scalar_value_t<T>,
1209 md::extents<std::size_t, md::dynamic_extent, 3>>
1210 x,
1211 std::span<const T> constants,
1212 const std::map<std::pair<IntegralType, int>,
1213 std::pair<std::span<const T>, int>>& coefficients)
1214{
1215 // Integration domain mesh
1216 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
1217 assert(mesh);
1218
1219 // Test function mesh
1220 auto mesh0 = L.function_spaces().at(0)->mesh();
1221 assert(mesh0);
1222
1223 const int num_cell_types = mesh->topology()->cell_types().size();
1224 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
1225 {
1226 // Geometry dofmap and data
1227 mdspan2_t x_dofmap = mesh->geometry().dofmap(cell_type_idx);
1228
1229 // Get dofmap data
1230 assert(L.function_spaces().at(0));
1231 auto element = L.function_spaces().at(0)->elements(cell_type_idx);
1232 assert(element);
1233 std::shared_ptr<const fem::DofMap> dofmap
1234 = L.function_spaces().at(0)->dofmaps(cell_type_idx);
1235 assert(dofmap);
1236 auto dofs = dofmap->map();
1237 const int bs = dofmap->bs();
1238
1239 fem::DofTransformKernel<T> auto P0
1240 = element->template dof_transformation_fn<T>(doftransform::standard);
1241
1242 std::span<const std::uint32_t> cell_info0;
1243 if (element->needs_dof_transformations() or L.needs_facet_permutations())
1244 {
1245 mesh0->topology_mutable()->create_entity_permutations();
1246 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
1247 }
1248
1249 for (int i : L.integral_ids(IntegralType::cell))
1250 {
1251 auto fn = L.kernel(IntegralType::cell, i, cell_type_idx);
1252 assert(fn);
1253 std::span cells = L.domain(IntegralType::cell, i, cell_type_idx);
1254 std::span cells0 = L.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
1255 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1256 assert(cells.size() * cstride == coeffs.size());
1257 if (bs == 1)
1258 {
1259 impl::assemble_cells<T, 1>(
1260 P0, b, x_dofmap, x, cells, {dofs, bs, cells0}, fn, constants,
1261 md::mdspan(coeffs.data(), cells.size(), cstride), cell_info0);
1262 }
1263 else if (bs == 3)
1264 {
1265 impl::assemble_cells<T, 3>(
1266 P0, b, x_dofmap, x, cells, {dofs, bs, cells0}, fn, constants,
1267 md::mdspan(coeffs.data(), cells.size(), cstride), cell_info0);
1268 }
1269 else
1270 {
1271 impl::assemble_cells(
1272 P0, b, x_dofmap, x, cells, {dofs, bs, cells0}, fn, constants,
1273 md::mdspan(coeffs.data(), cells.size(), cstride), cell_info0);
1274 }
1275 }
1276
1277 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms;
1278 if (L.needs_facet_permutations())
1279 {
1280 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
1281 int num_facets_per_cell
1282 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
1283 mesh->topology_mutable()->create_entity_permutations();
1284 const std::vector<std::uint8_t>& p
1285 = mesh->topology()->get_facet_permutations();
1286 perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
1287 num_facets_per_cell);
1288 }
1289
1290 using mdspanx2_t
1291 = md::mdspan<const std::int32_t,
1292 md::extents<std::size_t, md::dynamic_extent, 2>>;
1293
1294 for (int i : L.integral_ids(IntegralType::exterior_facet))
1295 {
1296 auto fn = L.kernel(IntegralType::exterior_facet, i, 0);
1297 assert(fn);
1298 auto& [coeffs, cstride]
1299 = coefficients.at({IntegralType::exterior_facet, i});
1300 std::span f = L.domain(IntegralType::exterior_facet, i, 0);
1301 mdspanx2_t facets(f.data(), f.size() / 2, 2);
1302 std::span f1 = L.domain_arg(IntegralType::exterior_facet, 0, i, 0);
1303 mdspanx2_t facets1(f1.data(), f1.size() / 2, 2);
1304 assert((facets.size() / 2) * cstride == coeffs.size());
1305 if (bs == 1)
1306 {
1307 impl::assemble_exterior_facets<T, 1>(
1308 P0, b, x_dofmap, x, facets, {dofs, bs, facets1}, fn, constants,
1309 md::mdspan(coeffs.data(), facets.extent(0), cstride), cell_info0,
1310 perms);
1311 }
1312 else if (bs == 3)
1313 {
1314 impl::assemble_exterior_facets<T, 3>(
1315 P0, b, x_dofmap, x, facets, {dofs, bs, facets1}, fn, constants,
1316 md::mdspan(coeffs.data(), facets.size() / 2, cstride), cell_info0,
1317 perms);
1318 }
1319 else
1320 {
1321 impl::assemble_exterior_facets(
1322 P0, b, x_dofmap, x, facets, {dofs, bs, facets1}, fn, constants,
1323 md::mdspan(coeffs.data(), facets.size() / 2, cstride), cell_info0,
1324 perms);
1325 }
1326 }
1327
1328 for (int i : L.integral_ids(IntegralType::interior_facet))
1329 {
1330 using mdspanx22_t
1331 = md::mdspan<const std::int32_t,
1332 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
1333 using mdspanx2x_t
1334 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
1335 md::dynamic_extent>>;
1336
1337 auto fn = L.kernel(IntegralType::interior_facet, i, 0);
1338 assert(fn);
1339 auto& [coeffs, cstride]
1340 = coefficients.at({IntegralType::interior_facet, i});
1341 std::span facets = L.domain(IntegralType::interior_facet, i, 0);
1342 std::span facets1 = L.domain_arg(IntegralType::interior_facet, 0, i, 0);
1343 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
1344 if (bs == 1)
1345 {
1346 impl::assemble_interior_facets<T, 1>(
1347 P0, b, x_dofmap, x,
1348 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
1349 {*dofmap, bs,
1350 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
1351 fn, constants,
1352 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
1353 cell_info0, perms);
1354 }
1355 else if (bs == 3)
1356 {
1357 impl::assemble_interior_facets<T, 3>(
1358 P0, b, x_dofmap, x,
1359 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
1360 {*dofmap, bs,
1361 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
1362 fn, constants,
1363 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
1364 cell_info0, perms);
1365 }
1366 else
1367 {
1368 impl::assemble_interior_facets(
1369 P0, b, x_dofmap, x,
1370 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
1371 {*dofmap, bs,
1372 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
1373 fn, constants,
1374 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
1375 cell_info0, perms);
1376 }
1377 }
1378 }
1379}
1380
1387template <dolfinx::scalar T, std::floating_point U>
1388void assemble_vector(
1389 std::span<T> b, const Form<T, U>& L, std::span<const T> constants,
1390 const std::map<std::pair<IntegralType, int>,
1391 std::pair<std::span<const T>, int>>& coefficients)
1392{
1393 using mdspanx3_t
1394 = md::mdspan<const scalar_value_t<T>,
1395 md::extents<std::size_t, md::dynamic_extent, 3>>;
1396
1397 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
1398 assert(mesh);
1399 auto x = mesh->geometry().x();
1400 if constexpr (std::is_same_v<U, scalar_value_t<T>>)
1401 {
1402 assemble_vector(b, L, mdspanx3_t(x.data(), x.size() / 3, 3), constants,
1403 coefficients);
1404 }
1405 else
1406 {
1407 std::vector<scalar_value_t<T>> _x(x.begin(), x.end());
1408 assemble_vector(b, L, mdspanx3_t(_x.data(), _x.size() / 3, 3), constants,
1409 coefficients);
1410 }
1411}
1412} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Definition DirichletBC.h:255
Functions supporting finite element method operations.
void cells(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:16
Finite element method functionality.
Definition assemble_expression_impl.h:23
@ transpose
Transpose.
Definition FiniteElement.h:28
@ standard
Standard.
Definition FiniteElement.h:27
@ interior_facet
Interior facet.
Definition Form.h:42
@ cell
Cell.
Definition Form.h:40
@ exterior_facet
Exterior facet.
Definition Form.h:41
CellType
Cell type identifier.
Definition cell_types.h:22
int cell_num_entities(CellType type, int dim)
Definition cell_types.cpp:139