DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
assemble_vector_impl.h
1// Copyright (C) 2018-2026 Garth N. Wells and Paul T. Kühner
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "Constant.h"
10#include "DirichletBC.h"
11#include "DofMap.h"
12#include "Form.h"
13#include "assemble_matrix_impl.h"
14#include "traits.h"
15#include "utils.h"
16#include <algorithm>
17#include <basix/mdspan.hpp>
18#include <concepts>
19#include <cstdint>
20#include <dolfinx/common/IndexMap.h>
21#include <dolfinx/mesh/Geometry.h>
22#include <dolfinx/mesh/Mesh.h>
23#include <dolfinx/mesh/Topology.h>
24#include <functional>
25#include <memory>
26#include <optional>
27#include <span>
28#include <stdexcept>
29#include <type_traits>
30#include <vector>
31
32namespace dolfinx::fem
33{
34template <dolfinx::scalar T, std::floating_point U>
35class DirichletBC;
36}
37
38namespace dolfinx::fem::impl
39{
41using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
43
73template <typename V, std::floating_point U,
74 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
75 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
76void assemble_cells(const fem::DofTransformKernel<T> auto& P0, V&& b,
77 MDSpan2Int32 auto x_dofmap, MDSpan2Floating<U> auto x,
78 std::span<const std::int32_t> cells,
79 const DofMapPackCells auto& dofmap,
80 const FEkernel<T, U> auto& kernel,
81 std::span<const T> constants,
82 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
83 std::span<const std::uint32_t> cell_info0,
84 std::span<T> be_b, std::span<U> cdofs_b)
85{
86 if (cells.empty())
87 return;
88
89 const auto& [dmap, bs, cells0] = dofmap;
90 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
91 assert(be_b.size() >= bs * dmap.extent(1));
92 auto be = be_b.first(bs * dmap.extent(1));
93
94 const U* x_ptr = x.data_handle();
95 const std::int32_t* x_dofmap_ptr = x_dofmap.data_handle();
96 const std::int32_t* dmap_ptr = dmap.data_handle();
97
98 // P0 does not change across cells in this call, so whether it is a
99 // set (non-null) transform is loop-invariant -- checked once here
100 // rather than on every cell.
101 const bool p0_set = is_transform_set(P0);
102
103 const T* coeffs_data = coeffs.data_handle();
104 const std::size_t cstride = coeffs.extent(1);
105
106 // Iterate over active cells
107 for (std::size_t index = 0; index < cells.size(); ++index)
108 {
109 // Integration domain cell and test function cell
110 std::int32_t c = cells[index];
111 std::int32_t c0 = cells0[index];
112
113 // Get cell coordinates/geometry
114 for (std::size_t i = 0; i < x_dofmap.extent(1); ++i)
115 {
116 const U* _x_ptr
117 = x_ptr + x_dofmap_ptr[c * x_dofmap.extent(1) + i] * x.extent(1);
118 U* cdofs = cdofs_b.data() + 3 * i;
119 for (std::size_t j = 0; j < x.extent(1); ++j)
120 cdofs[j] = _x_ptr[j];
121 }
122
123 // Tabulate vector for cell
124 std::ranges::fill(be, 0);
125 kernel(be.data(), coeffs_data + index * cstride, constants.data(),
126 cdofs_b.data(), nullptr, nullptr, nullptr);
127 if (p0_set)
128 P0(be, cell_info0, c0, 1);
129
130 // Scatter cell vector to 'global' vector array
131 std::span dofs(dmap_ptr + c0 * dmap.extent(1), dmap.extent(1));
132 for (std::size_t i = 0; i < dmap.extent(1); ++i)
133 {
134 std::int32_t dof = bs * dofs[i];
135 std::int32_t offset = bs * i;
136 for (int k = 0; k < bs; ++k)
137 b[dof + k] += be[offset + k];
138 }
139 }
140}
141
181template <typename V, std::floating_point U,
182 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
183 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
184void assemble_entities(
185 const fem::DofTransformKernel<T> auto& P0, V&& b,
186 MDSpan2Int32 auto x_dofmap, MDSpan2Floating<U> auto x,
187 md::mdspan<const std::int32_t,
188 std::extents<std::size_t, md::dynamic_extent, 2>>
189 entities,
190 const DofMapPackEntities auto& dofmap, const FEkernel<T, U> auto& kernel,
191 std::span<const T> constants,
192 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
193 std::span<const std::uint32_t> cell_info0,
194 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
195 std::span<T> be_b, std::span<U> cdofs_b)
196{
197 if (entities.empty())
198 return;
199
200 const auto [dmap, bs, entities0] = dofmap;
201
202 const std::size_t num_dofs = dmap.extent(1);
203 assert(cdofs_b.size() >= 3 * x_dofmap.extent(1));
204 assert(be_b.size() >= static_cast<std::size_t>(bs) * num_dofs);
205 auto be = be_b.first(bs * num_dofs);
206 assert(entities0.size() == entities.size());
207
208 const U* x_ptr = x.data_handle();
209 const std::int32_t gdim = x.extent(1);
210 const std::int32_t* x_dofmap_ptr = x_dofmap.data_handle();
211 const std::int32_t num_x_dofs_cell = x_dofmap.extent(1);
212 const std::int32_t* dmap_ptr = dmap.data_handle();
213
214 // P0 does not change across entities in this call, so whether it is a
215 // set (non-null) transform is loop-invariant -- checked once here rather
216 // than on every entity.
217 const bool p0_set = is_transform_set(P0);
218
219 const T* coeffs_data = coeffs.data_handle();
220 const std::size_t cstride = coeffs.extent(1);
221
222 for (std::size_t f = 0; f < entities.extent(0); ++f)
223 {
224 // Cell in the integration domain, local facet index relative to the
225 // integration domain cell, and cell in the test function mesh
226 std::int32_t cell = entities(f, 0);
227 std::int32_t local_entity = entities(f, 1);
228 std::int32_t cell0 = entities0(f, 0);
229
230 // Get cell coordinates/geometry
231 for (std::int32_t i = 0; i < num_x_dofs_cell; ++i)
232 {
233 const U* _x_ptr = x_ptr + x_dofmap_ptr[cell * num_x_dofs_cell + i] * gdim;
234 std::copy_n(_x_ptr, gdim, cdofs_b.data() + 3 * i);
235 }
236
237 // Permutations
238 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
239
240 // Tabulate element vector
241 std::ranges::fill(be, 0);
242 kernel(be.data(), coeffs_data + f * cstride, constants.data(),
243 cdofs_b.data(), &local_entity, &perm, nullptr);
244 if (p0_set)
245 P0(be, cell_info0, cell0, 1);
246
247 // Add to global vector
248 std::span dofs(dmap_ptr + cell0 * num_dofs, num_dofs);
249 for (std::size_t i = 0; i < dofs.size(); ++i)
250 for (int k = 0; k < bs; ++k)
251 b[bs * dofs[i] + k] += be[bs * i + k];
252 }
253}
254
287template <typename V, std::floating_point U,
288 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
289 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
290void assemble_interior_facets(
291 const fem::DofTransformKernel<T> auto& P0, V&& b,
292 MDSpan2Int32 auto x_dofmap, MDSpan2Floating<U> auto x,
293 md::mdspan<const std::int32_t,
294 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
295 facets,
296 const DofMapPackFacets auto& dofmap, const FEkernel<T, U> auto& kernel,
297 std::span<const T> constants,
298 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
299 md::dynamic_extent>>
300 coeffs,
301 std::span<const std::uint32_t> cell_info0,
302 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
303 std::span<T> be_b, std::span<U> cdofs_b)
304{
305 if (facets.empty())
306 return;
307
308 const auto [dmap, bs, facets0] = dofmap;
309
310 assert(cdofs_b.size() >= 2 * x_dofmap.extent(1) * 3);
311 auto cdofs0 = cdofs_b.first(x_dofmap.extent(1) * 3);
312 auto cdofs1 = cdofs_b.subspan(x_dofmap.extent(1) * 3, x_dofmap.extent(1) * 3);
313
314 const std::size_t dmap_size = dmap.extent(1);
315 assert(be_b.size() >= static_cast<std::size_t>(bs) * 2 * dmap_size);
316 auto be = be_b.first(bs * 2 * dmap_size);
317
318 const T* coeffs_data = coeffs.data_handle();
319 const std::size_t cstride = 2 * coeffs.extent(2);
320
321 assert(facets0.size() == facets.size());
322
323 const U* x_ptr = x.data_handle();
324 const std::int32_t gdim = x.extent(1);
325 const std::int32_t* x_dofmap_ptr = x_dofmap.data_handle();
326 const std::int32_t num_x_dofs_cell = x_dofmap.extent(1);
327
328 // P0 does not change across facets in this call, so whether it is a
329 // set (non-null) transform is loop-invariant -- checked once here rather
330 // than on every facet.
331 const bool p0_set = is_transform_set(P0);
332
333 for (std::size_t f = 0; f < facets.extent(0); ++f)
334 {
335 // Cells in integration domain and test function domain meshes
336 std::array<std::int32_t, 2> cells{facets(f, 0, 0), facets(f, 1, 0)};
337 std::array<std::int32_t, 2> cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
338
339 // Local facet indices
340 std::array<std::int32_t, 2> local_facet{facets(f, 0, 1), facets(f, 1, 1)};
341
342 // Get cell geometry
343 for (std::int32_t i = 0; i < num_x_dofs_cell; ++i)
344 {
345 const U* _x_ptr0
346 = x_ptr + x_dofmap_ptr[cells[0] * num_x_dofs_cell + i] * gdim;
347 std::copy_n(_x_ptr0, gdim, cdofs0.data() + 3 * i);
348 const U* _x_ptr1
349 = x_ptr + x_dofmap_ptr[cells[1] * num_x_dofs_cell + i] * gdim;
350 std::copy_n(_x_ptr1, gdim, cdofs1.data() + 3 * i);
351 }
352
353 // Get dofmaps for cells. When integrating over interfaces between
354 // two domains, the test function might only be defined on one side,
355 // so we check which cells exist in the test function domain.
356 std::span dmap0 = cells0[0] >= 0 ? std::span(&dmap(cells0[0], 0), dmap_size)
357 : std::span<const std::int32_t>();
358 std::span dmap1 = cells0[1] >= 0 ? std::span(&dmap(cells0[1], 0), dmap_size)
359 : std::span<const std::int32_t>();
360
361 // Tabulate element vector
362 std::ranges::fill(be, 0);
363 std::array perm = perms.empty()
364 ? std::array<std::uint8_t, 2>{0, 0}
365 : std::array{perms(cells[0], local_facet[0]),
366 perms(cells[1], local_facet[1])};
367 kernel(be.data(), coeffs_data + f * cstride, constants.data(),
368 cdofs_b.data(), local_facet.data(), perm.data(), nullptr);
369
370 if (p0_set and cells0[0] >= 0)
371 P0(be, cell_info0, cells0[0], 1);
372 if (p0_set and cells0[1] >= 0)
373 {
374 std::span sub_be(be.data() + bs * dmap_size, bs * dmap_size);
375 P0(sub_be, cell_info0, cells0[1], 1);
376 }
377
378 // Add element vector to global vector
379 for (std::size_t i = 0; i < dmap0.size(); ++i)
380 {
381 std::int32_t dof = bs * dmap0[i];
382 std::int32_t offset = bs * i;
383 for (int k = 0; k < bs; ++k)
384 b[dof + k] += be[offset + k];
385 }
386 for (std::size_t i = 0; i < dmap1.size(); ++i)
387 {
388 std::int32_t dof = bs * dmap1[i];
389 std::int32_t offset = bs * (i + dmap_size);
390 for (int k = 0; k < bs; ++k)
391 b[dof + k] += be[offset + k];
392 }
393 }
394}
395
416template <dolfinx::scalar T, std::floating_point U, typename V>
417 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
418void lift_bc(V&& b, const Form<T, U>& a, auto bs0, auto bs1,
419 std::span<const T> constants,
420 const std::map<std::pair<IntegralType, int>,
421 std::pair<std::span<const T>, int>>& coefficients,
422 std::span<const T> bc_values1,
423 std::span<const std::int8_t> bc_markers1, std::span<const T> x0,
424 T alpha)
425{
426 // Deduce runtime block sizes as fallback when compile-time sizes
427 // not given. The block size of the dofmap and indexmap is the same
428 // on all sub-topologies.
429 assert(bs0 == a.function_spaces()[0]->dofmaps().front()->bs());
430 assert(bs1 == a.function_spaces()[1]->dofmaps().front()->bs());
431
432 auto lifting_fn = [bs0, bs1, alpha, &b, &bc_values1, &bc_markers1,
433 &x0](auto rows, auto cols, auto Ae)
434 {
435 const std::size_t nc = cols.size() * bs1;
436 for (std::size_t i = 0; i < cols.size(); ++i)
437 {
438 for (int k = 0; k < bs1; ++k)
439 {
440 const std::int32_t ii = cols[i] * bs1 + k;
441 if (bc_markers1[ii])
442 {
443 const T x_bc = bc_values1[ii];
444 const T _x0 = x0.empty() ? 0 : x0[ii];
445 for (std::size_t j = 0; j < rows.size(); ++j)
446 {
447 for (int m = 0; m < bs0; ++m)
448 {
449 const std::int32_t jj = rows[j] * bs0 + m;
450 b[jj] -= Ae[(j * bs0 + m) * nc + (i * bs1 + k)] * alpha
451 * (x_bc - _x0);
452 }
453 }
454 }
455 }
456 }
457 };
458
459 // Use dolfinx::fem::impl::assemble_matrix assembler to work on the
460 // vector b. With LiftingMode=true, the kernel is only called on cells
461 // that have BC-constrained DOFs in the column space.
462 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
463 assert(mesh);
464 std::span x = mesh->geometry().x();
465 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> _x(
466 x.data(), x.size() / 3, 3);
467 impl::assemble_matrix<true>(lifting_fn, a, _x, constants, coefficients, {},
468 bc_markers1);
469}
470
479template <typename V, std::floating_point U,
480 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
481 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
482void assemble_vector(
483 V&& b, const Form<T, U>& L,
484 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>> x,
485 std::span<const T> constants,
486 const std::map<std::pair<IntegralType, int>,
487 std::pair<std::span<const T>, int>>& coefficients)
488{
489 // Integration domain mesh
490 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
491 assert(mesh);
492
493 // Test function mesh
494 auto mesh0 = L.function_spaces().at(0)->mesh();
495 assert(mesh0);
496
497 const int num_cell_types = mesh->topology()->cell_types().size();
498 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
499 {
500 // Geometry dofmap and data
501 mdspan2_t x_dofmap = mesh->geometry().dofmaps().at(cell_type_idx);
502
503 // Get dofmap data
504 assert(L.function_spaces().at(0));
505 auto element = L.function_spaces().at(0)->elements(cell_type_idx);
506 assert(element);
507 std::shared_ptr<const fem::DofMap> dofmap
508 = L.function_spaces().at(0)->dofmaps().at(cell_type_idx);
509 assert(dofmap);
510 auto dofs = dofmap->map();
511 const int bs = dofmap->bs();
512
513 // Buffers reused across all integral kernels for this cell type,
514 // sized for the worst case (interior facets, which touch two cells).
515 std::vector<T> be_buffer(2 * bs * dofs.extent(1));
516 std::vector<U> cdofs_buffer(2 * 3 * x_dofmap.extent(1));
517 std::span be_b(be_buffer);
518 std::span cdofs_b(cdofs_buffer);
519
520 const fem::DofTransformKernel<T> auto& P0
521 = element->template dof_transformation_fn<T>(doftransform::standard);
522
523 std::span<const std::uint32_t> cell_info0;
524 if (element->needs_dof_transformations() or L.needs_facet_permutations())
525 {
526 mesh0->topology_mutable()->create_entity_permutations();
527 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
528 }
529
530 for (int i = 0; i < L.num_integrals(IntegralType::cell, 0); ++i)
531 {
532 auto fn = L.kernel(IntegralType::cell, i, cell_type_idx);
533 assert(fn);
534 std::span cells = L.domain(IntegralType::cell, i, cell_type_idx);
535 std::span cells0 = L.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
536 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
537 assert(cells.size() * cstride == coeffs.size());
538 if (bs == 1)
539 {
540 impl::assemble_cells(
541 P0, b, x_dofmap, x, cells,
542 std::tuple{dofs, std::integral_constant<int, 1>{}, cells0}, fn,
543 constants, md::mdspan(coeffs.data(), cells.size(), cstride),
544 cell_info0, be_b, cdofs_b);
545 }
546 else if (bs == 3)
547 {
548 impl::assemble_cells(
549 P0, b, x_dofmap, x, cells,
550 std::tuple{dofs, std::integral_constant<int, 3>(), cells0}, fn,
551 constants, md::mdspan(coeffs.data(), cells.size(), cstride),
552 cell_info0, be_b, cdofs_b);
553 }
554 else
555 {
556 impl::assemble_cells(P0, b, x_dofmap, x, cells,
557 std::tuple{dofs, bs, cells0}, fn, constants,
558 md::mdspan(coeffs.data(), cells.size(), cstride),
559 cell_info0, be_b, cdofs_b);
560 }
561 }
562
563 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
564 if (L.needs_facet_permutations())
565 {
566 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
567 int num_facets_per_cell
568 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
569 mesh->topology_mutable()->create_entity_permutations();
570 const std::vector<std::uint8_t>& p
571 = mesh->topology()->get_facet_permutations();
572 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
573 num_facets_per_cell);
574 }
575
576 using mdspanx2_t
577 = md::mdspan<const std::int32_t,
578 md::extents<std::size_t, md::dynamic_extent, 2>>;
579 using mdspanx22_t
580 = md::mdspan<const std::int32_t,
581 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
582 using mdspanx2x_t
583 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
584 md::dynamic_extent>>;
585
586 for (int i = 0; i < L.num_integrals(IntegralType::interior_facet, 0); ++i)
587 {
588 auto fn = L.kernel(IntegralType::interior_facet, i, 0);
589 assert(fn);
590 auto& [coeffs, cstride]
591 = coefficients.at({IntegralType::interior_facet, i});
592 std::span facets = L.domain(IntegralType::interior_facet, i, 0);
593 std::span facets1 = L.domain_arg(IntegralType::interior_facet, 0, i, 0);
594 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
595
596 mdspanx22_t facets_mdspan(facets.data(), facets.size() / 4, 2, 2);
597 mdspanx22_t facets1_mdspan(facets1.data(), facets1.size() / 4, 2, 2);
598 if (bs == 1)
599 {
600 impl::assemble_interior_facets(
601 P0, b, x_dofmap, x, facets_mdspan,
602 std::tuple{dofs, std::integral_constant<int, 1>{}, facets1_mdspan},
603 fn, constants,
604 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
605 cell_info0, facet_perms, be_b, cdofs_b);
606 }
607 else if (bs == 3)
608 {
609 impl::assemble_interior_facets(
610 P0, b, x_dofmap, x, facets_mdspan,
611 std::tuple{dofs, std::integral_constant<int, 3>{}, facets1_mdspan},
612 fn, constants,
613 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
614 cell_info0, facet_perms, be_b, cdofs_b);
615 }
616 else
617 {
618 impl::assemble_interior_facets(
619 P0, b, x_dofmap, x, facets_mdspan,
620 std::tuple{dofs, bs, facets1_mdspan}, fn, constants,
621 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride),
622 cell_info0, facet_perms, be_b, cdofs_b);
623 }
624 }
625
626 for (auto itg_type : {fem::IntegralType::exterior_facet,
628 {
629 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
631 ? facet_perms
632 : md::mdspan<const std::uint8_t,
633 md::dextents<std::size_t, 2>>{};
634 for (int i = 0; i < L.num_integrals(itg_type, 0); ++i)
635 {
636 auto fn = L.kernel(itg_type, i, 0);
637 assert(fn);
638 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
639 std::span e = L.domain(itg_type, i, 0);
640 mdspanx2_t entities(e.data(), e.size() / 2, 2);
641 std::span e1 = L.domain_arg(itg_type, 0, i, 0);
642 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
643 assert((entities.size() / 2) * cstride == coeffs.size());
644 if (bs == 1)
645 {
646 impl::assemble_entities(
647 P0, b, x_dofmap, x, entities,
648 std::tuple{dofs, std::integral_constant<int, 1>{}, entities1}, fn,
649 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
650 cell_info0, perms, be_b, cdofs_b);
651 }
652 else if (bs == 3)
653 {
654 impl::assemble_entities(
655 P0, b, x_dofmap, x, entities,
656 std::tuple{dofs, std::integral_constant<int, 3>{}, entities1}, fn,
657 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
658 cell_info0, perms, be_b, cdofs_b);
659 }
660 else
661 {
662 impl::assemble_entities(
663 P0, b, x_dofmap, x, entities, std::tuple{dofs, bs, entities1}, fn,
664 constants, md::mdspan(coeffs.data(), entities.extent(0), cstride),
665 cell_info0, perms, be_b, cdofs_b);
666 }
667 }
668 }
669 }
670}
671
678template <typename V, std::floating_point U,
679 dolfinx::scalar T = typename std::remove_cvref_t<V>::value_type>
680 requires std::is_same_v<typename std::remove_cvref_t<V>::value_type, T>
681void assemble_vector(
682 V&& b, const Form<T, U>& L, std::span<const T> constants,
683 const std::map<std::pair<IntegralType, int>,
684 std::pair<std::span<const T>, int>>& coefficients)
685{
686 using mdspanx3_t
687 = md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent, 3>>;
688
689 std::shared_ptr<const mesh::Mesh<U>> mesh = L.mesh();
690 assert(mesh);
691 auto x = mesh->geometry().x();
692 impl::assemble_vector(b, L, mdspanx3_t(x.data(), x.size() / 3, 3), constants,
693 coefficients);
694}
695} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Definition DirichletBC.h:259
Functions supporting finite element method operations.
void cells(la::SparsityPattern &pattern, const std::pair< R0, R1 > &cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.h:37
Finite element method functionality.
Definition assemble_expression_impl.h:24
@ standard
Standard.
Definition FiniteElement.h:29
@ 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
constexpr bool is_transform_set(const F &fn)
Whether a DofTransformKernel fn should be invoked.
Definition traits.h:33
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