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 "FunctionSpace.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 <vector>
28
29namespace dolfinx::fem::impl
30{
32using mdspan2_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
33 const std::int32_t,
34 MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<std::size_t, 2>>;
36
74template <dolfinx::scalar T, int _bs0 = -1, int _bs1 = -1>
75void _lift_bc_cells(
76 std::span<T> b, mdspan2_t x_dofmap,
77 std::span<const scalar_value_type_t<T>> x, FEkernel<T> auto kernel,
78 std::span<const std::int32_t> cells,
79 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
80 fem::DofTransformKernel<T> auto P0,
81 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
82 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
83 std::span<const T> coeffs, int cstride,
84 std::span<const std::uint32_t> cell_info0,
85 std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
86 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha)
87{
88 if (cells.empty())
89 return;
90
91 const auto [dmap0, bs0, cells0] = dofmap0;
92 const auto [dmap1, bs1, cells1] = dofmap1;
93 assert(_bs0 < 0 or _bs0 == bs0);
94 assert(_bs1 < 0 or _bs1 == bs1);
95
96 // Data structures used in bc application
97 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
98 std::vector<T> Ae, be;
99 assert(cells0.size() == cells.size());
100 assert(cells1.size() == cells.size());
101 for (std::size_t index = 0; index < cells.size(); ++index)
102 {
103 // Cell index in integration domain mesh, test function mesh, and trial
104 // function mesh
105 std::int32_t c = cells[index];
106 std::int32_t c0 = cells0[index];
107 std::int32_t c1 = cells1[index];
108
109 // Get dof maps for cell
110 auto dofs1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
111 dmap1, c1, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
112
113 // Check if bc is applied to cell
114 bool has_bc = false;
115 for (std::size_t j = 0; j < dofs1.size(); ++j)
116 {
117 if constexpr (_bs1 > 0)
118 {
119 for (int k = 0; k < _bs1; ++k)
120 {
121 assert(_bs1 * dofs1[j] + k < (int)bc_markers1.size());
122 if (bc_markers1[_bs1 * dofs1[j] + k])
123 {
124 has_bc = true;
125 break;
126 }
127 }
128 }
129 else
130 {
131 for (int k = 0; k < bs1; ++k)
132 {
133 assert(bs1 * dofs1[j] + k < (int)bc_markers1.size());
134 if (bc_markers1[bs1 * dofs1[j] + k])
135 {
136 has_bc = true;
137 break;
138 }
139 }
140 }
141 }
142
143 if (!has_bc)
144 continue;
145
146 // Get cell coordinates/geometry
147 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
148 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
149 for (std::size_t i = 0; i < x_dofs.size(); ++i)
150 {
151 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
152 std::next(coordinate_dofs.begin(), 3 * i));
153 }
154
155 // Size data structure for assembly
156 auto dofs0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
157 dmap0, c0, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
158
159 const int num_rows = bs0 * dofs0.size();
160 const int num_cols = bs1 * dofs1.size();
161
162 const T* coeff_array = coeffs.data() + index * cstride;
163 Ae.resize(num_rows * num_cols);
164 std::ranges::fill(Ae, 0);
165 kernel(Ae.data(), coeff_array, constants.data(), coordinate_dofs.data(),
166 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
261template <dolfinx::scalar T, int _bs = -1>
262void _lift_bc_exterior_facets(
263 std::span<T> b, mdspan2_t x_dofmap,
264 std::span<const scalar_value_type_t<T>> x, int num_facets_per_cell,
265 FEkernel<T> auto kernel, std::span<const std::int32_t> facets,
266 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
267 fem::DofTransformKernel<T> auto P0,
268 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
269 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
270 std::span<const T> coeffs, int cstride,
271 std::span<const std::uint32_t> cell_info0,
272 std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
273 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
274 std::span<const std::uint8_t> perms)
275{
276 if (facets.empty())
277 return;
278
279 const auto [dmap0, bs0, facets0] = dofmap0;
280 const auto [dmap1, bs1, facets1] = dofmap1;
281
282 // Data structures used in bc application
283 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
284 std::vector<T> Ae, be;
285 assert(facets.size() % 2 == 0);
286 assert(facets0.size() == facets.size());
287 assert(facets1.size() == facets.size());
288 for (std::size_t index = 0; index < facets.size(); index += 2)
289 {
290 // Cell in integration domain mesh
291 std::int32_t cell = facets[index];
292 // Cell in test function mesh
293 std::int32_t cell0 = facets0[index];
294 // Cell in trial function mesh
295 std::int32_t cell1 = facets1[index];
296
297 // Local facet index
298 std::int32_t local_facet = facets[index + 1];
299
300 // Get dof maps for cell
301 auto dofs1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
302 dmap1, cell1, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
303
304 // Check if bc is applied to cell
305 bool has_bc = false;
306 for (std::size_t j = 0; j < dofs1.size(); ++j)
307 {
308 for (int k = 0; k < bs1; ++k)
309 {
310 if (bc_markers1[bs1 * dofs1[j] + k])
311 {
312 has_bc = true;
313 break;
314 }
315 }
316 }
317
318 if (!has_bc)
319 continue;
320
321 // Get cell coordinates/geometry
322 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
323 x_dofmap, cell, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
324 for (std::size_t i = 0; i < x_dofs.size(); ++i)
325 {
326 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
327 std::next(coordinate_dofs.begin(), 3 * i));
328 }
329
330 // Size data structure for assembly
331 auto dofs0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
332 dmap0, cell0, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
333
334 const int num_rows = bs0 * dofs0.size();
335 const int num_cols = bs1 * dofs1.size();
336
337 // Permutations
338 std::uint8_t perm
339 = perms.empty() ? 0 : perms[cell * num_facets_per_cell + local_facet];
340
341 const T* coeff_array = coeffs.data() + index / 2 * cstride;
342 Ae.resize(num_rows * num_cols);
343 std::ranges::fill(Ae, 0);
344 kernel(Ae.data(), coeff_array, constants.data(), coordinate_dofs.data(),
345 &local_facet, &perm);
346 P0(Ae, cell_info0, cell0, num_cols);
347 P1T(Ae, cell_info1, cell1, num_rows);
348
349 // Size data structure for assembly
350 be.resize(num_rows);
351 std::ranges::fill(be, 0);
352 for (std::size_t j = 0; j < dofs1.size(); ++j)
353 {
354 for (int k = 0; k < bs1; ++k)
355 {
356 const std::int32_t jj = bs1 * dofs1[j] + k;
357 if (bc_markers1[jj])
358 {
359 const T bc = bc_values1[jj];
360 const T _x0 = x0.empty() ? 0 : x0[jj];
361 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
362 for (int m = 0; m < num_rows; ++m)
363 be[m] -= Ae[m * num_cols + bs1 * j + k] * alpha * (bc - _x0);
364 }
365 }
366 }
367
368 for (std::size_t i = 0; i < dofs0.size(); ++i)
369 for (int k = 0; k < bs0; ++k)
370 b[bs0 * dofs0[i] + k] += be[bs0 * i + k];
371 }
372}
373
410template <dolfinx::scalar T, int _bs = -1>
411void _lift_bc_interior_facets(
412 std::span<T> b, mdspan2_t x_dofmap,
413 std::span<const scalar_value_type_t<T>> x, int num_facets_per_cell,
414 FEkernel<T> auto kernel, std::span<const std::int32_t> facets,
415 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
416 fem::DofTransformKernel<T> auto P0,
417 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
418 fem::DofTransformKernel<T> auto P1T, std::span<const T> constants,
419 std::span<const T> coeffs, int cstride,
420 std::span<const std::uint32_t> cell_info0,
421 std::span<const std::uint32_t> cell_info1,
422 std::span<const std::uint8_t> perms, std::span<const T> bc_values1,
423 std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha)
424{
425 if (facets.empty())
426 return;
427
428 const auto [dmap0, bs0, facets0] = dofmap0;
429 const auto [dmap1, bs1, facets1] = dofmap1;
430
431 // Data structures used in assembly
432 using X = scalar_value_type_t<T>;
433 std::vector<X> coordinate_dofs(2 * x_dofmap.extent(1) * 3);
434 std::span<X> cdofs0(coordinate_dofs.data(), x_dofmap.extent(1) * 3);
435 std::span<X> cdofs1(coordinate_dofs.data() + x_dofmap.extent(1) * 3,
436 x_dofmap.extent(1) * 3);
437 std::vector<T> Ae, be;
438
439 // Temporaries for joint dofmaps
440 std::vector<std::int32_t> dmapjoint0, dmapjoint1;
441 assert(facets.size() % 4 == 0);
442
443 const int num_dofs0 = dmap0.extent(1);
444 const int num_dofs1 = dmap1.extent(1);
445 assert(facets0.size() == facets.size());
446 assert(facets1.size() == facets.size());
447 for (std::size_t index = 0; index < facets.size(); index += 4)
448 {
449 // Cells in integration domain, test function domain and trial
450 // function domain meshes
451 std::array cells{facets[index], facets[index + 2]};
452 std::array cells0{facets0[index], facets0[index + 2]};
453 std::array cells1{facets1[index], facets1[index + 2]};
454
455 // Local facet indices
456 std::array<std::int32_t, 2> local_facet
457 = {facets[index + 1], facets[index + 3]};
458
459 // Get cell geometry
460 auto x_dofs0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
461 x_dofmap, cells[0], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
462 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
463 {
464 std::copy_n(std::next(x.begin(), 3 * x_dofs0[i]), 3,
465 std::next(cdofs0.begin(), 3 * i));
466 }
467 auto x_dofs1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
468 x_dofmap, cells[1], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
469 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
470 {
471 std::copy_n(std::next(x.begin(), 3 * x_dofs1[i]), 3,
472 std::next(cdofs1.begin(), 3 * i));
473 }
474
475 // Get dof maps for cells and pack
476 auto dmap0_cell0
477 = std::span(dmap0.data_handle() + cells0[0] * num_dofs0, num_dofs0);
478 auto dmap0_cell1
479 = std::span(dmap0.data_handle() + cells0[1] * num_dofs0, num_dofs0);
480
481 dmapjoint0.resize(dmap0_cell0.size() + dmap0_cell1.size());
482 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
483 std::ranges::copy(dmap0_cell1,
484 std::next(dmapjoint0.begin(), dmap0_cell0.size()));
485
486 auto dmap1_cell0
487 = std::span(dmap1.data_handle() + cells1[0] * num_dofs1, num_dofs1);
488 auto dmap1_cell1
489 = std::span(dmap1.data_handle() + cells1[1] * num_dofs1, num_dofs1);
490
491 dmapjoint1.resize(dmap1_cell0.size() + dmap1_cell1.size());
492 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
493 std::ranges::copy(dmap1_cell1,
494 std::next(dmapjoint1.begin(), dmap1_cell0.size()));
495
496 // Check if bc is applied to cell0
497 bool has_bc = false;
498 for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
499 {
500 for (int k = 0; k < bs1; ++k)
501 {
502 if (bc_markers1[bs1 * dmap1_cell0[j] + k])
503 {
504 has_bc = true;
505 break;
506 }
507 }
508 }
509
510 // Check if bc is applied to cell1
511 for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
512 {
513 for (int k = 0; k < bs1; ++k)
514 {
515 if (bc_markers1[bs1 * dmap1_cell1[j] + k])
516 {
517 has_bc = true;
518 break;
519 }
520 }
521 }
522
523 if (!has_bc)
524 continue;
525
526 const int num_rows = bs0 * dmapjoint0.size();
527 const int num_cols = bs1 * dmapjoint1.size();
528
529 // Tabulate tensor
530 Ae.resize(num_rows * num_cols);
531 std::ranges::fill(Ae, 0);
532 std::array perm
533 = perms.empty()
534 ? std::array<std::uint8_t, 2>{0, 0}
535 : std::array{
536 perms[cells[0] * num_facets_per_cell + local_facet[0]],
537 perms[cells[1] * num_facets_per_cell + local_facet[1]]};
538 kernel(Ae.data(), coeffs.data() + index / 2 * cstride, constants.data(),
539 coordinate_dofs.data(), local_facet.data(), perm.data());
540
541 std::span<T> _Ae(Ae);
542 std::span<T> sub_Ae0 = _Ae.subspan(bs0 * dmap0_cell0.size() * num_cols,
543 bs0 * dmap0_cell1.size() * num_cols);
544
545 P0(_Ae, cell_info0, cells0[0], num_cols);
546 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
547 P1T(_Ae, cell_info1, cells1[0], num_rows);
548
549 for (int row = 0; row < num_rows; ++row)
550 {
551 // DOFs for dmap1 and cell1 are not stored contiguously in
552 // the block matrix, so each row needs a separate span access
553 std::span<T> sub_Ae1 = _Ae.subspan(
554 row * num_cols + bs1 * dmap1_cell0.size(), bs1 * dmap1_cell1.size());
555 P1T(sub_Ae1, cell_info1, cells1[1], 1);
556 }
557
558 be.resize(num_rows);
559 std::ranges::fill(be, 0);
560
561 // Compute b = b - A*b for cell0
562 for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
563 {
564 for (int k = 0; k < bs1; ++k)
565 {
566 const std::int32_t jj = bs1 * dmap1_cell0[j] + k;
567 if (bc_markers1[jj])
568 {
569 const T bc = bc_values1[jj];
570 const T _x0 = x0.empty() ? 0 : x0[jj];
571 // be -= Ae.col(bs1 * j + k) * alpha * (bc - _x0);
572 for (int m = 0; m < num_rows; ++m)
573 be[m] -= Ae[m * num_cols + bs1 * j + k] * alpha * (bc - _x0);
574 }
575 }
576 }
577
578 // Compute b = b - A*b for cell1
579 const int offset = bs1 * dmap1_cell0.size();
580 for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
581 {
582 for (int k = 0; k < bs1; ++k)
583 {
584 const std::int32_t jj = bs1 * dmap1_cell1[j] + k;
585 if (bc_markers1[jj])
586 {
587 const T bc = bc_values1[jj];
588 const T _x0 = x0.empty() ? 0 : x0[jj];
589 // be -= Ae.col(offset + bs1 * j + k) * alpha * (bc - x0[jj]);
590 for (int m = 0; m < num_rows; ++m)
591 {
592 be[m]
593 -= Ae[m * num_cols + offset + bs1 * j + k] * alpha * (bc - _x0);
594 }
595 }
596 }
597 }
598
599 for (std::size_t i = 0; i < dmap0_cell0.size(); ++i)
600 for (int k = 0; k < bs0; ++k)
601 b[bs0 * dmap0_cell0[i] + k] += be[bs0 * i + k];
602
603 const int offset_be = bs0 * dmap0_cell0.size();
604 for (std::size_t i = 0; i < dmap0_cell1.size(); ++i)
605 for (int k = 0; k < bs0; ++k)
606 b[bs0 * dmap0_cell1[i] + k] += be[offset_be + bs0 * i + k];
607 }
608}
609
632template <dolfinx::scalar T, int _bs = -1>
633void assemble_cells(
634 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
635 std::span<const scalar_value_type_t<T>> x,
636 std::span<const std::int32_t> cells,
637 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap,
638 FEkernel<T> auto kernel, std::span<const T> constants,
639 std::span<const T> coeffs, int cstride,
640 std::span<const std::uint32_t> cell_info0)
641{
642 if (cells.empty())
643 return;
644
645 const auto [dmap, bs, cells0] = dofmap;
646 assert(_bs < 0 or _bs == bs);
647
648 // Create data structures used in assembly
649 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
650 std::vector<T> be(bs * dmap.extent(1));
651 std::span<T> _be(be);
652
653 // Iterate over active cells
654 for (std::size_t index = 0; index < cells.size(); ++index)
655 {
656 // Integration domain celland test function cell
657 std::int32_t c = cells[index];
658 std::int32_t c0 = cells0[index];
659
660 // Get cell coordinates/geometry
661 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
662 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
663 for (std::size_t i = 0; i < x_dofs.size(); ++i)
664 {
665 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
666 std::next(coordinate_dofs.begin(), 3 * i));
667 }
668
669 // Tabulate vector for cell
670 std::ranges::fill(be, 0);
671 kernel(be.data(), coeffs.data() + index * cstride, constants.data(),
672 coordinate_dofs.data(), nullptr, nullptr);
673 P0(_be, cell_info0, c0, 1);
674
675 // Scatter cell vector to 'global' vector array
676 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
677 dmap, c0, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
678 if constexpr (_bs > 0)
679 {
680 for (std::size_t i = 0; i < dofs.size(); ++i)
681 for (int k = 0; k < _bs; ++k)
682 b[_bs * dofs[i] + k] += be[_bs * i + k];
683 }
684 else
685 {
686 for (std::size_t i = 0; i < dofs.size(); ++i)
687 for (int k = 0; k < bs; ++k)
688 b[bs * dofs[i] + k] += be[bs * i + k];
689 }
690 }
691}
692
718template <dolfinx::scalar T, int _bs = -1>
719void assemble_exterior_facets(
720 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
721 std::span<const scalar_value_type_t<T>> x, int num_facets_per_cell,
722 std::span<const std::int32_t> facets,
723 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap,
724 FEkernel<T> auto fn, std::span<const T> constants,
725 std::span<const T> coeffs, int cstride,
726 std::span<const std::uint32_t> cell_info0,
727 std::span<const std::uint8_t> perms)
728{
729 if (facets.empty())
730 return;
731
732 const auto [dmap, bs, facets0] = dofmap;
733 assert(_bs < 0 or _bs == bs);
734
735 // FIXME: Add proper interface for num_dofs
736 // Create data structures used in assembly
737 const int num_dofs = dmap.extent(1);
738 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
739 std::vector<T> be(bs * num_dofs);
740 std::span<T> _be(be);
741 assert(facets.size() % 2 == 0);
742 assert(facets0.size() == facets.size());
743 for (std::size_t index = 0; index < facets.size(); index += 2)
744 {
745 // Cell in the integration domain, local facet index relative to the
746 // integration domain cell, and cell in the test function mesh
747 std::int32_t cell = facets[index];
748 std::int32_t local_facet = facets[index + 1];
749 std::int32_t cell0 = facets0[index];
750
751 // Get cell coordinates/geometry
752 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
753 x_dofmap, cell, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
754 for (std::size_t i = 0; i < x_dofs.size(); ++i)
755 {
756 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
757 std::next(coordinate_dofs.begin(), 3 * i));
758 }
759
760 // Permutations
761 std::uint8_t perm
762 = perms.empty() ? 0 : perms[cell * num_facets_per_cell + local_facet];
763
764 // Tabulate element vector
765 std::ranges::fill(be, 0);
766 fn(be.data(), coeffs.data() + index / 2 * cstride, constants.data(),
767 coordinate_dofs.data(), &local_facet, &perm);
768
769 P0(_be, cell_info0, cell0, 1);
770
771 // Add element vector to global vector
772 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
773 dmap, cell0, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
774 if constexpr (_bs > 0)
775 {
776 for (std::size_t i = 0; i < dofs.size(); ++i)
777 for (int k = 0; k < _bs; ++k)
778 b[_bs * dofs[i] + k] += be[_bs * i + k];
779 }
780 else
781 {
782 for (std::size_t i = 0; i < dofs.size(); ++i)
783 for (int k = 0; k < bs; ++k)
784 b[bs * dofs[i] + k] += be[bs * i + k];
785 }
786 }
787}
788
814template <dolfinx::scalar T, int _bs = -1>
815void assemble_interior_facets(
816 fem::DofTransformKernel<T> auto P0, std::span<T> b, mdspan2_t x_dofmap,
817 std::span<const scalar_value_type_t<T>> x, int num_facets_per_cell,
818 std::span<const std::int32_t> facets,
819 std::tuple<const DofMap&, int, std::span<const std::int32_t>> dofmap,
820 FEkernel<T> auto fn, std::span<const T> constants,
821 std::span<const T> coeffs, int cstride,
822 std::span<const std::uint32_t> cell_info0,
823 std::span<const std::uint8_t> perms)
824{
825 if (facets.empty())
826 return;
827
828 const auto [dmap, bs, facets0] = dofmap;
829 assert(_bs < 0 or _bs == bs);
830
831 // Create data structures used in assembly
832 using X = scalar_value_type_t<T>;
833 std::vector<X> coordinate_dofs(2 * x_dofmap.extent(1) * 3);
834 std::span<X> cdofs0(coordinate_dofs.data(), x_dofmap.extent(1) * 3);
835 std::span<X> cdofs1(coordinate_dofs.data() + x_dofmap.extent(1) * 3,
836 x_dofmap.extent(1) * 3);
837 std::vector<T> be;
838
839 assert(facets.size() % 4 == 0);
840 assert(facets0.size() == facets.size());
841 for (std::size_t index = 0; index < facets.size(); index += 4)
842 {
843 // Cells in integration domain and test function domain meshes
844 std::array<std::int32_t, 2> cells{facets[index], facets[index + 2]};
845 std::array<std::int32_t, 2> cells0{facets0[index], facets0[index + 2]};
846
847 // Local facet indices
848 std::array<std::int32_t, 2> local_facet{facets[index + 1],
849 facets[index + 3]};
850
851 // Get cell geometry
852 auto x_dofs0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
853 x_dofmap, cells[0], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
854 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
855 {
856 std::copy_n(std::next(x.begin(), 3 * x_dofs0[i]), 3,
857 std::next(cdofs0.begin(), 3 * i));
858 }
859 auto x_dofs1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
860 x_dofmap, cells[1], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
861 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
862 {
863 std::copy_n(std::next(x.begin(), 3 * x_dofs1[i]), 3,
864 std::next(cdofs1.begin(), 3 * i));
865 }
866
867 // Get dofmaps for cells
868 std::span<const std::int32_t> dmap0 = dmap.cell_dofs(cells0[0]);
869 std::span<const std::int32_t> dmap1 = dmap.cell_dofs(cells0[1]);
870
871 // Tabulate element vector
872 be.resize(bs * (dmap0.size() + dmap1.size()));
873 std::ranges::fill(be, 0);
874 std::array perm
875 = perms.empty()
876 ? std::array<std::uint8_t, 2>{0, 0}
877 : std::array{
878 perms[cells[0] * num_facets_per_cell + local_facet[0]],
879 perms[cells[1] * num_facets_per_cell + local_facet[1]]};
880 fn(be.data(), coeffs.data() + index / 2 * cstride, constants.data(),
881 coordinate_dofs.data(), local_facet.data(), perm.data());
882
883 std::span<T> _be(be);
884 std::span<T> sub_be = _be.subspan(bs * dmap0.size(), bs * dmap1.size());
885
886 P0(be, cell_info0, cells0[0], 1);
887 P0(sub_be, cell_info0, cells0[1], 1);
888
889 // Add element vector to global vector
890 if constexpr (_bs > 0)
891 {
892 for (std::size_t i = 0; i < dmap0.size(); ++i)
893 for (int k = 0; k < _bs; ++k)
894 b[_bs * dmap0[i] + k] += be[_bs * i + k];
895 for (std::size_t i = 0; i < dmap1.size(); ++i)
896 for (int k = 0; k < _bs; ++k)
897 b[_bs * dmap1[i] + k] += be[_bs * (i + dmap0.size()) + k];
898 }
899 else
900 {
901 for (std::size_t i = 0; i < dmap0.size(); ++i)
902 for (int k = 0; k < bs; ++k)
903 b[bs * dmap0[i] + k] += be[bs * i + k];
904 for (std::size_t i = 0; i < dmap1.size(); ++i)
905 for (int k = 0; k < bs; ++k)
906 b[bs * dmap1[i] + k] += be[bs * (i + dmap0.size()) + k];
907 }
908 }
909}
910
927template <dolfinx::scalar T, std::floating_point U>
928void lift_bc(std::span<T> b, const Form<T, U>& a, mdspan2_t x_dofmap,
929 std::span<const scalar_value_type_t<T>> x,
930 std::span<const T> constants,
931 const std::map<std::pair<IntegralType, int>,
932 std::pair<std::span<const T>, int>>& coefficients,
933 std::span<const T> bc_values1,
934 std::span<const std::int8_t> bc_markers1, std::span<const T> x0,
935 T alpha)
936{
937 // Integration domain mesh
938 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
939 assert(mesh);
940
941 // Test function mesh
942 auto mesh0 = a.function_spaces().at(0)->mesh();
943 assert(mesh0);
944
945 // Trial function mesh
946 auto mesh1 = a.function_spaces().at(1)->mesh();
947 assert(mesh1);
948
949 // Get dofmap for columns and rows of a
950 assert(a.function_spaces().at(0));
951 assert(a.function_spaces().at(1));
952 auto dofmap0 = a.function_spaces()[0]->dofmap()->map();
953 const int bs0 = a.function_spaces()[0]->dofmap()->bs();
954 auto element0 = a.function_spaces()[0]->element();
955 auto dofmap1 = a.function_spaces()[1]->dofmap()->map();
956 const int bs1 = a.function_spaces()[1]->dofmap()->bs();
957 auto element1 = a.function_spaces()[1]->element();
958 assert(element0);
959
960 std::span<const std::uint32_t> cell_info0;
961 std::span<const std::uint32_t> cell_info1;
962 // TODO: Check for each element instead
963 if (element0->needs_dof_transformations()
964 or element1->needs_dof_transformations() or a.needs_facet_permutations())
965 {
966 mesh0->topology_mutable()->create_entity_permutations();
967 mesh1->topology_mutable()->create_entity_permutations();
968 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
969 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
970 }
971
972 fem::DofTransformKernel<T> auto P0
973 = element0->template dof_transformation_fn<T>(doftransform::standard);
974 fem::DofTransformKernel<T> auto P1T
975 = element1->template dof_transformation_right_fn<T>(
977
978 for (int i : a.integral_ids(IntegralType::cell))
979 {
980 auto kernel = a.kernel(IntegralType::cell, i);
981 assert(kernel);
982 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
983 std::span<const std::int32_t> cells = a.domain(IntegralType::cell, i);
984 if (bs0 == 1 and bs1 == 1)
985 {
986 _lift_bc_cells<T, 1, 1>(
987 b, x_dofmap, x, kernel, cells,
988 {dofmap0, bs0, a.domain(IntegralType::cell, i, *mesh0)}, P0,
989 {dofmap1, bs1, a.domain(IntegralType::cell, i, *mesh1)}, P1T,
990 constants, coeffs, cstride, cell_info0, cell_info1, bc_values1,
991 bc_markers1, x0, alpha);
992 }
993 else if (bs0 == 3 and bs1 == 3)
994 {
995 _lift_bc_cells<T, 3, 3>(
996 b, x_dofmap, x, kernel, cells,
997 {dofmap0, bs0, a.domain(IntegralType::cell, i, *mesh0)}, P0,
998 {dofmap1, bs1, a.domain(IntegralType::cell, i, *mesh1)}, P1T,
999 constants, coeffs, cstride, cell_info0, cell_info1, bc_values1,
1000 bc_markers1, x0, alpha);
1001 }
1002 else
1003 {
1004 _lift_bc_cells(b, x_dofmap, x, kernel, cells,
1005 {dofmap0, bs0, a.domain(IntegralType::cell, i, *mesh0)},
1006 P0,
1007 {dofmap1, bs1, a.domain(IntegralType::cell, i, *mesh1)},
1008 P1T, constants, coeffs, cstride, cell_info0, cell_info1,
1009 bc_values1, bc_markers1, x0, alpha);
1010 }
1011 }
1012
1013 std::span<const std::uint8_t> perms;
1014 if (a.needs_facet_permutations())
1015 {
1016 mesh->topology_mutable()->create_entity_permutations();
1017 perms = std::span(mesh->topology()->get_facet_permutations());
1018 }
1019
1020 mesh::CellType cell_type = mesh->topology()->cell_type();
1021 int num_facets_per_cell
1022 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
1023 for (int i : a.integral_ids(IntegralType::exterior_facet))
1024 {
1025 auto kernel = a.kernel(IntegralType::exterior_facet, i);
1026 assert(kernel);
1027 auto& [coeffs, cstride]
1028 = coefficients.at({IntegralType::exterior_facet, i});
1029 _lift_bc_exterior_facets(
1030 b, x_dofmap, x, num_facets_per_cell, kernel,
1031 a.domain(IntegralType::exterior_facet, i),
1032 {dofmap0, bs0, a.domain(IntegralType::exterior_facet, i, *mesh0)}, P0,
1033 {dofmap1, bs1, a.domain(IntegralType::exterior_facet, i, *mesh1)}, P1T,
1034 constants, coeffs, cstride, cell_info0, cell_info1, bc_values1,
1035 bc_markers1, x0, alpha, perms);
1036 }
1037
1038 for (int i : a.integral_ids(IntegralType::interior_facet))
1039 {
1040 auto kernel = a.kernel(IntegralType::interior_facet, i);
1041 assert(kernel);
1042 auto& [coeffs, cstride]
1043 = coefficients.at({IntegralType::interior_facet, i});
1044 _lift_bc_interior_facets(
1045 b, x_dofmap, x, num_facets_per_cell, kernel,
1046 a.domain(IntegralType::interior_facet, i),
1047 {dofmap0, bs0, a.domain(IntegralType::interior_facet, i, *mesh0)}, P0,
1048 {dofmap1, bs1, a.domain(IntegralType::interior_facet, i, *mesh1)}, P1T,
1049 constants, coeffs, cstride, cell_info0, cell_info1, perms, bc_values1,
1050 bc_markers1, x0, alpha);
1051 }
1052}
1053
1074template <dolfinx::scalar T, std::floating_point U>
1075void apply_lifting(
1076 std::span<T> b,
1077 std::vector<std::optional<std::reference_wrapper<const Form<T, U>>>> a,
1078 const std::vector<std::span<const T>>& constants,
1079 const std::vector<std::map<std::pair<IntegralType, int>,
1080 std::pair<std::span<const T>, int>>>& coeffs,
1081 const std::vector<
1082 std::vector<std::reference_wrapper<const DirichletBC<T, U>>>>& bcs1,
1083 const std::vector<std::span<const T>>& x0, T alpha)
1084{
1085 if (!x0.empty() and x0.size() != a.size())
1086 {
1087 throw std::runtime_error(
1088 "Mismatch in size between x0 and bilinear form in assembler.");
1089 }
1090
1091 if (a.size() != bcs1.size())
1092 {
1093 throw std::runtime_error(
1094 "Mismatch in size between a and bcs in assembler.");
1095 }
1096
1097 for (std::size_t j = 0; j < a.size(); ++j)
1098 {
1099 std::vector<std::int8_t> bc_markers1;
1100 std::vector<T> bc_values1;
1101 if (a[j] and !bcs1[j].empty())
1102 {
1103 // Extract data from mesh
1104 std::shared_ptr<const mesh::Mesh<U>> mesh = a[j]->get().mesh();
1105 if (!mesh)
1106 throw std::runtime_error("Unable to extract a mesh.");
1107 mdspan2_t x_dofmap = mesh->geometry().dofmap();
1108 auto x = mesh->geometry().x();
1109
1110 assert(a[j]->get().function_spaces().at(0));
1111 auto V1 = a[j]->get().function_spaces()[1];
1112 assert(V1);
1113 auto map1 = V1->dofmap()->index_map;
1114 const int bs1 = V1->dofmap()->index_map_bs();
1115 assert(map1);
1116 const int crange = bs1 * (map1->size_local() + map1->num_ghosts());
1117 bc_markers1.assign(crange, false);
1118 bc_values1.assign(crange, 0);
1119 for (auto& bc : bcs1[j])
1120 {
1121 bc.get().mark_dofs(bc_markers1);
1122 bc.get().set(bc_values1, std::nullopt, 1);
1123 }
1124
1125 if (!x0.empty())
1126 {
1127 lift_bc<T>(b, a[j]->get(), x_dofmap, x, constants[j], coeffs[j],
1128 bc_values1, bc_markers1, x0[j], alpha);
1129 }
1130 else
1131 {
1132 lift_bc<T>(b, a[j]->get(), x_dofmap, x, constants[j], coeffs[j],
1133 bc_values1, bc_markers1, std::span<const T>(), alpha);
1134 }
1135 }
1136 }
1137}
1138
1147template <dolfinx::scalar T, std::floating_point U>
1148void assemble_vector(
1149 std::span<T> b, const Form<T, U>& L, mdspan2_t x_dofmap,
1150 std::span<const scalar_value_type_t<T>> x, std::span<const T> constants,
1151 const std::map<std::pair<IntegralType, int>,
1152 std::pair<std::span<const T>, int>>& coefficients)
1153{
1154 // Integration domain mesh
1155 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
1156 assert(mesh);
1157
1158 // Test function mesh
1159 auto mesh0 = L.function_spaces().at(0)->mesh();
1160 assert(mesh0);
1161
1162 // Get dofmap data
1163 assert(L.function_spaces().at(0));
1164 auto element = L.function_spaces().at(0)->element();
1165 assert(element);
1166 std::shared_ptr<const fem::DofMap> dofmap
1167 = L.function_spaces().at(0)->dofmap();
1168 assert(dofmap);
1169 auto dofs = dofmap->map();
1170 const int bs = dofmap->bs();
1171
1172 fem::DofTransformKernel<T> auto P0
1173 = element->template dof_transformation_fn<T>(doftransform::standard);
1174
1175 std::span<const std::uint32_t> cell_info0;
1176 if (element->needs_dof_transformations() or L.needs_facet_permutations())
1177 {
1178 mesh0->topology_mutable()->create_entity_permutations();
1179 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
1180 }
1181
1182 for (int i : L.integral_ids(IntegralType::cell))
1183 {
1184 auto fn = L.kernel(IntegralType::cell, i);
1185 assert(fn);
1186 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1187 std::span<const std::int32_t> cells = L.domain(IntegralType::cell, i);
1188 if (bs == 1)
1189 {
1190 impl::assemble_cells<T, 1>(
1191 P0, b, x_dofmap, x, cells,
1192 {dofs, bs, L.domain(IntegralType::cell, i, *mesh0)}, fn, constants,
1193 coeffs, cstride, cell_info0);
1194 }
1195 else if (bs == 3)
1196 {
1197 impl::assemble_cells<T, 3>(
1198 P0, b, x_dofmap, x, cells,
1199 {dofs, bs, L.domain(IntegralType::cell, i, *mesh0)}, fn, constants,
1200 coeffs, cstride, cell_info0);
1201 }
1202 else
1203 {
1204 impl::assemble_cells(P0, b, x_dofmap, x, cells,
1205 {dofs, bs, L.domain(IntegralType::cell, i, *mesh0)},
1206 fn, constants, coeffs, cstride, cell_info0);
1207 }
1208 }
1209
1210 std::span<const std::uint8_t> perms;
1211 if (L.needs_facet_permutations())
1212 {
1213 mesh->topology_mutable()->create_entity_permutations();
1214 perms = std::span(mesh->topology()->get_facet_permutations());
1215 }
1216
1217 mesh::CellType cell_type = mesh->topology()->cell_type();
1218 int num_facets_per_cell
1219 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
1220 for (int i : L.integral_ids(IntegralType::exterior_facet))
1221 {
1222 auto fn = L.kernel(IntegralType::exterior_facet, i);
1223 assert(fn);
1224 auto& [coeffs, cstride]
1225 = coefficients.at({IntegralType::exterior_facet, i});
1226 std::span<const std::int32_t> facets
1227 = L.domain(IntegralType::exterior_facet, i);
1228 if (bs == 1)
1229 {
1230 impl::assemble_exterior_facets<T, 1>(
1231 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1232 {dofs, bs, L.domain(IntegralType::exterior_facet, i, *mesh0)}, fn,
1233 constants, coeffs, cstride, cell_info0, perms);
1234 }
1235 else if (bs == 3)
1236 {
1237 impl::assemble_exterior_facets<T, 3>(
1238 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1239 {dofs, bs, L.domain(IntegralType::exterior_facet, i, *mesh0)}, fn,
1240 constants, coeffs, cstride, cell_info0, perms);
1241 }
1242 else
1243 {
1244 impl::assemble_exterior_facets(
1245 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1246 {dofs, bs, L.domain(IntegralType::exterior_facet, i, *mesh0)}, fn,
1247 constants, coeffs, cstride, cell_info0, perms);
1248 }
1249 }
1250
1251 for (int i : L.integral_ids(IntegralType::interior_facet))
1252 {
1253 auto fn = L.kernel(IntegralType::interior_facet, i);
1254 assert(fn);
1255 auto& [coeffs, cstride]
1256 = coefficients.at({IntegralType::interior_facet, i});
1257 std::span<const std::int32_t> facets
1258 = L.domain(IntegralType::interior_facet, i);
1259 if (bs == 1)
1260 {
1261 impl::assemble_interior_facets<T, 1>(
1262 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1263 {*dofmap, bs, L.domain(IntegralType::interior_facet, i, *mesh0)}, fn,
1264 constants, coeffs, cstride, cell_info0, perms);
1265 }
1266 else if (bs == 3)
1267 {
1268 impl::assemble_interior_facets<T, 3>(
1269 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1270 {*dofmap, bs, L.domain(IntegralType::interior_facet, i, *mesh0)}, fn,
1271 constants, coeffs, cstride, cell_info0, perms);
1272 }
1273 else
1274 {
1275 impl::assemble_interior_facets(
1276 P0, b, x_dofmap, x, num_facets_per_cell, facets,
1277 {*dofmap, bs, L.domain(IntegralType::interior_facet, i, *mesh0)}, fn,
1278 constants, coeffs, cstride, cell_info0, perms);
1279 }
1280 }
1281}
1282
1289template <dolfinx::scalar T, std::floating_point U>
1290void assemble_vector(
1291 std::span<T> b, const Form<T, U>& L, std::span<const T> constants,
1292 const std::map<std::pair<IntegralType, int>,
1293 std::pair<std::span<const T>, int>>& coefficients)
1294{
1295 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
1296 assert(mesh);
1297 if constexpr (std::is_same_v<U, scalar_value_type_t<T>>)
1298 assemble_vector(b, L, mesh->geometry().dofmap(), mesh->geometry().x(),
1299 constants, coefficients);
1300 else
1301 {
1302 auto x = mesh->geometry().x();
1303 std::vector<scalar_value_type_t<T>> _x(x.begin(), x.end());
1304 assemble_vector(b, L, mesh->geometry().dofmap(), _x, constants,
1305 coefficients);
1306 }
1307}
1308} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Definition types.h:20
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
@ interior_facet
Interior facet.
@ exterior_facet
Exterior facet.
int cell_num_entities(CellType type, int dim)
Definition cell_types.cpp:139
CellType
Cell type identifier.
Definition cell_types.h:22