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{
28using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
29
65template <dolfinx::scalar T, std::floating_point U, bool LiftingMode = false>
66void assemble_cells_matrix(
67 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
68 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
69 std::span<const std::int32_t> cells,
70 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
71 fem::DofTransformKernel<T> auto P0,
72 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
73 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
74 std::span<const std::int8_t> bc1, FEkernel<T, U> auto kernel,
75 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
76 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
77 std::span<const std::uint32_t> cell_info1)
78{
79 if (cells.empty())
80 return;
81
82 const auto [dmap0, bs0, cells0] = dofmap0;
83 const auto [dmap1, bs1, cells1] = dofmap1;
84
85 // Iterate over active cells
86 const int num_dofs0 = dmap0.extent(1);
87 const int num_dofs1 = dmap1.extent(1);
88 const int ndim0 = bs0 * num_dofs0;
89 const int ndim1 = bs1 * num_dofs1;
90 std::vector<T> Ae(ndim0 * ndim1);
91 std::vector<U> cdofs(3 * x_dofmap.extent(1));
92
93 // Iterate over active cells
94 assert(cells0.size() == cells.size());
95 assert(cells1.size() == cells.size());
96 for (std::size_t c = 0; c < cells.size(); ++c)
97 {
98 // Cell index in integration domain mesh (c), test function mesh
99 // (c0) and trial function mesh (c1)
100 std::int32_t cell = cells[c];
101 std::int32_t cell0 = cells0[c];
102 std::int32_t cell1 = cells1[c];
103
104 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
105 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
106
107 // In "LiftingMode" only execute kernel if there are BCs on column space
108 if constexpr (LiftingMode)
109 {
110 auto has_bc = [&]()
111 {
112 for (std::int32_t dof : dofs1)
113 {
114 for (int k = 0; k < bs1; ++k)
115 {
116 if (bc1[bs1 * dof + k])
117 return true;
118 }
119 }
120 return false;
121 };
122
123 if (!has_bc())
124 continue;
125 }
126
127 // Get cell coordinates/geometry
128 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
129 for (std::size_t i = 0; i < x_dofs.size(); ++i)
130 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
131
132 // Tabulate tensor
133 std::ranges::fill(Ae, 0);
134 kernel(Ae.data(), &coeffs(c, 0), constants.data(), cdofs.data(), nullptr,
135 nullptr, nullptr);
136
137 // Compute A = P_0 \tilde{A} P_1^T (dof transformation)
138 P0(Ae, cell_info0, cell0, ndim1); // B = P0 \tilde{A}
139 P1T(Ae, cell_info1, cell1, ndim0); // A = B P1_T
140
141 // In lifting mode only BC dofs are assembled, while in standard mode these
142 // row/column dofs are zeroed.
143 if constexpr (!LiftingMode)
144 {
145 // Zero rows and columns for BCs
146 if (!bc0.empty())
147 {
148 for (int i = 0; i < num_dofs0; ++i)
149 {
150 for (int k = 0; k < bs0; ++k)
151 {
152 if (bc0[bs0 * dofs0[i] + k])
153 {
154 // Zero row bs0 * i + k
155 const int row = bs0 * i + k;
156 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
157 }
158 }
159 }
160 }
161 if (!bc1.empty())
162 {
163 for (int j = 0; j < num_dofs1; ++j)
164 {
165 for (int k = 0; k < bs1; ++k)
166 {
167 if (bc1[bs1 * dofs1[j] + k])
168 {
169 // Zero col bs1 * j + k
170 const int col = bs1 * j + k;
171 for (int row = 0; row < ndim0; ++row)
172 Ae[row * ndim1 + col] = 0;
173 }
174 }
175 }
176 }
177 }
178
179 mat_set(dofs0, dofs1, Ae);
180 }
181}
182
227template <dolfinx::scalar T, std::floating_point U, bool LiftingMode = false>
228void assemble_entities(
229 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
230 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
231 md::mdspan<const std::int32_t,
232 std::extents<std::size_t, md::dynamic_extent, 2>>
233 entities,
234 std::tuple<mdspan2_t, int,
235 md::mdspan<const std::int32_t,
236 std::extents<std::size_t, md::dynamic_extent, 2>>>
237 dofmap0,
238 fem::DofTransformKernel<T> auto P0,
239 std::tuple<mdspan2_t, int,
240 md::mdspan<const std::int32_t,
241 std::extents<std::size_t, md::dynamic_extent, 2>>>
242 dofmap1,
243 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
244 std::span<const std::int8_t> bc1, FEkernel<T, U> auto kernel,
245 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
246 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
247 std::span<const std::uint32_t> cell_info1,
248 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
249{
250 if (entities.empty())
251 return;
252
253 const auto [dmap0, bs0, entities0] = dofmap0;
254 const auto [dmap1, bs1, entities1] = dofmap1;
255
256 // Data structures used in assembly
257 std::vector<U> cdofs(3 * x_dofmap.extent(1));
258 const int num_dofs0 = dmap0.extent(1);
259 const int num_dofs1 = dmap1.extent(1);
260 const int ndim0 = bs0 * num_dofs0;
261 const int ndim1 = bs1 * num_dofs1;
262 std::vector<T> Ae(ndim0 * ndim1);
263
264 assert(entities0.size() == entities.size());
265 assert(entities1.size() == entities.size());
266 for (std::size_t f = 0; f < entities.extent(0); ++f)
267 {
268 // Cell in the integration domain, local entity index relative to the
269 // integration domain cell, and cells in the test and trial function
270 // meshes
271 std::int32_t cell = entities(f, 0);
272 std::int32_t local_entity = entities(f, 1);
273 std::int32_t cell0 = entities0(f, 0);
274 std::int32_t cell1 = entities1(f, 0);
275
276 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
277 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
278
279 // Check for BCs on column space
280 if constexpr (LiftingMode)
281 {
282 auto has_bc = [&]()
283 {
284 for (std::int32_t dof : dofs1)
285 {
286 for (int k = 0; k < bs1; ++k)
287 {
288 if (bc1[bs1 * dof + k])
289 return true;
290 }
291 }
292 return false;
293 };
294 if (!has_bc())
295 continue;
296 }
297
298 // Get cell coordinates/geometry
299 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
300 for (std::size_t i = 0; i < x_dofs.size(); ++i)
301 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
302
303 // Permutations
304 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
305
306 // Tabulate tensor
307 std::ranges::fill(Ae, 0);
308 kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
309 &local_entity, &perm, nullptr);
310 P0(Ae, cell_info0, cell0, ndim1);
311 P1T(Ae, cell_info1, cell1, ndim0);
312
313 // Don't clear rows/cols in LiftingMode
314 if constexpr (!LiftingMode)
315 {
316 // Zero rows and columns for BCs
317 if (!bc0.empty())
318 {
319 for (int i = 0; i < num_dofs0; ++i)
320 {
321 for (int k = 0; k < bs0; ++k)
322 {
323 if (bc0[bs0 * dofs0[i] + k])
324 {
325 // Zero row bs0 * i + k
326 const int row = bs0 * i + k;
327 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
328 }
329 }
330 }
331 }
332 if (!bc1.empty())
333 {
334 for (int j = 0; j < num_dofs1; ++j)
335 {
336 for (int k = 0; k < bs1; ++k)
337 {
338 if (bc1[bs1 * dofs1[j] + k])
339 {
340 // Zero column bs1 * j + k
341 const int col = bs1 * j + k;
342 for (int row = 0; row < ndim0; ++row)
343 Ae[row * ndim1 + col] = 0;
344 }
345 }
346 }
347 }
348 }
349
350 mat_set(dofs0, dofs1, Ae);
351 }
352}
353
392template <dolfinx::scalar T, std::floating_point U, bool LiftingMode = false>
393void assemble_interior_facets(
394 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
395 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
396 md::mdspan<const std::int32_t,
397 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
398 facets,
399 std::tuple<const DofMap&, int,
400 md::mdspan<const std::int32_t,
401 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
402 dofmap0,
403 fem::DofTransformKernel<T> auto P0,
404 std::tuple<const DofMap&, int,
405 md::mdspan<const std::int32_t,
406 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
407 dofmap1,
408 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
409 std::span<const std::int8_t> bc1, FEkernel<T, U> auto kernel,
410 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
411 md::dynamic_extent>>
412 coeffs,
413 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
414 std::span<const std::uint32_t> cell_info1,
415 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
416{
417 if (facets.empty())
418 return;
419
420 const auto [dmap0, bs0, facets0] = dofmap0;
421 const auto [dmap1, bs1, facets1] = dofmap1;
422
423 // Data structures used in assembly
424 using X = U;
425 std::vector<X> cdofs(2 * x_dofmap.extent(1) * 3);
426 std::span<X> cdofs0(cdofs.data(), x_dofmap.extent(1) * 3);
427 std::span<X> cdofs1(cdofs.data() + x_dofmap.extent(1) * 3,
428 x_dofmap.extent(1) * 3);
429
430 const std::size_t dmap0_size = dmap0.map().extent(1);
431 const std::size_t dmap1_size = dmap1.map().extent(1);
432 const int num_rows = bs0 * 2 * dmap0_size;
433 const int num_cols = bs1 * 2 * dmap1_size;
434
435 // Temporaries for joint dofmaps
436 std::vector<T> Ae(num_rows * num_cols);
437 std::vector<std::int32_t> dmapjoint0(2 * dmap0_size);
438 std::vector<std::int32_t> dmapjoint1(2 * dmap1_size);
439 assert(facets0.size() == facets.size());
440 assert(facets1.size() == facets.size());
441 for (std::size_t f = 0; f < facets.extent(0); ++f)
442 {
443 // Cells in integration domain, test function domain and trial
444 // function domain
445 std::array cells{facets(f, 0, 0), facets(f, 1, 0)};
446 std::array cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
447 std::array cells1{facets1(f, 0, 0), facets1(f, 1, 0)};
448
449 // Local facets indices
450 std::array local_facet{facets(f, 0, 1), facets(f, 1, 1)};
451
452 // Get cell geometry
453 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
454 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
455 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
456 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
457 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
458 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
459
460 // Get dof maps for cells and pack
461 // When integrating over interfaces between two domains, the test function
462 // might only be defined on one side, so we check which cells exist in the
463 // test function domain
464 std::span<const std::int32_t> dmap0_cell0
465 = cells0[0] >= 0 ? dmap0.cell_dofs(cells0[0])
466 : std::span<const std::int32_t>();
467 std::span<const std::int32_t> dmap0_cell1
468 = cells0[1] >= 0 ? dmap0.cell_dofs(cells0[1])
469 : std::span<const std::int32_t>();
470
471 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
472 std::ranges::copy(dmap0_cell1, std::next(dmapjoint0.begin(), dmap0_size));
473
474 // Check which cells exist in the trial function domain
475 std::span<const std::int32_t> dmap1_cell0
476 = cells1[0] >= 0 ? dmap1.cell_dofs(cells1[0])
477 : std::span<const std::int32_t>();
478 std::span<const std::int32_t> dmap1_cell1
479 = cells1[1] >= 0 ? dmap1.cell_dofs(cells1[1])
480 : std::span<const std::int32_t>();
481
482 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
483 std::ranges::copy(dmap1_cell1, std::next(dmapjoint1.begin(), dmap1_size));
484
485 // Check for BCs on column space
486 if constexpr (LiftingMode)
487 {
488 auto has_bc = [&]()
489 {
490 for (std::int32_t dof : dmapjoint1)
491 {
492 for (int k = 0; k < bs1; ++k)
493 {
494 if (bc1[bs1 * dof + k])
495 return true;
496 }
497 }
498 return false;
499 };
500
501 if (!has_bc())
502 continue;
503 }
504
505 // Tabulate tensor
506 std::ranges::fill(Ae, 0);
507 std::array perm = perms.empty()
508 ? std::array<std::uint8_t, 2>{0, 0}
509 : std::array{perms(cells[0], local_facet[0]),
510 perms(cells[1], local_facet[1])};
511 kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
512 local_facet.data(), perm.data(), nullptr);
513
514 // Local element layout is a 2x2 block matrix with structure
515 //
516 // cell0cell0 | cell0cell1
517 // cell1cell0 | cell1cell1
518 //
519 // where each block is element tensor of size (dmap0, dmap1).
520
521 // Only apply transformation when cells exist
522 if (cells0[0] >= 0)
523 P0(Ae, cell_info0, cells0[0], num_cols);
524 if (cells0[1] >= 0)
525 {
526 std::span sub_Ae0(Ae.data() + bs0 * dmap0_size * num_cols,
527 bs0 * dmap0_size * num_cols);
528
529 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
530 }
531 if (cells1[0] >= 0)
532 P1T(Ae, cell_info1, cells1[0], num_rows);
533
534 if (cells1[1] >= 0)
535 {
536 for (int row = 0; row < num_rows; ++row)
537 {
538 // DOFs for dmap1 and cell1 are not stored contiguously in the
539 // block matrix, so each row needs a separate span access
540 std::span sub_Ae1(Ae.data() + row * num_cols + bs1 * dmap1_size,
541 bs1 * dmap1_size);
542 P1T(sub_Ae1, cell_info1, cells1[1], 1);
543 }
544 }
545
546 // Don't clear rows/cols in LiftingMode
547 if constexpr (!LiftingMode)
548 {
549 // Zero rows and columns for BCs
550 if (!bc0.empty())
551 {
552 for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
553 {
554 for (int k = 0; k < bs0; ++k)
555 {
556 if (bc0[bs0 * dmapjoint0[i] + k])
557 {
558 // Zero row bs0 * i + k
559 int row = bs0 * i + k;
560 std::fill_n(std::next(Ae.begin(), num_cols * row), num_cols, 0);
561 }
562 }
563 }
564 }
565 if (!bc1.empty())
566 {
567 for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
568 {
569 for (int k = 0; k < bs1; ++k)
570 {
571 if (bc1[bs1 * dmapjoint1[j] + k])
572 {
573 // Zero column bs1 * j + k
574 for (int m = 0; m < num_rows; ++m)
575 Ae[m * num_cols + bs1 * j + k] = 0;
576 }
577 }
578 }
579 }
580 }
581
582 mat_set(dmapjoint0, dmapjoint1, Ae);
583 }
584}
585
606template <dolfinx::scalar T, std::floating_point U, bool LiftingMode = false>
607void assemble_matrix(
608 la::MatSet<T> auto mat_set, const Form<T, U>& a,
609 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
610 std::span<const T> constants,
611 const std::map<std::pair<IntegralType, int>,
612 std::pair<std::span<const T>, int>>& coefficients,
613 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1)
614{
615 // Integration domain mesh
616 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
617 assert(mesh);
618
619 // Test function mesh
620 auto mesh0 = a.function_spaces().at(0)->mesh();
621 assert(mesh0);
622
623 // Trial function mesh
624 auto mesh1 = a.function_spaces().at(1)->mesh();
625 assert(mesh1);
626
627 // TODO: Mixed topology with exterior and interior facet integrals.
628 //
629 // NOTE: Can't just loop over cell types for interior facet integrals
630 // because we have a kernel per combination of comparable cell types,
631 // rather than one per cell type. Also, we need the dofmaps for two
632 // different cell types at the same time.
633 const int num_cell_types = mesh->topology()->cell_types().size();
634 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
635 {
636 // Geometry dofmap and data
637 mdspan2_t x_dofmap = mesh->geometry().dofmaps().at(cell_type_idx);
638
639 // Get dofmap data
640 std::shared_ptr<const fem::DofMap> dofmap0
641 = a.function_spaces().at(0)->dofmaps().at(cell_type_idx);
642 std::shared_ptr<const fem::DofMap> dofmap1
643 = a.function_spaces().at(1)->dofmaps().at(cell_type_idx);
644 assert(dofmap0);
645 assert(dofmap1);
646 auto dofs0 = dofmap0->map();
647 const int bs0 = dofmap0->bs();
648 auto dofs1 = dofmap1->map();
649 const int bs1 = dofmap1->bs();
650
651 auto element0 = a.function_spaces().at(0)->elements(cell_type_idx);
652 assert(element0);
653 auto element1 = a.function_spaces().at(1)->elements(cell_type_idx);
654 assert(element1);
655 fem::DofTransformKernel<T> auto P0
656 = element0->template dof_transformation_fn<T>(doftransform::standard);
657 fem::DofTransformKernel<T> auto P1T
658 = element1->template dof_transformation_right_fn<T>(
660
661 std::span<const std::uint32_t> cell_info0;
662 std::span<const std::uint32_t> cell_info1;
663 if (element0->needs_dof_transformations()
664 or element1->needs_dof_transformations()
665 or a.needs_facet_permutations())
666 {
667 mesh0->topology_mutable()->create_entity_permutations();
668 mesh1->topology_mutable()->create_entity_permutations();
669 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
670 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
671 }
672
673 for (int i = 0; i < a.num_integrals(IntegralType::cell, cell_type_idx); ++i)
674 {
675 auto fn = a.kernel(IntegralType::cell, i, cell_type_idx);
676 assert(fn);
677 std::span cells = a.domain(IntegralType::cell, i, cell_type_idx);
678 std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
679 std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, cell_type_idx);
680 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
681 assert(cells.size() * cstride == coeffs.size());
682 impl::assemble_cells_matrix<T, U, LiftingMode>(
683 mat_set, x_dofmap, x, cells, {dofs0, bs0, cells0}, P0,
684 {dofs1, bs1, cells1}, P1T, bc0, bc1, fn,
685 md::mdspan(coeffs.data(), cells.size(), cstride), constants,
686 cell_info0, cell_info1);
687 }
688
689 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
690 if (a.needs_facet_permutations())
691 {
692 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
693 int num_facets_per_cell
694 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
695 mesh->topology_mutable()->create_entity_permutations();
696 const std::vector<std::uint8_t>& p
697 = mesh->topology()->get_facet_permutations();
698 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
699 num_facets_per_cell);
700 }
701
702 for (int i = 0;
703 i < a.num_integrals(IntegralType::interior_facet, cell_type_idx); ++i)
704 {
705 if (num_cell_types > 1)
706 {
707 throw std::runtime_error("Interior facet integrals with mixed "
708 "topology aren't supported yet");
709 }
710
711 using mdspanx22_t
712 = md::mdspan<const std::int32_t,
713 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
714 using mdspanx2x_t
715 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
716 md::dynamic_extent>>;
717
718 auto fn = a.kernel(IntegralType::interior_facet, i, 0);
719 assert(fn);
720 auto& [coeffs, cstride]
721 = coefficients.at({IntegralType::interior_facet, i});
722
723 std::span facets = a.domain(IntegralType::interior_facet, i, 0);
724 std::span facets0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
725 std::span facets1 = a.domain_arg(IntegralType::interior_facet, 1, i, 0);
726 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
727 impl::assemble_interior_facets<T, U, LiftingMode>(
728 mat_set, x_dofmap, x,
729 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
730 {*dofmap0, bs0,
731 mdspanx22_t(facets0.data(), facets0.size() / 4, 2, 2)},
732 P0,
733 {*dofmap1, bs1,
734 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
735 P1T, bc0, bc1, fn,
736 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride), constants,
737 cell_info0, cell_info1, facet_perms);
738 }
739
740 for (auto itg_type : {fem::IntegralType::exterior_facet,
742 {
743 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
745 ? facet_perms
746 : md::mdspan<const std::uint8_t,
747 md::dextents<std::size_t, 2>>{};
748
749 for (int i = 0; i < a.num_integrals(itg_type, cell_type_idx); ++i)
750 {
751 if (num_cell_types > 1)
752 {
753 throw std::runtime_error("Exterior facet integrals with mixed "
754 "topology aren't supported yet");
755 }
756
757 using mdspanx2_t
758 = md::mdspan<const std::int32_t,
759 md::extents<std::size_t, md::dynamic_extent, 2>>;
760
761 auto fn = a.kernel(itg_type, i, 0);
762 assert(fn);
763 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
764
765 std::span e = a.domain(itg_type, i, 0);
766 mdspanx2_t entities(e.data(), e.size() / 2, 2);
767 std::span e0 = a.domain_arg(itg_type, 0, i, 0);
768 mdspanx2_t entities0(e0.data(), e0.size() / 2, 2);
769 std::span e1 = a.domain_arg(itg_type, 1, i, 0);
770 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
771 assert((entities.size() / 2) * cstride == coeffs.size());
772 impl::assemble_entities<T, U, LiftingMode>(
773 mat_set, x_dofmap, x, entities, {dofs0, bs0, entities0}, P0,
774 {dofs1, bs1, entities1}, P1T, bc0, bc1, fn,
775 md::mdspan(coeffs.data(), entities.extent(0), cstride), constants,
776 cell_info0, cell_info1, perms);
777 }
778 }
779 }
780}
781
782} // 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:43
@ interior_facet
Interior facet.
Definition Form.h:42
@ ridge
Ridge.
Definition Form.h:44
@ 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)
Number of entities of dimension.
Definition cell_types.cpp:92