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