DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
assemble_matrix_impl.h
1// Copyright (C) 2018-2019 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 "DofMap.h"
10#include "Form.h"
11#include "FunctionSpace.h"
12#include "traits.h"
13#include "utils.h"
14#include <algorithm>
15#include <dolfinx/la/utils.h>
16#include <dolfinx/mesh/Geometry.h>
17#include <dolfinx/mesh/Mesh.h>
18#include <dolfinx/mesh/Topology.h>
19#include <functional>
20#include <iterator>
21#include <span>
22#include <tuple>
23#include <vector>
24
25namespace dolfinx::fem::impl
26{
27bool has_bc(auto& dofs, auto& bc, auto bs)
28{
29 for (auto dof : dofs)
30 for (int k = 0; k < bs; ++k)
31 if (bc[bs * dof + k])
32 return true;
33 return false;
34};
35
37using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
38
92template <bool LiftingMode, dolfinx::scalar T, std::floating_point U>
93void assemble_cells_matrix(
94 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
95 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
96 std::span<const std::int32_t> cells,
97 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
98 const fem::DofTransformKernel<T> auto& P0,
99 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
100 const fem::DofTransformKernel<T> auto& P1T,
101 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1,
102 const FEkernel<T, U> auto& kernel,
103 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
104 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
105 std::span<const std::uint32_t> cell_info1, std::span<T> Ab,
106 std::span<U> cdofs_b)
107{
108 if (cells.empty())
109 return;
110
111 const auto [dmap0, bs0, cells0] = dofmap0;
112 const auto [dmap1, bs1, cells1] = dofmap1;
113
114 // Iterate over active cells
115 std::size_t num_dofs0 = dmap0.extent(1);
116 std::size_t num_dofs1 = dmap1.extent(1);
117 std::size_t ndim0 = bs0 * num_dofs0;
118 std::size_t ndim1 = bs1 * num_dofs1;
119
120 assert(Ab.size() >= ndim0 * ndim1);
121 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
122 auto Ae = Ab.first(ndim0 * ndim1);
123
124 // Iterate over active cells
125 assert(cells0.size() == cells.size());
126 assert(cells1.size() == cells.size());
127 for (std::size_t c = 0; c < cells.size(); ++c)
128 {
129 // Cell index in integration domain mesh (c), test function mesh
130 // (c0) and trial function mesh (c1)
131 std::int32_t cell = cells[c];
132 std::int32_t cell0 = cells0[c];
133 std::int32_t cell1 = cells1[c];
134
135 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
136 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
137
138 // In "LiftingMode" only execute kernel if there are BCs on column space
139 if constexpr (LiftingMode)
140 {
141 if (!has_bc(dofs1, bc1, bs1))
142 continue;
143 }
144
145 // Get cell coordinates/geometry
146 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
147 for (std::size_t i = 0; i < x_dofs.size(); ++i)
148 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs_b.begin(), 3 * i));
149
150 // Tabulate tensor
151 std::ranges::fill(Ae, 0);
152 kernel(Ae.data(), &coeffs(c, 0), constants.data(), cdofs_b.data(), nullptr,
153 nullptr, nullptr);
154
155 // Compute A = P_0 \tilde{A} P_1^T (dof transformation)
156 P0(Ae, cell_info0, cell0, ndim1); // B = P0 \tilde{A}
157 P1T(Ae, cell_info1, cell1, ndim0); // A = B P1_T
158
159 // In lifting mode only BC dofs are assembled, while in standard mode these
160 // row/column dofs are zeroed.
161 if constexpr (!LiftingMode)
162 {
163 // Zero rows and columns for BCs
164 if (!bc0.empty())
165 {
166 for (std::size_t i = 0; i < num_dofs0; ++i)
167 {
168 for (int k = 0; k < bs0; ++k)
169 {
170 if (bc0[bs0 * dofs0[i] + k])
171 {
172 // Zero row bs0 * i + k
173 const int row = bs0 * i + k;
174 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
175 }
176 }
177 }
178 }
179
180 if (!bc1.empty())
181 {
182 for (std::size_t j = 0; j < num_dofs1; ++j)
183 {
184 for (int k = 0; k < bs1; ++k)
185 {
186 if (bc1[bs1 * dofs1[j] + k])
187 {
188 // Zero column bs1 * j + k
189 int col = bs1 * j + k;
190 for (std::size_t row = 0; row < ndim0; ++row)
191 Ae[row * ndim1 + col] = 0;
192 }
193 }
194 }
195 }
196 }
197
198 mat_set(dofs0, dofs1, Ae);
199 }
200}
201
264template <bool LiftingMode, dolfinx::scalar T, std::floating_point U>
265void assemble_entities(
266 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
267 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
268 md::mdspan<const std::int32_t,
269 std::extents<std::size_t, md::dynamic_extent, 2>>
270 entities,
271 std::tuple<mdspan2_t, int,
272 md::mdspan<const std::int32_t,
273 std::extents<std::size_t, md::dynamic_extent, 2>>>
274 dofmap0,
275 const fem::DofTransformKernel<T> auto& P0,
276 std::tuple<mdspan2_t, int,
277 md::mdspan<const std::int32_t,
278 std::extents<std::size_t, md::dynamic_extent, 2>>>
279 dofmap1,
280 const fem::DofTransformKernel<T> auto& P1T,
281 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1,
282 const FEkernel<T, U> auto& kernel,
283 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
284 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
285 std::span<const std::uint32_t> cell_info1,
286 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
287 std::span<T> Ab, std::span<U> cdofs_b)
288{
289 if (entities.empty())
290 return;
291
292 const auto [dmap0, bs0, entities0] = dofmap0;
293 const auto [dmap1, bs1, entities1] = dofmap1;
294
295 std::size_t num_dofs0 = dmap0.extent(1);
296 std::size_t num_dofs1 = dmap1.extent(1);
297 std::size_t ndim0 = bs0 * num_dofs0;
298 std::size_t ndim1 = bs1 * num_dofs1;
299 assert(entities0.size() == entities.size());
300 assert(entities1.size() == entities.size());
301 assert(Ab.size() >= ndim0 * ndim1);
302 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
303 auto Ae = Ab.first(ndim0 * ndim1);
304 for (std::size_t f = 0; f < entities.extent(0); ++f)
305 {
306 // Cell in the integration domain, local entity index relative to the
307 // integration domain cell, and cells in the test and trial function
308 // meshes
309 std::int32_t cell = entities(f, 0);
310 std::int32_t local_entity = entities(f, 1);
311 std::int32_t cell0 = entities0(f, 0);
312 std::int32_t cell1 = entities1(f, 0);
313
314 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
315 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
316
317 // Check for BCs on column space
318 if constexpr (LiftingMode)
319 {
320 if (!has_bc(dofs1, bc1, bs1))
321 continue;
322 }
323
324 // Get cell coordinates/geometry
325 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
326 for (std::size_t i = 0; i < x_dofs.size(); ++i)
327 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs_b.begin(), 3 * i));
328
329 // Permutations
330 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
331
332 // Tabulate tensor
333 std::ranges::fill(Ae, 0);
334 kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs_b.data(),
335 &local_entity, &perm, nullptr);
336 P0(Ae, cell_info0, cell0, ndim1);
337 P1T(Ae, cell_info1, cell1, ndim0);
338
339 // Don't clear rows/cols in LiftingMode
340 if constexpr (!LiftingMode)
341 {
342 // Zero rows and columns for BCs
343 if (!bc0.empty())
344 {
345 for (std::size_t i = 0; i < num_dofs0; ++i)
346 {
347 for (int k = 0; k < bs0; ++k)
348 {
349 if (bc0[bs0 * dofs0[i] + k])
350 {
351 // Zero row bs0 * i + k
352 const int row = bs0 * i + k;
353 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
354 }
355 }
356 }
357 }
358
359 if (!bc1.empty())
360 {
361 for (std::size_t j = 0; j < num_dofs1; ++j)
362 {
363 for (int k = 0; k < bs1; ++k)
364 {
365 if (bc1[bs1 * dofs1[j] + k])
366 {
367 // Zero column bs1 * j + k
368 int col = bs1 * j + k;
369 for (std::size_t row = 0; row < ndim0; ++row)
370 Ae[row * ndim1 + col] = 0;
371 }
372 }
373 }
374 }
375 }
376
377 mat_set(dofs0, dofs1, Ae);
378 }
379}
380
442template <bool LiftingMode, dolfinx::scalar T, std::floating_point U>
443void assemble_interior_facets(
444 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
445 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
446 md::mdspan<const std::int32_t,
447 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
448 facets,
449 std::tuple<const DofMap&, int,
450 md::mdspan<const std::int32_t,
451 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
452 dofmap0,
453 const fem::DofTransformKernel<T> auto& P0,
454 std::tuple<const DofMap&, int,
455 md::mdspan<const std::int32_t,
456 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
457 dofmap1,
458 const fem::DofTransformKernel<T> auto& P1T,
459 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1,
460 const FEkernel<T, U> auto& kernel,
461 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
462 md::dynamic_extent>>
463 coeffs,
464 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
465 std::span<const std::uint32_t> cell_info1,
466 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
467 std::span<T> Ab, std::span<U> cdofs_b, std::span<std::int32_t> dofs_b,
468 std::span<T> Ae_block_b)
469{
470 if (facets.empty())
471 return;
472
473 const auto [dmap0, bs0, facets0] = dofmap0;
474 const auto [dmap1, bs1, facets1] = dofmap1;
475
476 // Data structures used in assembly
477 assert(cdofs_b.size() >= 2 * 3 * x_dofmap.extent(1));
478 auto cdofs0 = cdofs_b.first(3 * x_dofmap.extent(1));
479 auto cdofs1 = cdofs_b.last(3 * x_dofmap.extent(1));
480
481 std::size_t dmap0_size = dmap0.map().extent(1);
482 std::size_t dmap1_size = dmap1.map().extent(1);
483 std::size_t num_rows = bs0 * 2 * dmap0_size;
484 std::size_t num_cols = bs1 * 2 * dmap1_size;
485
486 // Dofmap data structures
487 assert(dofs_b.size() >= (2 * dmap0_size) + (2 * dmap1_size));
488 auto dmapjoint0 = dofs_b.first(2 * dmap0_size);
489 auto dmapjoint1 = dofs_b.last(2 * dmap1_size);
490
491 assert(facets0.size() == facets.size());
492 assert(facets1.size() == facets.size());
493 assert(Ab.size() >= num_rows * num_cols);
494 auto Ae = Ab.first(num_rows * num_cols);
495
496 // Buffer used to gather a contiguous (test, trial) block of Ae when
497 // one of the two cells attached to the facet does not exist in the
498 // test/trial function domain (e.g. an interface between two
499 // domains) -- the sparsity pattern only holds entries for blocks
500 // where both cells exist, so such blocks must be inserted
501 // individually rather than as part of the full joint block.
502 assert(Ae_block_b.size() >= dmap0_size * bs0 * dmap1_size * bs1);
503 auto insert_block = [&Ae_block_b, &Ae, &bs0, &bs1, &num_cols,
504 &mat_set](std::span<const std::int32_t> rdofs,
505 std::span<const std::int32_t> cdofs,
506 std::size_t row_offset, std::size_t col_offset)
507 {
508 if (rdofs.empty() or cdofs.empty())
509 return;
510 auto Ae_block = Ae_block_b.first(rdofs.size() * bs0 * cdofs.size() * bs1);
511 for (std::size_t i = 0; i < rdofs.size() * bs0; ++i)
512 {
513 auto row
514 = std::next(Ae.begin(), (row_offset + i) * num_cols + col_offset);
515 std::copy_n(row, cdofs.size() * bs1,
516 std::next(Ae_block.begin(), i * cdofs.size() * bs1));
517 }
518 mat_set(rdofs, cdofs, Ae_block);
519 };
520
521 for (std::size_t f = 0; f < facets.extent(0); ++f)
522 {
523 // Cells in integration domain, test function domain and trial
524 // function domain
525 std::array cells{facets(f, 0, 0), facets(f, 1, 0)};
526 std::array cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
527 std::array cells1{facets1(f, 0, 0), facets1(f, 1, 0)};
528
529 // Local facets indices
530 std::array local_facet{facets(f, 0, 1), facets(f, 1, 1)};
531
532 // Get cell geometry
533 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
534 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
535 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
536 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
537 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
538 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
539
540 // Get dof maps for cells and pack
541 // When integrating over interfaces between two domains, the test function
542 // might only be defined on one side, so we check which cells exist in the
543 // test function domain
544 std::span<const std::int32_t> dmap0_cell0
545 = cells0[0] >= 0 ? dmap0.cell_dofs(cells0[0])
546 : std::span<const std::int32_t>();
547 std::span<const std::int32_t> dmap0_cell1
548 = cells0[1] >= 0 ? dmap0.cell_dofs(cells0[1])
549 : std::span<const std::int32_t>();
550
551 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
552 std::ranges::copy(dmap0_cell1, std::next(dmapjoint0.begin(), dmap0_size));
553
554 // Check which cells exist in the trial function domain
555 std::span<const std::int32_t> dmap1_cell0
556 = cells1[0] >= 0 ? dmap1.cell_dofs(cells1[0])
557 : std::span<const std::int32_t>();
558 std::span<const std::int32_t> dmap1_cell1
559 = cells1[1] >= 0 ? dmap1.cell_dofs(cells1[1])
560 : std::span<const std::int32_t>();
561
562 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
563 std::ranges::copy(dmap1_cell1, std::next(dmapjoint1.begin(), dmap1_size));
564
565 // Check for BCs on column space
566 if constexpr (LiftingMode)
567 {
568 if (!has_bc(dmapjoint1, bc1, bs1))
569 continue;
570 }
571
572 // Tabulate tensor
573 std::ranges::fill(Ae, 0);
574 std::array perm = perms.empty()
575 ? std::array<std::uint8_t, 2>{0, 0}
576 : std::array{perms(cells[0], local_facet[0]),
577 perms(cells[1], local_facet[1])};
578 kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs_b.data(),
579 local_facet.data(), perm.data(), nullptr);
580
581 // Local element layout is a 2x2 block matrix with structure
582 //
583 // cell0cell0 | cell0cell1
584 // cell1cell0 | cell1cell1
585 //
586 // where each block is element tensor of size (dmap0, dmap1).
587
588 // Only apply transformation when cells exist
589 if (cells0[0] >= 0)
590 P0(Ae, cell_info0, cells0[0], num_cols);
591 if (cells0[1] >= 0)
592 {
593 std::span sub_Ae0(Ae.data() + bs0 * dmap0_size * num_cols,
594 bs0 * dmap0_size * num_cols);
595
596 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
597 }
598 if (cells1[0] >= 0)
599 P1T(Ae, cell_info1, cells1[0], num_rows);
600
601 if (cells1[1] >= 0)
602 {
603 for (std::size_t row = 0; row < num_rows; ++row)
604 {
605 // DOFs for dmap1 and cell1 are not stored contiguously in the
606 // block matrix, so each row needs a separate span access
607 std::span sub_Ae1(Ae.data() + row * num_cols + bs1 * dmap1_size,
608 bs1 * dmap1_size);
609 P1T(sub_Ae1, cell_info1, cells1[1], 1);
610 }
611 }
612
613 // Clear rows/cols if not in LiftingMode
614 if constexpr (!LiftingMode)
615 {
616 // Zero rows and columns for BCs
617 if (!bc0.empty())
618 {
619 for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
620 {
621 for (int k = 0; k < bs0; ++k)
622 {
623 if (bc0[bs0 * dmapjoint0[i] + k])
624 {
625 // Zero row bs0 * i + k
626 std::fill_n(std::next(Ae.begin(), num_cols * (bs0 * i + k)),
627 num_cols, 0);
628 }
629 }
630 }
631 }
632
633 if (!bc1.empty())
634 {
635 for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
636 {
637 for (int k = 0; k < bs1; ++k)
638 {
639 if (bc1[bs1 * dmapjoint1[j] + k])
640 {
641 // Zero column bs1 * j + k
642 for (std::size_t m = 0; m < num_rows; ++m)
643 Ae[m * num_cols + bs1 * j + k] = 0;
644 }
645 }
646 }
647 }
648 }
649
650 // The common case is that a cell exists on both sides of the
651 // facet for both the test and trial function domains, in which
652 // case the full joint block can be inserted in one go. Otherwise
653 // (e.g. an interface between two domains), only the blocks
654 // corresponding to existing (test, trial) cell pairs are present
655 // in the sparsity pattern, so each must be inserted individually.
656 if (cells0[0] >= 0 and cells0[1] >= 0 and cells1[0] >= 0 and cells1[1] >= 0)
657 mat_set(dmapjoint0, dmapjoint1, Ae);
658 else
659 {
660 insert_block(dmap0_cell0, dmap1_cell0, 0, 0);
661 insert_block(dmap0_cell0, dmap1_cell1, 0, bs1 * dmap1_size);
662 insert_block(dmap0_cell1, dmap1_cell0, bs0 * dmap0_size, 0);
663 insert_block(dmap0_cell1, dmap1_cell1, bs0 * dmap0_size,
664 bs1 * dmap1_size);
665 }
666 }
667}
668
697template <bool LiftingMode, dolfinx::scalar T, std::floating_point U>
698void assemble_matrix(
699 la::MatSet<T> auto mat_set, const Form<T, U>& a,
700 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
701 std::span<const T> constants,
702 const std::map<std::pair<IntegralType, int>,
703 std::pair<std::span<const T>, int>>& coefficients,
704 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1)
705{
706 // Integration domain mesh
707 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
708 assert(mesh);
709
710 // Test function mesh
711 auto mesh0 = a.function_spaces().at(0)->mesh();
712 assert(mesh0);
713
714 // Trial function mesh
715 auto mesh1 = a.function_spaces().at(1)->mesh();
716 assert(mesh1);
717
718 // TODO: Mixed topology with exterior and interior facet integrals.
719 //
720 // NOTE: Can't just loop over cell types for interior facet integrals
721 // because we have a kernel per combination of comparable cell types,
722 // rather than one per cell type. Also, we need the dofmaps for two
723 // different cell types at the same time.
724 const int num_cell_types = mesh->topology()->cell_types().size();
725 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
726 {
727 // Geometry dofmap and data
728 mdspan2_t x_dofmap = mesh->geometry().dofmaps().at(cell_type_idx);
729
730 // Get dofmap data
731 std::shared_ptr<const fem::DofMap> dofmap0
732 = a.function_spaces().at(0)->dofmaps().at(cell_type_idx);
733 std::shared_ptr<const fem::DofMap> dofmap1
734 = a.function_spaces().at(1)->dofmaps().at(cell_type_idx);
735 assert(dofmap0);
736 assert(dofmap1);
737 md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> dofs0
738 = dofmap0->map();
739 const int bs0 = dofmap0->bs();
740 md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> dofs1
741 = dofmap1->map();
742 const int bs1 = dofmap1->bs();
743
744 // Buffers reused across all integral kernels for this cell type,
745 // sized for the worst case (interior facets, which touch two cells).
746 std::vector<T> Ab((2 * bs0 * dofs0.extent(1))
747 * (2 * bs1 * dofs1.extent(1)));
748 std::vector<U> cdofs_b(2 * 3 * x_dofmap.extent(1));
749 std::size_t dmap0_size = dofmap0->map().extent(1);
750 std::size_t dmap1_size = dofmap1->map().extent(1);
751 std::vector<std::int32_t> dmap_b((2 * dmap0_size) + (2 * dmap1_size));
752 std::vector<T> Ae_block_b(dmap0_size * bs0 * dmap1_size * bs1);
753
754 auto element0 = a.function_spaces().at(0)->elements(cell_type_idx);
755 assert(element0);
756 auto element1 = a.function_spaces().at(1)->elements(cell_type_idx);
757 assert(element1);
758 const fem::DofTransformKernel<T> auto& P0
759 = element0->template dof_transformation_fn<T>(doftransform::standard);
760 const fem::DofTransformKernel<T> auto& P1T
761 = element1->template dof_transformation_right_fn<T>(
763
764 std::span<const std::uint32_t> cell_info0;
765 std::span<const std::uint32_t> cell_info1;
766 if (element0->needs_dof_transformations()
767 or element1->needs_dof_transformations()
768 or a.needs_facet_permutations())
769 {
770 mesh0->topology_mutable()->create_entity_permutations();
771 mesh1->topology_mutable()->create_entity_permutations();
772 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
773 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
774 }
775
776 for (int i = 0; i < a.num_integrals(IntegralType::cell, cell_type_idx); ++i)
777 {
778 auto fn = a.kernel(IntegralType::cell, i, cell_type_idx);
779 assert(fn);
780 std::span cells = a.domain(IntegralType::cell, i, cell_type_idx);
781 std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
782 std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, cell_type_idx);
783 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
784 assert(cells.size() * cstride == coeffs.size());
785 impl::assemble_cells_matrix<LiftingMode>(
786 mat_set, x_dofmap, x, cells, {dofs0, bs0, cells0}, P0,
787 {dofs1, bs1, cells1}, P1T, bc0, bc1, fn,
788 md::mdspan(coeffs.data(), cells.size(), cstride), constants,
789 cell_info0, cell_info1, std::span(Ab), std::span(cdofs_b));
790 }
791
792 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
793 if (a.needs_facet_permutations())
794 {
795 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
796 int num_facets_per_cell
797 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
798 mesh->topology_mutable()->create_entity_permutations();
799 const std::vector<std::uint8_t>& p
800 = mesh->topology()->get_facet_permutations();
801 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
802 num_facets_per_cell);
803 }
804
805 for (int i = 0;
806 i < a.num_integrals(IntegralType::interior_facet, cell_type_idx); ++i)
807 {
808 if (num_cell_types > 1)
809 {
810 throw std::runtime_error("Interior facet integrals with mixed "
811 "topology aren't supported yet");
812 }
813
814 using mdspanx22_t
815 = md::mdspan<const std::int32_t,
816 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
817 using mdspanx2x_t
818 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
819 md::dynamic_extent>>;
820
821 auto fn = a.kernel(IntegralType::interior_facet, i, 0);
822 assert(fn);
823 auto& [coeffs, cstride]
824 = coefficients.at({IntegralType::interior_facet, i});
825
826 std::span facets = a.domain(IntegralType::interior_facet, i, 0);
827 std::span facets0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
828 std::span facets1 = a.domain_arg(IntegralType::interior_facet, 1, i, 0);
829 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
830 impl::assemble_interior_facets<LiftingMode>(
831 mat_set, x_dofmap, x,
832 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
833 {*dofmap0, bs0,
834 mdspanx22_t(facets0.data(), facets0.size() / 4, 2, 2)},
835 P0,
836 {*dofmap1, bs1,
837 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
838 P1T, bc0, bc1, fn,
839 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride), constants,
840 cell_info0, cell_info1, facet_perms, std::span(Ab),
841 std::span(cdofs_b), dmap_b, std::span(Ae_block_b));
842 }
843
844 for (auto itg_type : {fem::IntegralType::exterior_facet,
846 {
847 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
849 ? facet_perms
850 : md::mdspan<const std::uint8_t,
851 md::dextents<std::size_t, 2>>{};
852
853 for (int i = 0; i < a.num_integrals(itg_type, cell_type_idx); ++i)
854 {
855 if (num_cell_types > 1)
856 {
857 throw std::runtime_error("Exterior facet integrals with mixed "
858 "topology aren't supported yet");
859 }
860
861 using mdspanx2_t
862 = md::mdspan<const std::int32_t,
863 md::extents<std::size_t, md::dynamic_extent, 2>>;
864
865 auto fn = a.kernel(itg_type, i, 0);
866 assert(fn);
867 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
868
869 std::span e = a.domain(itg_type, i, 0);
870 mdspanx2_t entities(e.data(), e.size() / 2, 2);
871 std::span e0 = a.domain_arg(itg_type, 0, i, 0);
872 mdspanx2_t entities0(e0.data(), e0.size() / 2, 2);
873 std::span e1 = a.domain_arg(itg_type, 1, i, 0);
874 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
875 assert((entities.size() / 2) * cstride == coeffs.size());
876 impl::assemble_entities<LiftingMode>(
877 mat_set, x_dofmap, x, entities, {dofs0, bs0, entities0}, P0,
878 {dofs1, bs1, entities1}, P1T, bc0, bc1, fn,
879 md::mdspan(coeffs.data(), entities.extent(0), cstride), constants,
880 cell_info0, cell_info1, perms, std::span(Ab), std::span(cdofs_b));
881 }
882 }
883 }
884}
885} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
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
@ transpose
Transpose.
Definition FiniteElement.h:28
@ 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