DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1// Copyright (C) 2013-2020 Johan Hake, Jan Blechta and 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 "CoordinateElement.h"
11#include "DofMap.h"
12#include "ElementDofLayout.h"
13#include "Expression.h"
14#include "Form.h"
15#include "Function.h"
16#include "FunctionSpace.h"
17#include "sparsitybuild.h"
18#include <algorithm>
19#include <array>
20#include <concepts>
21#include <dolfinx/common/types.h>
22#include <dolfinx/la/SparsityPattern.h>
23#include <dolfinx/mesh/Topology.h>
24#include <dolfinx/mesh/cell_types.h>
25#include <dolfinx/mesh/utils.h>
26#include <format>
27#include <functional>
28#include <memory>
29#include <optional>
30#include <set>
31#include <span>
32#include <stdexcept>
33#include <string>
34#include <tuple>
35#include <type_traits>
36#include <ufcx.h>
37#include <utility>
38#include <vector>
39
42namespace basix
43{
44template <std::floating_point T>
45class FiniteElement;
46}
47
48namespace dolfinx::common
49{
50class IndexMap;
51}
52
53namespace dolfinx::fem
54{
55template <dolfinx::scalar T, std::floating_point U>
56class Expression;
57
58namespace impl
59{
66template <int num_cells>
67std::array<std::int32_t, 2 * num_cells>
68get_cell_facet_pairs(std::int32_t f, std::span<const std::int32_t> cells,
70{
71 // Loop over cells sharing facet
72 assert(cells.size() == num_cells);
73 std::array<std::int32_t, 2 * num_cells> cell_local_facet_pairs;
74 for (int c = 0; c < num_cells; ++c)
75 {
76 // Get local index of facet with respect to the cell
77 std::int32_t cell = cells[c];
78 auto cell_facets = c_to_f.links(cell);
79 auto facet_it = std::find(cell_facets.begin(), cell_facets.end(), f);
80 assert(facet_it != cell_facets.end());
81 int local_f = std::distance(cell_facets.begin(), facet_it);
82 cell_local_facet_pairs[2 * c] = cell;
83 cell_local_facet_pairs[2 * c + 1] = local_f;
84 }
85
86 return cell_local_facet_pairs;
87}
88} // namespace impl
89
116std::vector<std::int32_t>
118 const mesh::Topology& topology,
119 std::span<const std::int32_t> entities);
120
128template <dolfinx::scalar T, std::floating_point U>
129std::vector<std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
130extract_function_spaces(const std::vector<std::vector<const Form<T, U>*>>& a)
131{
132 std::vector<
133 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
134 spaces(
135 a.size(),
136 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>(
137 a.front().size()));
138 for (std::size_t i = 0; i < a.size(); ++i)
139 {
140 for (std::size_t j = 0; j < a[i].size(); ++j)
141 {
142 if (const Form<T, U>* form = a[i][j]; form)
143 spaces[i][j] = {form->function_spaces()[0], form->function_spaces()[1]};
144 }
145 }
146 return spaces;
147}
148
154template <dolfinx::scalar T, std::floating_point U>
156{
157 std::shared_ptr mesh = a.mesh();
158 assert(mesh);
159
160 // Get index maps and block sizes from the DOF maps. Note that in
161 // mixed-topology meshes, despite there being multiple DOF maps, the
162 // index maps and block sizes are the same.
163 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
164 *a.function_spaces().at(0)->dofmaps(0),
165 *a.function_spaces().at(1)->dofmaps(0)};
166
167 const std::array index_maps{dofmaps[0].get().index_map,
168 dofmaps[1].get().index_map};
169 const std::array bs
170 = {dofmaps[0].get().index_map_bs(), dofmaps[1].get().index_map_bs()};
171
172 la::SparsityPattern pattern(mesh->comm(), index_maps, bs);
173 build_sparsity_pattern(pattern, a);
174 return pattern;
175}
176
182template <dolfinx::scalar T, std::floating_point U>
184{
185 if (a.rank() != 2)
186 {
187 throw std::runtime_error(
188 "Cannot create sparsity pattern. Form is not a bilinear.");
189 }
190
191 std::shared_ptr mesh = a.mesh();
192 assert(mesh);
193 std::shared_ptr mesh0 = a.function_spaces().at(0)->mesh();
194 assert(mesh0);
195 std::shared_ptr mesh1 = a.function_spaces().at(1)->mesh();
196 assert(mesh1);
197
198 const std::set<IntegralType> types = a.integral_types();
199 if (types.find(IntegralType::interior_facet) != types.end()
200 or types.find(IntegralType::exterior_facet) != types.end())
201 {
202 // FIXME: cleanup these calls? Some of the happen internally again.
203 int tdim = mesh->topology()->dim();
204 mesh->topology_mutable()->create_entities(tdim - 1);
205 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
206 }
207
208 common::Timer t0("Build sparsity");
209
210 auto extract_cells = [](std::span<const std::int32_t> facets)
211 {
212 assert(facets.size() % 2 == 0);
213 std::vector<std::int32_t> cells;
214 cells.reserve(facets.size() / 2);
215 for (std::size_t i = 0; i < facets.size(); i += 2)
216 cells.push_back(facets[i]);
217 return cells;
218 };
219
220 const int num_cell_types = mesh->topology()->cell_types().size();
221 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
222 {
223 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
224 *a.function_spaces().at(0)->dofmaps(cell_type_idx),
225 *a.function_spaces().at(1)->dofmaps(cell_type_idx)};
226
227 // Create and build sparsity pattern
228 for (auto type : types)
229 {
230 std::vector<int> ids = a.integral_ids(type);
231 switch (type)
232 {
234 for (int id : ids)
235 {
236 sparsitybuild::cells(pattern,
237 {a.domain_arg(type, 0, id, cell_type_idx),
238 a.domain_arg(type, 1, id, cell_type_idx)},
239 {{dofmaps[0], dofmaps[1]}});
240 }
241 break;
243 for (int id : ids)
244 {
246 pattern,
247 {extract_cells(a.domain_arg(type, 0, id, 0)),
248 extract_cells(a.domain_arg(type, 1, id, 0))},
249 {{dofmaps[0], dofmaps[1]}});
250 }
251 break;
253 for (int id : ids)
254 {
255 sparsitybuild::cells(pattern,
256 {extract_cells(a.domain_arg(type, 0, id, 0)),
257 extract_cells(a.domain_arg(type, 1, id, 0))},
258 {{dofmaps[0], dofmaps[1]}});
259 }
260 break;
261 default:
262 throw std::runtime_error("Unsupported integral type");
263 }
264 }
265 }
266
267 t0.stop();
268}
269
271template <std::floating_point T>
273 const std::vector<int>& parent_map
274 = {})
275{
276 // Create subdofmaps and compute offset
277 std::vector<int> offsets(1, 0);
278 std::vector<dolfinx::fem::ElementDofLayout> sub_doflayout;
279 int bs = element.block_size();
280 for (int i = 0; i < element.num_sub_elements(); ++i)
281 {
282 // The ith sub-element. For mixed elements this is subelements()[i]. For
283 // blocked elements, the sub-element will always be the same, so we'll use
284 // sub_elements()[0]
285 std::shared_ptr<const fem::FiniteElement<T>> sub_e
286 = element.sub_elements()[bs > 1 ? 0 : i];
287
288 // In a mixed element DOFs are ordered element by element, so the offset to
289 // the next sub-element is sub_e->space_dimension(). Blocked elements use
290 // xxyyzz ordering, so the offset to the next sub-element is 1
291
292 std::vector<int> parent_map_sub(sub_e->space_dimension(), offsets.back());
293 for (std::size_t j = 0; j < parent_map_sub.size(); ++j)
294 parent_map_sub[j] += bs * j;
295 offsets.push_back(offsets.back() + (bs > 1 ? 1 : sub_e->space_dimension()));
296 sub_doflayout.push_back(
297 dolfinx::fem::create_element_dof_layout(*sub_e, parent_map_sub));
298 }
299
300 return ElementDofLayout(bs, element.entity_dofs(),
301 element.entity_closure_dofs(), parent_map,
302 sub_doflayout);
303}
304
314 MPI_Comm comm, const ElementDofLayout& layout, mesh::Topology& topology,
315 std::function<void(std::span<std::int32_t>, std::uint32_t)> permute_inv,
316 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
317 reorder_fn);
318
329std::vector<DofMap> create_dofmaps(
330 MPI_Comm comm, const std::vector<ElementDofLayout>& layouts,
331 mesh::Topology& topology,
332 std::function<void(std::span<std::int32_t>, std::uint32_t)> permute_inv,
333 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
334 reorder_fn);
335
339std::vector<std::string> get_coefficient_names(const ufcx_form& ufcx_form);
340
344std::vector<std::string> get_constant_names(const ufcx_form& ufcx_form);
345
364template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
366 const std::vector<std::reference_wrapper<const ufcx_form>>& ufcx_forms,
367 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
368 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
369 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
370 const std::map<
372 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
373 subdomains,
374 const std::map<std::shared_ptr<const mesh::Mesh<U>>,
375 std::span<const std::int32_t>>& entity_maps,
376 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
377{
378 for (const ufcx_form& ufcx_form : ufcx_forms)
379 {
380 if (ufcx_form.rank != (int)spaces.size())
381 throw std::runtime_error("Wrong number of argument spaces for Form.");
382 if (ufcx_form.num_coefficients != (int)coefficients.size())
383 {
384 throw std::runtime_error("Mismatch between number of expected and "
385 "provided Form coefficients.");
386 }
387
388 // Check Constants for rank and size consistency
389 if (ufcx_form.num_constants != (int)constants.size())
390 {
391 throw std::runtime_error(std::format(
392 "Mismatch between number of expected and "
393 "provided Form Constants. Expected {} constants, but got {}.",
394 ufcx_form.num_constants, constants.size()));
395 }
396 for (std::size_t c = 0; c < constants.size(); ++c)
397 {
398 if (ufcx_form.constant_ranks[c] != (int)constants[c]->shape.size())
399 {
400 throw std::runtime_error(std::format(
401 "Mismatch between expected and actual rank of "
402 "Form Constant. Rank of Constant {} should be {}, but got rank {}.",
403 c, ufcx_form.constant_ranks[c], constants[c]->shape.size()));
404 }
405 if (!std::equal(constants[c]->shape.begin(), constants[c]->shape.end(),
406 ufcx_form.constant_shapes[c]))
407 {
408 throw std::runtime_error(
409 std::format("Mismatch between expected and actual shape of Form "
410 "Constant for Constant {}.",
411 c));
412 }
413 }
414 }
415
416 // Check argument function spaces
417 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
418 {
419 for (std::size_t i = 0; i < spaces.size(); ++i)
420 {
421 assert(spaces[i]->elements(form_idx));
422 if (auto element_hash
423 = ufcx_forms[form_idx].get().finite_element_hashes[i];
424 element_hash != 0
425 and element_hash
426 != spaces[i]->elements(form_idx)->basix_element().hash())
427 {
428 throw std::runtime_error(
429 "Cannot create form. Elements are different to "
430 "those used to compile the form.");
431 }
432 }
433 }
434
435 // Extract mesh from FunctionSpace, and check they are the same
436 if (!mesh and !spaces.empty())
437 mesh = spaces.front()->mesh();
438 if (!mesh)
439 throw std::runtime_error("No mesh could be associated with the Form.");
440 for (auto& V : spaces)
441 {
442 if (mesh != V->mesh() and entity_maps.find(V->mesh()) == entity_maps.end())
443 {
444 throw std::runtime_error(
445 "Incompatible mesh. entity_maps must be provided.");
446 }
447 }
448
449 auto topology = mesh->topology();
450 assert(topology);
451 const int tdim = topology->dim();
452
453 // NOTE: This assumes all forms in mixed-topology meshes have the same
454 // integral offsets. Since the UFL forms for each type of cell should be
455 // the same, I think this assumption is OK.
456 const int* integral_offsets = ufcx_forms[0].get().form_integral_offsets;
457 std::vector<int> num_integrals_type(3);
458 for (int i = 0; i < 3; ++i)
459 num_integrals_type[i] = integral_offsets[i + 1] - integral_offsets[i];
460
461 // Create facets, if required
462 if (num_integrals_type[exterior_facet] > 0
463 or num_integrals_type[interior_facet] > 0)
464 {
465 mesh->topology_mutable()->create_entities(tdim - 1);
466 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
467 mesh->topology_mutable()->create_connectivity(tdim, tdim - 1);
468 }
469
470 // Get list of integral IDs, and load tabulate tensor into memory for
471 // each
472 using kern_t = std::function<void(T*, const T*, const T*, const U*,
473 const int*, const std::uint8_t*, void*)>;
474 std::map<std::tuple<IntegralType, int, int>, integral_data<T, U>> integrals;
475
476 auto check_geometry_hash
477 = [&geo = mesh->geometry()](const ufcx_integral& integral,
478 std::size_t cell_idx)
479 {
480 if (integral.coordinate_element_hash != geo.cmaps().at(cell_idx).hash())
481 {
482 throw std::runtime_error(
483 "Generated integral geometry element does not match mesh geometry: "
484 + std::to_string(integral.coordinate_element_hash) + ", "
485 + std::to_string(geo.cmaps().at(cell_idx).hash()));
486 }
487 };
488
489 // Attach cell kernels
490 bool needs_facet_permutations = false;
491 {
492 std::vector<std::int32_t> default_cells;
493 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
494 + integral_offsets[cell],
495 num_integrals_type[cell]);
496 auto sd = subdomains.find(IntegralType::cell);
497 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
498 {
499 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
500 for (int i = 0; i < num_integrals_type[cell]; ++i)
501 {
502 const int id = ids[i];
503 ufcx_integral* integral
504 = ufcx_form.form_integrals[integral_offsets[cell] + i];
505 assert(integral);
506 check_geometry_hash(*integral, form_idx);
507
508 // Build list of active coefficients
509 std::vector<int> active_coeffs;
510 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
511 {
512 if (integral->enabled_coefficients[j])
513 active_coeffs.push_back(j);
514 }
515
516 kern_t k = nullptr;
517 if constexpr (std::is_same_v<T, float>)
518 k = integral->tabulate_tensor_float32;
519#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
520 else if constexpr (std::is_same_v<T, std::complex<float>>)
521 {
522 k = reinterpret_cast<void (*)(T*, const T*, const T*,
523 const scalar_value_t<T>*, const int*,
524 const unsigned char*, void*)>(
525 integral->tabulate_tensor_complex64);
526 }
527#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
528 else if constexpr (std::is_same_v<T, double>)
529 k = integral->tabulate_tensor_float64;
530#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
531 else if constexpr (std::is_same_v<T, std::complex<double>>)
532 {
533 k = reinterpret_cast<void (*)(T*, const T*, const T*,
534 const scalar_value_t<T>*, const int*,
535 const unsigned char*, void*)>(
536 integral->tabulate_tensor_complex128);
537 }
538#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
539
540 if (!k)
541 {
542 throw std::runtime_error(
543 "UFCx kernel function is NULL. Check requested types.");
544 }
545
546 // Build list of entities to assemble over
547 if (id == -1)
548 {
549 // Default kernel, operates on all (owned) cells
550 assert(topology->index_maps(tdim).at(form_idx));
551 default_cells.resize(
552 topology->index_maps(tdim).at(form_idx)->size_local(), 0);
553 std::iota(default_cells.begin(), default_cells.end(), 0);
554 integrals.insert({{IntegralType::cell, id, form_idx},
555 {k, default_cells, active_coeffs}});
556 }
557 else if (sd != subdomains.end())
558 {
559 // NOTE: This requires that pairs are sorted
560 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
561 [](auto& a) { return a.first; });
562 if (it != sd->second.end() and it->first == id)
563 {
564 integrals.insert({{IntegralType::cell, id, form_idx},
565 {k,
566 std::vector<std::int32_t>(it->second.begin(),
567 it->second.end()),
568 active_coeffs}});
569 }
570 }
571
572 if (integral->needs_facet_permutations)
573 needs_facet_permutations = true;
574 }
575 }
576 }
577
578 // Attach exterior facet kernels
579 std::vector<std::int32_t> default_facets_ext;
580 {
581 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
582 + integral_offsets[exterior_facet],
583 num_integrals_type[exterior_facet]);
584 auto sd = subdomains.find(IntegralType::exterior_facet);
585 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
586 {
587 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
588 for (int i = 0; i < num_integrals_type[exterior_facet]; ++i)
589 {
590 const int id = ids[i];
591 ufcx_integral* integral
592 = ufcx_form.form_integrals[integral_offsets[exterior_facet] + i];
593 assert(integral);
594 check_geometry_hash(*integral, form_idx);
595
596 std::vector<int> active_coeffs;
597 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
598 {
599 if (integral->enabled_coefficients[j])
600 active_coeffs.push_back(j);
601 }
602
603 kern_t k = nullptr;
604 if constexpr (std::is_same_v<T, float>)
605 k = integral->tabulate_tensor_float32;
606#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
607 else if constexpr (std::is_same_v<T, std::complex<float>>)
608 {
609 k = reinterpret_cast<void (*)(T*, const T*, const T*,
610 const scalar_value_t<T>*, const int*,
611 const unsigned char*, void*)>(
612 integral->tabulate_tensor_complex64);
613 }
614#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
615 else if constexpr (std::is_same_v<T, double>)
616 k = integral->tabulate_tensor_float64;
617#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
618 else if constexpr (std::is_same_v<T, std::complex<double>>)
619 {
620 k = reinterpret_cast<void (*)(T*, const T*, const T*,
621 const scalar_value_t<T>*, const int*,
622 const unsigned char*, void*)>(
623 integral->tabulate_tensor_complex128);
624 }
625#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
626 assert(k);
627
628 // Build list of entities to assembler over
629 const std::vector bfacets = mesh::exterior_facet_indices(*topology);
630 auto f_to_c = topology->connectivity(tdim - 1, tdim);
631 assert(f_to_c);
632 auto c_to_f = topology->connectivity(tdim, tdim - 1);
633 assert(c_to_f);
634 if (id == -1)
635 {
636 // Default kernel, operates on all (owned) exterior facets
637 default_facets_ext.reserve(2 * bfacets.size());
638 for (std::int32_t f : bfacets)
639 {
640 // There will only be one pair for an exterior facet integral
641 std::array<std::int32_t, 2> pair
642 = impl::get_cell_facet_pairs<1>(f, f_to_c->links(f), *c_to_f);
643 default_facets_ext.insert(default_facets_ext.end(), pair.begin(),
644 pair.end());
645 }
646 integrals.insert({{IntegralType::exterior_facet, id, form_idx},
647 {k, default_facets_ext, active_coeffs}});
648 }
649 else if (sd != subdomains.end())
650 {
651 // NOTE: This requires that pairs are sorted
652 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
653 [](auto& a) { return a.first; });
654 if (it != sd->second.end() and it->first == id)
655 {
656 integrals.insert({{IntegralType::exterior_facet, id, form_idx},
657 {k,
658 std::vector<std::int32_t>(it->second.begin(),
659 it->second.end()),
660 active_coeffs}});
661 }
662 }
663
664 if (integral->needs_facet_permutations)
665 needs_facet_permutations = true;
666 }
667 }
668 }
669
670 // Attach interior facet kernels
671 std::vector<std::int32_t> default_facets_int;
672 {
673 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
674 + integral_offsets[interior_facet],
675 num_integrals_type[interior_facet]);
676 auto sd = subdomains.find(IntegralType::interior_facet);
677 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
678 {
679 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
680
681 // Create indicator for interprocess facets
682 std::vector<std::int8_t> interprocess_marker;
683 if (num_integrals_type[interior_facet] > 0)
684 {
685 assert(topology->index_map(tdim - 1));
686 const std::vector<std::int32_t>& interprocess_facets
687 = topology->interprocess_facets();
688 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local()
689 + topology->index_map(tdim - 1)->num_ghosts();
690 interprocess_marker.resize(num_facets, 0);
691 std::ranges::for_each(interprocess_facets,
692 [&interprocess_marker](auto f)
693 { interprocess_marker[f] = 1; });
694 }
695
696 for (int i = 0; i < num_integrals_type[interior_facet]; ++i)
697 {
698 const int id = ids[i];
699 ufcx_integral* integral
700 = ufcx_form.form_integrals[integral_offsets[interior_facet] + i];
701 assert(integral);
702 check_geometry_hash(*integral, form_idx);
703
704 std::vector<int> active_coeffs;
705 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
706 {
707 if (integral->enabled_coefficients[j])
708 active_coeffs.push_back(j);
709 }
710
711 kern_t k = nullptr;
712 if constexpr (std::is_same_v<T, float>)
713 k = integral->tabulate_tensor_float32;
714#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
715 else if constexpr (std::is_same_v<T, std::complex<float>>)
716 {
717 k = reinterpret_cast<void (*)(T*, const T*, const T*,
718 const scalar_value_t<T>*, const int*,
719 const unsigned char*, void*)>(
720 integral->tabulate_tensor_complex64);
721 }
722#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
723 else if constexpr (std::is_same_v<T, double>)
724 k = integral->tabulate_tensor_float64;
725#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
726 else if constexpr (std::is_same_v<T, std::complex<double>>)
727 {
728 k = reinterpret_cast<void (*)(T*, const T*, const T*,
729 const scalar_value_t<T>*, const int*,
730 const unsigned char*, void*)>(
731 integral->tabulate_tensor_complex128);
732 }
733#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
734 assert(k);
735
736 // Build list of entities to assembler over
737 auto f_to_c = topology->connectivity(tdim - 1, tdim);
738 assert(f_to_c);
739 auto c_to_f = topology->connectivity(tdim, tdim - 1);
740 assert(c_to_f);
741 if (id == -1)
742 {
743 // Default kernel, operates on all (owned) interior facets
744 assert(topology->index_map(tdim - 1));
745 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local();
746 default_facets_int.reserve(4 * num_facets);
747 for (std::int32_t f = 0; f < num_facets; ++f)
748 {
749 if (f_to_c->num_links(f) == 2)
750 {
751 std::array<std::int32_t, 4> pairs
752 = impl::get_cell_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
753 default_facets_int.insert(default_facets_int.end(), pairs.begin(),
754 pairs.end());
755 }
756 else if (interprocess_marker[f])
757 {
758 throw std::runtime_error(
759 "Cannot compute interior facet integral over interprocess "
760 "facet. Please use ghost mode shared facet when creating the "
761 "mesh");
762 }
763 }
764 integrals.insert({{IntegralType::interior_facet, id, form_idx},
765 {k, default_facets_int, active_coeffs}});
766 }
767 else if (sd != subdomains.end())
768 {
769 auto it = std::ranges::lower_bound(sd->second, id, std::less{},
770 [](auto& a) { return a.first; });
771 if (it != sd->second.end() and it->first == id)
772 {
773 integrals.insert({{IntegralType::interior_facet, id, form_idx},
774 {k,
775 std::vector<std::int32_t>(it->second.begin(),
776 it->second.end()),
777 active_coeffs}});
778 }
779 }
780
781 if (integral->needs_facet_permutations)
782 needs_facet_permutations = true;
783 }
784 }
785 }
786
787 return Form<T, U>(spaces, std::move(integrals), mesh, coefficients, constants,
788 needs_facet_permutations, entity_maps);
789}
790
805template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
807 const ufcx_form& ufcx_form,
808 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
809 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
810 coefficients,
811 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
812 const std::map<
814 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
815 subdomains,
816 const std::map<std::shared_ptr<const mesh::Mesh<U>>,
817 std::span<const std::int32_t>>& entity_maps,
818 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
819{
820 // Place coefficients in appropriate order
821 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
822 for (const std::string& name : get_coefficient_names(ufcx_form))
823 {
824 if (auto it = coefficients.find(name); it != coefficients.end())
825 coeff_map.push_back(it->second);
826 else
827 {
828 throw std::runtime_error("Form coefficient \"" + name
829 + "\" not provided.");
830 }
831 }
832
833 // Place constants in appropriate order
834 std::vector<std::shared_ptr<const Constant<T>>> const_map;
835 for (const std::string& name : get_constant_names(ufcx_form))
836 {
837 if (auto it = constants.find(name); it != constants.end())
838 const_map.push_back(it->second);
839 else
840 throw std::runtime_error("Form constant \"" + name + "\" not provided.");
841 }
842
843 return create_form_factory({ufcx_form}, spaces, coeff_map, const_map,
844 subdomains, entity_maps, mesh);
845}
846
865template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
867 ufcx_form* (*fptr)(),
868 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
869 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
870 coefficients,
871 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
872 const std::map<
874 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
875 subdomains,
876 const std::map<std::shared_ptr<const mesh::Mesh<U>>,
877 std::span<const std::int32_t>>& entity_maps,
878 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
879{
880 ufcx_form* form = fptr();
881 Form<T, U> L = create_form<T, U>(*form, spaces, coefficients, constants,
882 subdomains, entity_maps, mesh);
883 std::free(form);
884 return L;
885}
886
888template <std::floating_point T>
890 std::shared_ptr<mesh::Mesh<T>> mesh,
891 std::shared_ptr<const fem::FiniteElement<T>> e,
892 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
893 reorder_fn
894 = nullptr)
895{
896 // TODO: check cell type of e (need to add method to fem::FiniteElement)
897 assert(e);
898 assert(mesh);
899 assert(mesh->topology());
900 if (e->cell_type() != mesh->topology()->cell_type())
901 throw std::runtime_error("Cell type of element and mesh must match.");
902
903 // Create element dof layout
905
906 // Create a dofmap
907 std::function<void(std::span<std::int32_t>, std::uint32_t)> permute_inv
908 = e->needs_dof_permutations() ? e->dof_permutation_fn(true, true)
909 : nullptr;
910 auto dofmap = std::make_shared<const DofMap>(create_dofmap(
911 mesh->comm(), layout, *mesh->topology(), permute_inv, reorder_fn));
912
913 return FunctionSpace(mesh, e, dofmap);
914}
915
917template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
919 const ufcx_expression& e,
920 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
921 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
922 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
923{
924 if (!coefficients.empty())
925 {
926 assert(coefficients.front());
927 assert(coefficients.front()->function_space());
928 std::shared_ptr<const mesh::Mesh<U>> mesh
929 = coefficients.front()->function_space()->mesh();
930 if (mesh->geometry().cmap().hash() != e.coordinate_element_hash)
931 {
932 throw std::runtime_error(
933 "Expression and mesh geometric maps do not match.");
934 }
935 }
936
937 if (e.rank > 0 and !argument_space)
938 {
939 throw std::runtime_error("Expression has Argument but no Argument "
940 "function space was provided.");
941 }
942
943 std::vector<U> X(e.points, e.points + e.num_points * e.entity_dimension);
944 std::array<std::size_t, 2> Xshape
945 = {static_cast<std::size_t>(e.num_points),
946 static_cast<std::size_t>(e.entity_dimension)};
947 std::vector<std::size_t> value_shape(e.value_shape,
948 e.value_shape + e.num_components);
949 std::function<void(T*, const T*, const T*, const scalar_value_t<T>*,
950 const int*, const std::uint8_t*, void*)>
951 tabulate_tensor = nullptr;
952 if constexpr (std::is_same_v<T, float>)
953 tabulate_tensor = e.tabulate_tensor_float32;
954#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
955 else if constexpr (std::is_same_v<T, std::complex<float>>)
956 {
957 tabulate_tensor = reinterpret_cast<void (*)(
958 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
959 const unsigned char*, void*)>(e.tabulate_tensor_complex64);
960 }
961#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
962 else if constexpr (std::is_same_v<T, double>)
963 tabulate_tensor = e.tabulate_tensor_float64;
964#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
965 else if constexpr (std::is_same_v<T, std::complex<double>>)
966 {
967 tabulate_tensor = reinterpret_cast<void (*)(
968 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
969 const unsigned char*, void*)>(e.tabulate_tensor_complex128);
970 }
971#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
972 else
973 throw std::runtime_error("Type not supported.");
974
975 assert(tabulate_tensor);
976 return Expression(coefficients, constants, std::span<const U>(X), Xshape,
977 tabulate_tensor, value_shape, argument_space);
978}
979
982template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
984 const ufcx_expression& e,
985 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
986 coefficients,
987 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
988 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
989{
990 // Place coefficients in appropriate order
991 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
992 std::vector<std::string> coefficient_names;
993 for (int i = 0; i < e.num_coefficients; ++i)
994 coefficient_names.push_back(e.coefficient_names[i]);
995
996 for (const std::string& name : coefficient_names)
997 {
998 if (auto it = coefficients.find(name); it != coefficients.end())
999 coeff_map.push_back(it->second);
1000 else
1001 {
1002 throw std::runtime_error("Expression coefficient \"" + name
1003 + "\" not provided.");
1004 }
1005 }
1006
1007 // Place constants in appropriate order
1008 std::vector<std::shared_ptr<const Constant<T>>> const_map;
1009 std::vector<std::string> constant_names;
1010 for (int i = 0; i < e.num_constants; ++i)
1011 constant_names.push_back(e.constant_names[i]);
1012
1013 for (const std::string& name : constant_names)
1014 {
1015 if (auto it = constants.find(name); it != constants.end())
1016 const_map.push_back(it->second);
1017 else
1018 {
1019 throw std::runtime_error("Expression constant \"" + name
1020 + "\" not provided.");
1021 }
1022 }
1023
1024 return create_expression(e, coeff_map, const_map, argument_space);
1025}
1026
1027} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:26
Definition IndexMap.h:94
Timer for measuring and logging elapsed time durations.
Definition Timer.h:41
std::chrono::duration< double, Period > stop()
Stop timer and return elapsed time.
Definition Timer.h:91
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
Degree-of-freedom map.
Definition DofMap.h:73
Definition ElementDofLayout.h:30
An Expression represents a mathematical expression evaluated at a pre-defined points on a reference c...
Definition Expression.h:41
Model of a finite element.
Definition FiniteElement.h:57
const std::vector< std::shared_ptr< const FiniteElement< geometry_type > > > & sub_elements() const noexcept
Get subelements (if any)
Definition FiniteElement.cpp:396
const std::vector< std::vector< std::vector< int > > > & entity_dofs() const noexcept
Local DOFs associated with each sub-entity of the cell.
Definition FiniteElement.cpp:340
int num_sub_elements() const noexcept
Number of sub elements (for a mixed or blocked element).
Definition FiniteElement.cpp:383
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs() const noexcept
Local DOFs associated with the closure of each sub-entity of the cell.
Definition FiniteElement.cpp:347
int block_size() const noexcept
Block size of the finite element function space.
Definition FiniteElement.cpp:359
A representation of finite element variational forms.
Definition Form.h:175
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:34
Definition Function.h:47
Definition AdjacencyList.h:27
std::span< T > links(std::size_t node)
Definition AdjacencyList.h:111
Definition SparsityPattern.h:26
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:46
std::array< std::int32_t, 2 *num_cells > get_cell_facet_pairs(std::int32_t f, std::span< const std::int32_t > cells, const graph::AdjacencyList< std::int32_t > &c_to_f)
Definition utils.h:68
Functions supporting mesh operations.
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
void interior_facets(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over interior facets and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:28
void cells(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:16
Finite element method functionality.
Definition assemble_expression_impl.h:23
std::vector< std::string > get_constant_names(const ufcx_form &ufcx_form)
Get the name of each constant in a UFC form.
Definition utils.cpp:124
FunctionSpace< T > create_functionspace(std::shared_ptr< mesh::Mesh< T > > mesh, std::shared_ptr< const fem::FiniteElement< T > > e, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn=nullptr)
NEW Create a function space from a fem::FiniteElement.
Definition utils.h:889
std::vector< DofMap > create_dofmaps(MPI_Comm comm, const std::vector< ElementDofLayout > &layouts, mesh::Topology &topology, std::function< void(std::span< std::int32_t >, std::uint32_t)> permute_inv, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn)
Create a set of dofmaps on a given topology.
Definition utils.cpp:68
DofMap create_dofmap(MPI_Comm comm, const ElementDofLayout &layout, mesh::Topology &topology, std::function< void(std::span< std::int32_t >, std::uint32_t)> permute_inv, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn)
Create a dof map on mesh.
Definition utils.cpp:31
std::vector< std::string > get_coefficient_names(const ufcx_form &ufcx_form)
Definition utils.cpp:117
std::vector< std::vector< std::array< std::shared_ptr< const FunctionSpace< U > >, 2 > > > extract_function_spaces(const std::vector< std::vector< const Form< T, U > * > > &a)
Extract test (0) and trial (1) function spaces pairs for each bilinear form for a rectangular array o...
Definition utils.h:130
Expression< T, U > create_expression(const ufcx_expression &e, const std::vector< std::shared_ptr< const Function< T, U > > > &coefficients, const std::vector< std::shared_ptr< const Constant< T > > > &constants, std::shared_ptr< const FunctionSpace< U > > argument_space=nullptr)
Create Expression from UFC.
Definition utils.h:918
std::vector< std::int32_t > compute_integration_domains(IntegralType integral_type, const mesh::Topology &topology, std::span< const std::int32_t > entities)
Given an integral type and a set of entities, computes and return data for the entities that should b...
Definition utils.cpp:132
Form< T, U > create_form_factory(const std::vector< std::reference_wrapper< const ufcx_form > > &ufcx_forms, const std::vector< std::shared_ptr< const FunctionSpace< U > > > &spaces, const std::vector< std::shared_ptr< const Function< T, U > > > &coefficients, const std::vector< std::shared_ptr< const Constant< T > > > &constants, const std::map< IntegralType, std::vector< std::pair< std::int32_t, std::span< const std::int32_t > > > > &subdomains, const std::map< std::shared_ptr< const mesh::Mesh< U > >, std::span< const std::int32_t > > &entity_maps, std::shared_ptr< const mesh::Mesh< U > > mesh=nullptr)
Create a Form from UFCx input with coefficients and constants passed in the required order.
Definition utils.h:365
ElementDofLayout create_element_dof_layout(const fem::FiniteElement< T > &element, const std::vector< int > &parent_map={})
Create an ElementDofLayout from a FiniteElement.
Definition utils.h:272
Form< T, U > create_form(const ufcx_form &ufcx_form, const std::vector< std::shared_ptr< const FunctionSpace< U > > > &spaces, const std::map< std::string, std::shared_ptr< const Function< T, U > > > &coefficients, const std::map< std::string, std::shared_ptr< const Constant< T > > > &constants, const std::map< IntegralType, std::vector< std::pair< std::int32_t, std::span< const std::int32_t > > > > &subdomains, const std::map< std::shared_ptr< const mesh::Mesh< U > >, std::span< const std::int32_t > > &entity_maps, std::shared_ptr< const mesh::Mesh< U > > mesh=nullptr)
Create a Form from UFC input with coefficients and constants resolved by name.
Definition utils.h:806
IntegralType
Type of integral.
Definition Form.h:36
@ interior_facet
Interior facet.
Definition Form.h:39
@ cell
Cell.
Definition Form.h:37
@ exterior_facet
Exterior facet.
Definition Form.h:38
la::SparsityPattern create_sparsity_pattern(const Form< T, U > &a)
Create a sparsity pattern for a given form.
Definition utils.h:155
FunctionSpace(U mesh, V element, W dofmap) -> FunctionSpace< typename std::remove_cvref< typename U::element_type >::type::geometry_type::value_type >
Type deduction.
void build_sparsity_pattern(la::SparsityPattern &pattern, const Form< T, U > &a)
Build a sparsity pattern for a given form.
Definition utils.h:183
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
std::vector< std::int32_t > exterior_facet_indices(const Topology &topology, int facet_type_idx)
Compute the indices of all exterior facets that are owned by the caller.
Definition utils.cpp:58
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:110