Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/d9/dc0/fem_2utils_8h_source.html
DOLFINx 0.7.3
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 <array>
19#include <concepts>
20#include <dolfinx/common/types.h>
21#include <dolfinx/la/SparsityPattern.h>
22#include <dolfinx/mesh/Topology.h>
23#include <dolfinx/mesh/cell_types.h>
24#include <dolfinx/mesh/utils.h>
25#include <functional>
26#include <memory>
27#include <set>
28#include <span>
29#include <stdexcept>
30#include <string>
31#include <type_traits>
32#include <ufcx.h>
33#include <utility>
34#include <vector>
35
38
39namespace basix
40{
41template <std::floating_point T>
42class FiniteElement;
43}
44
45namespace dolfinx::common
46{
47class IndexMap;
48}
49
50namespace dolfinx::fem
51{
52
53namespace impl
54{
61template <int num_cells>
62std::array<std::int32_t, 2 * num_cells>
63get_cell_facet_pairs(std::int32_t f, const std::span<const std::int32_t>& cells,
65{
66 // Loop over cells sharing facet
67 assert(cells.size() == num_cells);
68 std::array<std::int32_t, 2 * num_cells> cell_local_facet_pairs;
69 for (int c = 0; c < num_cells; ++c)
70 {
71 // Get local index of facet with respect to the cell
72 std::int32_t cell = cells[c];
73 auto cell_facets = c_to_f.links(cell);
74 auto facet_it = std::find(cell_facets.begin(), cell_facets.end(), f);
75 assert(facet_it != cell_facets.end());
76 int local_f = std::distance(cell_facets.begin(), facet_it);
77 cell_local_facet_pairs[2 * c] = cell;
78 cell_local_facet_pairs[2 * c + 1] = local_f;
79 }
80
81 return cell_local_facet_pairs;
82}
83
84} // namespace impl
85
111std::vector<std::pair<int, std::vector<std::int32_t>>>
113 const mesh::Topology& topology,
114 std::span<const std::int32_t> entities, int dim,
115 std::span<const int> values);
116
121template <class U, class T>
122concept FEkernel = std::is_invocable_v<U, T*, const T*, const T*,
123 const scalar_value_type_t<T>*,
124 const int*, const std::uint8_t*>;
125
133template <dolfinx::scalar T, std::floating_point U>
134std::vector<std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
135extract_function_spaces(const std::vector<std::vector<const Form<T, U>*>>& a)
136{
137 std::vector<
138 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
139 spaces(
140 a.size(),
141 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>(
142 a.front().size()));
143 for (std::size_t i = 0; i < a.size(); ++i)
144 {
145 for (std::size_t j = 0; j < a[i].size(); ++j)
146 {
147 if (const Form<T, U>* form = a[i][j]; form)
148 spaces[i][j] = {form->function_spaces()[0], form->function_spaces()[1]};
149 }
150 }
151 return spaces;
152}
153
159template <dolfinx::scalar T, std::floating_point U>
161{
162 if (a.rank() != 2)
163 {
164 throw std::runtime_error(
165 "Cannot create sparsity pattern. Form is not a bilinear.");
166 }
167
168 // Get dof maps and mesh
169 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
170 *a.function_spaces().at(0)->dofmap(),
171 *a.function_spaces().at(1)->dofmap()};
172 std::shared_ptr mesh = a.mesh();
173 assert(mesh);
174
175 const std::set<IntegralType> types = a.integral_types();
176 if (types.find(IntegralType::interior_facet) != types.end()
177 or types.find(IntegralType::exterior_facet) != types.end())
178 {
179 // FIXME: cleanup these calls? Some of the happen internally again.
180 int tdim = mesh->topology()->dim();
181 mesh->topology_mutable()->create_entities(tdim - 1);
182 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
183 }
184
185 common::Timer t0("Build sparsity");
186
187 // Get common::IndexMaps for each dimension
188 const std::array index_maps{dofmaps[0].get().index_map,
189 dofmaps[1].get().index_map};
190 const std::array bs
191 = {dofmaps[0].get().index_map_bs(), dofmaps[1].get().index_map_bs()};
192
193 // Create and build sparsity pattern
194 la::SparsityPattern pattern(mesh->comm(), index_maps, bs);
195 for (auto type : types)
196 {
197 std::vector<int> ids = a.integral_ids(type);
198 switch (type)
199 {
201 for (int id : ids)
202 {
203 sparsitybuild::cells(pattern, a.domain(type, id),
204 {{dofmaps[0], dofmaps[1]}});
205 }
206 break;
208 for (int id : ids)
209 {
210 std::span<const std::int32_t> facets = a.domain(type, id);
211 std::vector<std::int32_t> f;
212 f.reserve(facets.size() / 2);
213 for (std::size_t i = 0; i < facets.size(); i += 4)
214 f.insert(f.end(), {facets[i], facets[i + 2]});
215 sparsitybuild::interior_facets(pattern, f, {{dofmaps[0], dofmaps[1]}});
216 }
217 break;
219 for (int id : ids)
220 {
221 std::span<const std::int32_t> facets = a.domain(type, id);
222 std::vector<std::int32_t> cells;
223 cells.reserve(facets.size() / 2);
224 for (std::size_t i = 0; i < facets.size(); i += 2)
225 cells.push_back(facets[i]);
226 sparsitybuild::cells(pattern, cells, {{dofmaps[0], dofmaps[1]}});
227 }
228 break;
229 default:
230 throw std::runtime_error("Unsupported integral type");
231 }
232 }
233
234 t0.stop();
235
236 return pattern;
237}
238
240ElementDofLayout create_element_dof_layout(const ufcx_dofmap& dofmap,
241 const mesh::CellType cell_type,
242 const std::vector<int>& parent_map
243 = {});
244
253DofMap create_dofmap(
254 MPI_Comm comm, const ElementDofLayout& layout, mesh::Topology& topology,
255 std::function<void(const std::span<std::int32_t>&, std::uint32_t)>
256 unpermute_dofs,
257 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
258 reorder_fn);
259
263std::vector<std::string> get_coefficient_names(const ufcx_form& ufcx_form);
264
268std::vector<std::string> get_constant_names(const ufcx_form& ufcx_form);
269
278template <dolfinx::scalar T, typename U = dolfinx::scalar_value_type_t<T>>
280 const ufcx_form& ufcx_form,
281 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
282 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
283 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
284 const std::map<
286 std::vector<std::pair<std::int32_t, std::vector<std::int32_t>>>>&
287 subdomains,
288 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
289{
290 if (ufcx_form.rank != (int)spaces.size())
291 throw std::runtime_error("Wrong number of argument spaces for Form.");
292 if (ufcx_form.num_coefficients != (int)coefficients.size())
293 {
294 throw std::runtime_error(
295 "Mismatch between number of expected and provided Form coefficients.");
296 }
297 if (ufcx_form.num_constants != (int)constants.size())
298 {
299 throw std::runtime_error(
300 "Mismatch between number of expected and provided Form constants.");
301 }
302
303 // Check argument function spaces
304#ifndef NDEBUG
305 for (std::size_t i = 0; i < spaces.size(); ++i)
306 {
307 assert(spaces[i]->element());
308 ufcx_finite_element* ufcx_element = ufcx_form.finite_elements[i];
309 assert(ufcx_element);
310 if (std::string(ufcx_element->signature)
311 != spaces[i]->element()->signature())
312 {
313 throw std::runtime_error(
314 "Cannot create form. Wrong type of function space for argument.");
315 }
316 }
317#endif
318
319 // Extract mesh from FunctionSpace, and check they are the same
320 if (!mesh and !spaces.empty())
321 mesh = spaces[0]->mesh();
322 for (auto& V : spaces)
323 {
324 if (mesh != V->mesh())
325 throw std::runtime_error("Incompatible mesh");
326 }
327 if (!mesh)
328 throw std::runtime_error("No mesh could be associated with the Form.");
329
330 auto topology = mesh->topology();
331 assert(topology);
332 const int tdim = topology->dim();
333
334 const int* integral_offsets = ufcx_form.form_integral_offsets;
335 std::vector<int> num_integrals_type(3);
336 for (int i = 0; i < 3; ++i)
337 num_integrals_type[i] = integral_offsets[i + 1] - integral_offsets[i];
338
339 // Create facets, if required
340 if (num_integrals_type[exterior_facet] > 0
341 or num_integrals_type[interior_facet] > 0)
342 {
343 mesh->topology_mutable()->create_entities(tdim - 1);
344 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
345 mesh->topology_mutable()->create_connectivity(tdim, tdim - 1);
346 }
347
348 // Get list of integral IDs, and load tabulate tensor into memory for
349 // each
350 using kern = std::function<void(
351 T*, const T*, const T*, const typename scalar_value_type<T>::value_type*,
352 const int*, const std::uint8_t*)>;
353 std::map<IntegralType,
354 std::vector<std::tuple<int, kern, std::vector<std::int32_t>>>>
355 integral_data;
356
357 bool needs_facet_permutations = false;
358
359 // Attach cell kernels
360 {
361 std::span<const int> ids(ufcx_form.form_integral_ids
362 + integral_offsets[cell],
363 num_integrals_type[cell]);
364 auto itg = integral_data.insert({IntegralType::cell, {}});
365 auto sd = subdomains.find(IntegralType::cell);
366 for (int i = 0; i < num_integrals_type[cell]; ++i)
367 {
368 const int id = ids[i];
369 ufcx_integral* integral
370 = ufcx_form.form_integrals[integral_offsets[cell] + i];
371 assert(integral);
372
373 kern k = nullptr;
374 if constexpr (std::is_same_v<T, float>)
375 k = integral->tabulate_tensor_float32;
376 else if constexpr (std::is_same_v<T, std::complex<float>>)
377 {
378 k = reinterpret_cast<void (*)(
379 T*, const T*, const T*,
380 const typename scalar_value_type<T>::value_type*, const int*,
381 const unsigned char*)>(integral->tabulate_tensor_complex64);
382 }
383 else if constexpr (std::is_same_v<T, double>)
384 k = integral->tabulate_tensor_float64;
385 else if constexpr (std::is_same_v<T, std::complex<double>>)
386 {
387 k = reinterpret_cast<void (*)(
388 T*, const T*, const T*,
389 const typename scalar_value_type<T>::value_type*, const int*,
390 const unsigned char*)>(integral->tabulate_tensor_complex128);
391 }
392 assert(k);
393
394 // Build list of entities to assembler over
395 if (id == -1)
396 {
397 // Default kernel, operates on all (owned) cells
398 assert(topology->index_map(tdim));
399 std::vector<std::int32_t> e;
400 e.resize(topology->index_map(tdim)->size_local(), 0);
401 std::iota(e.begin(), e.end(), 0);
402 itg.first->second.emplace_back(id, k, std::move(e));
403 }
404 else if (sd != subdomains.end())
405 {
406 // NOTE: This requires that pairs are sorted
407 auto it = std::lower_bound(sd->second.begin(), sd->second.end(), id,
408 [](auto& pair, auto val)
409 { return pair.first < val; });
410 if (it != sd->second.end() and it->first == id)
411 itg.first->second.emplace_back(id, k, it->second);
412 }
413
414 if (integral->needs_facet_permutations)
415 needs_facet_permutations = true;
416 }
417 }
418
419 // Attach exterior facet kernels
420 {
421 std::span<const int> ids(ufcx_form.form_integral_ids
422 + integral_offsets[exterior_facet],
423 num_integrals_type[exterior_facet]);
424 auto itg = integral_data.insert({IntegralType::exterior_facet, {}});
425 auto sd = subdomains.find(IntegralType::exterior_facet);
426 for (int i = 0; i < num_integrals_type[exterior_facet]; ++i)
427 {
428 const int id = ids[i];
429 ufcx_integral* integral
430 = ufcx_form.form_integrals[integral_offsets[exterior_facet] + i];
431 assert(integral);
432
433 kern k = nullptr;
434 if constexpr (std::is_same_v<T, float>)
435 k = integral->tabulate_tensor_float32;
436 else if constexpr (std::is_same_v<T, std::complex<float>>)
437 {
438 k = reinterpret_cast<void (*)(
439 T*, const T*, const T*,
440 const typename scalar_value_type<T>::value_type*, const int*,
441 const unsigned char*)>(integral->tabulate_tensor_complex64);
442 }
443 else if constexpr (std::is_same_v<T, double>)
444 k = integral->tabulate_tensor_float64;
445 else if constexpr (std::is_same_v<T, std::complex<double>>)
446 {
447 k = reinterpret_cast<void (*)(
448 T*, const T*, const T*,
449 const typename scalar_value_type<T>::value_type*, const int*,
450 const unsigned char*)>(integral->tabulate_tensor_complex128);
451 }
452 assert(k);
453
454 // Build list of entities to assembler over
455 const std::vector bfacets = mesh::exterior_facet_indices(*topology);
456 auto f_to_c = topology->connectivity(tdim - 1, tdim);
457 assert(f_to_c);
458 auto c_to_f = topology->connectivity(tdim, tdim - 1);
459 assert(c_to_f);
460 if (id == -1)
461 {
462 // Default kernel, operates on all (owned) exterior facets
463 std::vector<std::int32_t> e;
464 e.reserve(2 * bfacets.size());
465 for (std::int32_t f : bfacets)
466 {
467 // There will only be one pair for an exterior facet integral
468 auto pair
469 = impl::get_cell_facet_pairs<1>(f, f_to_c->links(f), *c_to_f);
470 e.insert(e.end(), pair.begin(), pair.end());
471 }
472 itg.first->second.emplace_back(id, k, std::move(e));
473 }
474 else if (sd != subdomains.end())
475 {
476 // NOTE: This requires that pairs are sorted
477 auto it = std::lower_bound(sd->second.begin(), sd->second.end(), id,
478 [](auto& pair, auto val)
479 { return pair.first < val; });
480 if (it != sd->second.end() and it->first == id)
481 itg.first->second.emplace_back(id, k, it->second);
482 }
483
484 if (integral->needs_facet_permutations)
485 needs_facet_permutations = true;
486 }
487 }
488
489 // Attach interior facet kernels
490 {
491 std::span<const int> ids(ufcx_form.form_integral_ids
492 + integral_offsets[interior_facet],
493 num_integrals_type[interior_facet]);
494 auto itg = integral_data.insert({IntegralType::interior_facet, {}});
495 auto sd = subdomains.find(IntegralType::interior_facet);
496 for (int i = 0; i < num_integrals_type[interior_facet]; ++i)
497 {
498 const int id = ids[i];
499 ufcx_integral* integral
500 = ufcx_form.form_integrals[integral_offsets[interior_facet] + i];
501 assert(integral);
502
503 kern k = nullptr;
504 if constexpr (std::is_same_v<T, float>)
505 k = integral->tabulate_tensor_float32;
506 else if constexpr (std::is_same_v<T, std::complex<float>>)
507 {
508 k = reinterpret_cast<void (*)(
509 T*, const T*, const T*,
510 const typename scalar_value_type<T>::value_type*, const int*,
511 const unsigned char*)>(integral->tabulate_tensor_complex64);
512 }
513 else if constexpr (std::is_same_v<T, double>)
514 k = integral->tabulate_tensor_float64;
515 else if constexpr (std::is_same_v<T, std::complex<double>>)
516 {
517 k = reinterpret_cast<void (*)(
518 T*, const T*, const T*,
519 const typename scalar_value_type<T>::value_type*, const int*,
520 const unsigned char*)>(integral->tabulate_tensor_complex128);
521 }
522 assert(k);
523
524 // Build list of entities to assembler over
525 auto f_to_c = topology->connectivity(tdim - 1, tdim);
526 assert(f_to_c);
527 auto c_to_f = topology->connectivity(tdim, tdim - 1);
528 assert(c_to_f);
529 if (id == -1)
530 {
531 // Default kernel, operates on all (owned) interior facets
532 std::vector<std::int32_t> e;
533 assert(topology->index_map(tdim - 1));
534 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local();
535 e.reserve(4 * num_facets);
536 for (std::int32_t f = 0; f < num_facets; ++f)
537 {
538 if (f_to_c->num_links(f) == 2)
539 {
540 auto pairs
541 = impl::get_cell_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
542 e.insert(e.end(), pairs.begin(), pairs.end());
543 }
544 }
545 itg.first->second.emplace_back(id, k, std::move(e));
546 }
547 else if (sd != subdomains.end())
548 {
549 auto it = std::lower_bound(sd->second.begin(), sd->second.end(), id,
550 [](auto& pair, auto val)
551 { return pair.first < val; });
552 if (it != sd->second.end() and it->first == id)
553 itg.first->second.emplace_back(id, k, it->second);
554 }
555
556 if (integral->needs_facet_permutations)
557 needs_facet_permutations = true;
558 }
559 }
560
561 std::map<IntegralType,
562 std::vector<std::pair<std::int32_t, std::vector<std::int32_t>>>>
563 sd;
564 for (auto& [itg, data] : subdomains)
565 {
566 std::vector<std::pair<std::int32_t, std::vector<std::int32_t>>> x;
567 for (auto& [id, idx] : data)
568 x.emplace_back(id, std::vector(idx.data(), idx.data() + idx.size()));
569 sd.insert({itg, std::move(x)});
570 }
571
572 return Form<T, U>(spaces, integral_data, coefficients, constants,
573 needs_facet_permutations, mesh);
574}
575
586template <dolfinx::scalar T, typename U = dolfinx::scalar_value_type_t<T>>
588 const ufcx_form& ufcx_form,
589 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
590 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
591 coefficients,
592 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
593 const std::map<
595 std::vector<std::pair<std::int32_t, std::vector<std::int32_t>>>>&
596 subdomains,
597 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
598{
599 // Place coefficients in appropriate order
600 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
601 for (const std::string& name : get_coefficient_names(ufcx_form))
602 {
603 if (auto it = coefficients.find(name); it != coefficients.end())
604 coeff_map.push_back(it->second);
605 else
606 {
607 throw std::runtime_error("Form coefficient \"" + name
608 + "\" not provided.");
609 }
610 }
611
612 // Place constants in appropriate order
613 std::vector<std::shared_ptr<const Constant<T>>> const_map;
614 for (const std::string& name : get_constant_names(ufcx_form))
615 {
616 if (auto it = constants.find(name); it != constants.end())
617 const_map.push_back(it->second);
618 else
619 throw std::runtime_error("Form constant \"" + name + "\" not provided.");
620 }
621
622 return create_form(ufcx_form, spaces, coeff_map, const_map, subdomains, mesh);
623}
624
637template <dolfinx::scalar T, typename U = dolfinx::scalar_value_type_t<T>>
639 ufcx_form* (*fptr)(),
640 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
641 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
642 coefficients,
643 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
644 const std::map<
646 std::vector<std::pair<std::int32_t, std::vector<std::int32_t>>>>&
647 subdomains,
648 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
649{
650 ufcx_form* form = fptr();
651 Form<T, U> L = create_form<T, U>(*form, spaces, coefficients, constants,
652 subdomains, mesh);
653 std::free(form);
654 return L;
655}
656
668template <std::floating_point T>
670 std::shared_ptr<mesh::Mesh<T>> mesh, const basix::FiniteElement<T>& e,
671 const std::vector<std::size_t>& value_shape = {},
672 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
673 reorder_fn
674 = nullptr)
675{
676 // Create a DOLFINx element
677 auto _e = std::make_shared<FiniteElement<T>>(e, value_shape);
678 assert(_e);
679
680 // Create UFC subdofmaps and compute offset
681 const int num_sub_elements = _e->num_sub_elements();
682 std::vector<ElementDofLayout> sub_doflayout;
683 sub_doflayout.reserve(num_sub_elements);
684 for (int i = 0; i < num_sub_elements; ++i)
685 {
686 auto sub_element = _e->extract_sub_element({i});
687 std::vector<int> parent_map_sub(sub_element->space_dimension());
688 for (std::size_t j = 0; j < parent_map_sub.size(); ++j)
689 parent_map_sub[j] = i + _e->block_size() * j;
690 sub_doflayout.emplace_back(1, e.entity_dofs(), e.entity_closure_dofs(),
691 parent_map_sub, std::vector<ElementDofLayout>());
692 }
693
694 // Create a dofmap
695 ElementDofLayout layout(_e->block_size(), e.entity_dofs(),
696 e.entity_closure_dofs(), {}, sub_doflayout);
697 std::function<void(const std::span<std::int32_t>&, std::uint32_t)>
698 unpermute_dofs = nullptr;
699 if (_e->needs_dof_permutations())
700 unpermute_dofs = _e->get_dof_permutation_function(true, true);
701 assert(mesh);
702 assert(mesh->topology());
703 auto dofmap = std::make_shared<const DofMap>(create_dofmap(
704 mesh->comm(), layout, *mesh->topology(), unpermute_dofs, reorder_fn));
705 return FunctionSpace(mesh, _e, dofmap);
706}
707
718template <std::floating_point T>
720 ufcx_function_space* (*fptr)(const char*), const std::string& function_name,
721 std::shared_ptr<mesh::Mesh<T>> mesh,
722 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
723 reorder_fn
724 = nullptr)
725{
726 ufcx_function_space* space = fptr(function_name.c_str());
727 if (!space)
728 {
729 throw std::runtime_error(
730 "Could not create UFC function space with function name "
731 + function_name);
732 }
733
734 ufcx_finite_element* ufcx_element = space->finite_element;
735 assert(ufcx_element);
736
737 const auto& geometry = mesh->geometry();
738 if (geometry.cmaps().size() > 1)
739 throw std::runtime_error("Not supported for Mixed Topology");
740
741 const auto& cmap = geometry.cmaps()[0];
742 if (space->geometry_degree != cmap.degree()
743 or static_cast<basix::cell::type>(space->geometry_basix_cell)
744 != mesh::cell_type_to_basix_type(cmap.cell_shape())
745 or static_cast<basix::element::lagrange_variant>(
746 space->geometry_basix_variant)
747 != cmap.variant())
748 {
749 throw std::runtime_error("UFL mesh and CoordinateElement do not match.");
750 }
751
752 auto element = std::make_shared<FiniteElement<T>>(*ufcx_element);
753 assert(element);
754 ufcx_dofmap* ufcx_map = space->dofmap;
755 assert(ufcx_map);
756 const auto topology = mesh->topology();
757 assert(topology);
758 ElementDofLayout layout
759 = create_element_dof_layout(*ufcx_map, topology->cell_types()[0]);
760
761 std::function<void(const std::span<std::int32_t>&, std::uint32_t)>
762 unpermute_dofs;
763 if (element->needs_dof_permutations())
764 unpermute_dofs = element->get_dof_permutation_function(true, true);
765 return FunctionSpace(
766 mesh, element,
767 std::make_shared<DofMap>(create_dofmap(mesh->comm(), layout, *topology,
768 unpermute_dofs, reorder_fn)));
769}
770
772namespace impl
773{
775template <dolfinx::scalar T, std::floating_point U>
776std::span<const std::uint32_t> get_cell_orientation_info(
777 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients)
778{
779 bool needs_dof_transformations = false;
780 for (auto coeff : coefficients)
781 {
782 auto element = coeff->function_space()->element();
783 if (element->needs_dof_transformations())
784 {
785 needs_dof_transformations = true;
786 break;
787 }
788 }
789
790 std::span<const std::uint32_t> cell_info;
791 if (needs_dof_transformations)
792 {
793 auto mesh = coefficients.front()->function_space()->mesh();
794 mesh->topology_mutable()->create_entity_permutations();
795 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
796 }
797
798 return cell_info;
799}
800
802template <dolfinx::scalar T, int _bs>
803void pack(std::span<T> coeffs, std::int32_t cell, int bs, std::span<const T> v,
804 std::span<const std::uint32_t> cell_info, const DofMap& dofmap,
805 auto transform)
806{
807 auto dofs = dofmap.cell_dofs(cell);
808 for (std::size_t i = 0; i < dofs.size(); ++i)
809 {
810 if constexpr (_bs < 0)
811 {
812 const int pos_c = bs * i;
813 const int pos_v = bs * dofs[i];
814 for (int k = 0; k < bs; ++k)
815 coeffs[pos_c + k] = v[pos_v + k];
816 }
817 else
818 {
819 const int pos_c = _bs * i;
820 const int pos_v = _bs * dofs[i];
821 for (int k = 0; k < _bs; ++k)
822 coeffs[pos_c + k] = v[pos_v + k];
823 }
824 }
825
826 transform(coeffs, cell_info, cell, 1);
827}
828
831template <typename F>
832concept FetchCells = requires(F&& f, std::span<const std::int32_t> v) {
833 requires std::invocable<F, std::span<const std::int32_t>>;
834 {
835 f(v)
836 } -> std::convertible_to<std::int32_t>;
837};
838
852template <dolfinx::scalar T, std::floating_point U>
853void pack_coefficient_entity(std::span<T> c, int cstride,
854 const Function<T, U>& u,
855 std::span<const std::uint32_t> cell_info,
856 std::span<const std::int32_t> entities,
857 std::size_t estride, FetchCells auto&& fetch_cells,
858 std::int32_t offset)
859{
860 // Read data from coefficient Function u
861 std::span<const T> v = u.x()->array();
862 const DofMap& dofmap = *u.function_space()->dofmap();
863 auto element = u.function_space()->element();
864 assert(element);
865 int space_dim = element->space_dimension();
866 auto transformation
867 = element->template get_dof_transformation_function<T>(false, true);
868 const int bs = dofmap.bs();
869 switch (bs)
870 {
871 case 1:
872 for (std::size_t e = 0; e < entities.size(); e += estride)
873 {
874 auto entity = entities.subspan(e, estride);
875 std::int32_t cell = fetch_cells(entity);
876 auto cell_coeff = c.subspan((e / estride) * cstride + offset, space_dim);
877 pack<T, 1>(cell_coeff, cell, bs, v, cell_info, dofmap, transformation);
878 }
879 break;
880 case 2:
881 for (std::size_t e = 0; e < entities.size(); e += estride)
882 {
883 auto entity = entities.subspan(e, estride);
884 std::int32_t cell = fetch_cells(entity);
885 auto cell_coeff = c.subspan((e / estride) * cstride + offset, space_dim);
886 pack<T, 2>(cell_coeff, cell, bs, v, cell_info, dofmap, transformation);
887 }
888 break;
889 case 3:
890 for (std::size_t e = 0; e < entities.size(); e += estride)
891 {
892 auto entity = entities.subspan(e, estride);
893 std::int32_t cell = fetch_cells(entity);
894 auto cell_coeff = c.subspan(e / estride * cstride + offset, space_dim);
895 pack<T, 3>(cell_coeff, cell, bs, v, cell_info, dofmap, transformation);
896 }
897 break;
898 default:
899 for (std::size_t e = 0; e < entities.size(); e += estride)
900 {
901 auto entity = entities.subspan(e, estride);
902 std::int32_t cell = fetch_cells(entity);
903 auto cell_coeff = c.subspan((e / estride) * cstride + offset, space_dim);
904 pack<T, -1>(cell_coeff, cell, bs, v, cell_info, dofmap, transformation);
905 }
906 break;
907 }
908}
909
910} // namespace impl
911
918template <dolfinx::scalar T, std::floating_point U>
919std::pair<std::vector<T>, int>
921 int id)
922{
923 // Get form coefficient offsets and dofmaps
924 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
925 = form.coefficients();
926 const std::vector<int> offsets = form.coefficient_offsets();
927
928 std::size_t num_entities = 0;
929 int cstride = 0;
930 if (!coefficients.empty())
931 {
932 cstride = offsets.back();
933 num_entities = form.domain(integral_type, id).size();
934 if (integral_type == IntegralType::exterior_facet
935 or integral_type == IntegralType::interior_facet)
936 {
937 num_entities /= 2;
938 }
939 }
940
941 return {std::vector<T>(num_entities * cstride), cstride};
942}
943
948template <dolfinx::scalar T, std::floating_point U>
949std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>>
951{
952 std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>> coeffs;
953 for (auto integral_type : form.integral_types())
954 {
955 for (int id : form.integral_ids(integral_type))
956 {
957 coeffs.emplace_hint(
958 coeffs.end(), std::pair(integral_type, id),
959 allocate_coefficient_storage(form, integral_type, id));
960 }
961 }
962
963 return coeffs;
964}
965
973template <dolfinx::scalar T, std::floating_point U>
974void pack_coefficients(const Form<T, U>& form, IntegralType integral_type,
975 int id, std::span<T> c, int cstride)
976{
977 // Get form coefficient offsets and dofmaps
978 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
979 = form.coefficients();
980 const std::vector<int> offsets = form.coefficient_offsets();
981
982 if (!coefficients.empty())
983 {
984 std::span<const std::uint32_t> cell_info
985 = impl::get_cell_orientation_info(coefficients);
986
987 switch (integral_type)
988 {
989 case IntegralType::cell:
990 {
991 auto fetch_cell = [](auto entity) { return entity.front(); };
992
993 // Iterate over coefficients
994 std::span<const std::int32_t> cells = form.domain(IntegralType::cell, id);
995 for (std::size_t coeff = 0; coeff < coefficients.size(); ++coeff)
996 {
997 impl::pack_coefficient_entity(c, cstride, *coefficients[coeff],
998 cell_info, cells, 1, fetch_cell,
999 offsets[coeff]);
1000 }
1001 break;
1002 }
1003 case IntegralType::exterior_facet:
1004 {
1005 // Function to fetch cell index from exterior facet entity
1006 auto fetch_cell = [](auto entity) { return entity.front(); };
1007
1008 // Iterate over coefficients
1009 std::span<const std::int32_t> facets
1010 = form.domain(IntegralType::exterior_facet, id);
1011 for (std::size_t coeff = 0; coeff < coefficients.size(); ++coeff)
1012 {
1013 impl::pack_coefficient_entity(c, cstride, *coefficients[coeff],
1014 cell_info, facets, 2, fetch_cell,
1015 offsets[coeff]);
1016 }
1017 break;
1018 }
1019 case IntegralType::interior_facet:
1020 {
1021 // Functions to fetch cell indices from interior facet entity
1022 auto fetch_cell0 = [](auto entity) { return entity[0]; };
1023 auto fetch_cell1 = [](auto entity) { return entity[2]; };
1024
1025 // Iterate over coefficients
1026 std::span<const std::int32_t> facets
1027 = form.domain(IntegralType::interior_facet, id);
1028 for (std::size_t coeff = 0; coeff < coefficients.size(); ++coeff)
1029 {
1030 // Pack coefficient ['+']
1031 impl::pack_coefficient_entity(c, 2 * cstride, *coefficients[coeff],
1032 cell_info, facets, 4, fetch_cell0,
1033 2 * offsets[coeff]);
1034 // Pack coefficient ['-']
1035 impl::pack_coefficient_entity(c, 2 * cstride, *coefficients[coeff],
1036 cell_info, facets, 4, fetch_cell1,
1037 offsets[coeff] + offsets[coeff + 1]);
1038 }
1039 break;
1040 }
1041 default:
1042 throw std::runtime_error(
1043 "Could not pack coefficient. Integral type not supported.");
1044 }
1045 }
1046}
1047
1049template <dolfinx::scalar T, typename U = dolfinx::scalar_value_type_t<T>>
1051 const ufcx_expression& e,
1052 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
1053 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
1054 std::shared_ptr<const FunctionSpace<U>> argument_function_space = nullptr)
1055{
1056 if (e.rank > 0 and !argument_function_space)
1057 {
1058 throw std::runtime_error("Expression has Argument but no Argument "
1059 "function space was provided.");
1060 }
1061
1062 std::vector<U> X(e.points, e.points + e.num_points * e.topological_dimension);
1063 std::array<std::size_t, 2> Xshape
1064 = {static_cast<std::size_t>(e.num_points),
1065 static_cast<std::size_t>(e.topological_dimension)};
1066 std::vector<int> value_shape(e.value_shape, e.value_shape + e.num_components);
1067 std::function<void(T*, const T*, const T*,
1068 const typename scalar_value_type<T>::value_type*,
1069 const int*, const std::uint8_t*)>
1070 tabulate_tensor = nullptr;
1071 if constexpr (std::is_same_v<T, float>)
1072 tabulate_tensor = e.tabulate_tensor_float32;
1073 else if constexpr (std::is_same_v<T, std::complex<float>>)
1074 {
1075 tabulate_tensor = reinterpret_cast<void (*)(
1076 T*, const T*, const T*,
1077 const typename scalar_value_type<T>::value_type*, const int*,
1078 const unsigned char*)>(e.tabulate_tensor_complex64);
1079 }
1080 else if constexpr (std::is_same_v<T, double>)
1081 tabulate_tensor = e.tabulate_tensor_float64;
1082 else if constexpr (std::is_same_v<T, std::complex<double>>)
1083 {
1084 tabulate_tensor = reinterpret_cast<void (*)(
1085 T*, const T*, const T*,
1086 const typename scalar_value_type<T>::value_type*, const int*,
1087 const unsigned char*)>(e.tabulate_tensor_complex128);
1088 }
1089 else
1090 throw std::runtime_error("Type not supported.");
1091
1092 assert(tabulate_tensor);
1093 return Expression(coefficients, constants, std::span<const U>(X), Xshape,
1094 tabulate_tensor, value_shape, argument_function_space);
1095}
1096
1099template <dolfinx::scalar T, typename U = dolfinx::scalar_value_type_t<T>>
1101 const ufcx_expression& e,
1102 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
1103 coefficients,
1104 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
1105 std::shared_ptr<const FunctionSpace<U>> argument_function_space = nullptr)
1106{
1107 // Place coefficients in appropriate order
1108 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
1109 std::vector<std::string> coefficient_names;
1110 for (int i = 0; i < e.num_coefficients; ++i)
1111 coefficient_names.push_back(e.coefficient_names[i]);
1112
1113 for (const std::string& name : coefficient_names)
1114 {
1115 if (auto it = coefficients.find(name); it != coefficients.end())
1116 coeff_map.push_back(it->second);
1117 else
1118 {
1119 throw std::runtime_error("Expression coefficient \"" + name
1120 + "\" not provided.");
1121 }
1122 }
1123
1124 // Place constants in appropriate order
1125 std::vector<std::shared_ptr<const Constant<T>>> const_map;
1126 std::vector<std::string> constant_names;
1127 for (int i = 0; i < e.num_constants; ++i)
1128 constant_names.push_back(e.constant_names[i]);
1129
1130 for (const std::string& name : constant_names)
1131 {
1132 if (auto it = constants.find(name); it != constants.end())
1133 const_map.push_back(it->second);
1134 else
1135 {
1136 throw std::runtime_error("Expression constant \"" + name
1137 + "\" not provided.");
1138 }
1139 }
1140
1141 return create_expression(e, coeff_map, const_map, argument_function_space);
1142}
1143
1149template <dolfinx::scalar T, std::floating_point U>
1151 std::map<std::pair<IntegralType, int>,
1152 std::pair<std::vector<T>, int>>& coeffs)
1153{
1154 for (auto& [key, val] : coeffs)
1155 pack_coefficients<T>(form, key.first, key.second, val.first, val.second);
1156}
1157
1164template <dolfinx::scalar T, std::floating_point U>
1165std::pair<std::vector<T>, int>
1167 std::span<const std::int32_t> cells)
1168{
1169 // Get form coefficient offsets and dofmaps
1170 const std::vector<std::shared_ptr<const Function<T, U>>>& coeffs
1171 = e.coefficients();
1172 const std::vector<int> offsets = e.coefficient_offsets();
1173
1174 // Copy data into coefficient array
1175 const int cstride = offsets.back();
1176 std::vector<T> c(cells.size() * offsets.back());
1177 if (!coeffs.empty())
1178 {
1179 std::span<const std::uint32_t> cell_info
1180 = impl::get_cell_orientation_info(coeffs);
1181
1182 // Iterate over coefficients
1183 for (std::size_t coeff = 0; coeff < coeffs.size(); ++coeff)
1184 {
1185 impl::pack_coefficient_entity(
1186 std::span(c), cstride, *coeffs[coeff], cell_info, cells, 1,
1187 [](auto entity) { return entity[0]; }, offsets[coeff]);
1188 }
1189 }
1190 return {std::move(c), cstride};
1191}
1192
1195template <typename U>
1196std::vector<typename U::scalar_type> pack_constants(const U& u)
1197{
1198 using T = typename U::scalar_type;
1199 const std::vector<std::shared_ptr<const Constant<T>>>& constants
1200 = u.constants();
1201
1202 // Calculate size of array needed to store packed constants
1203 std::int32_t size = std::accumulate(constants.cbegin(), constants.cend(), 0,
1204 [](std::int32_t sum, auto& constant)
1205 { return sum + constant->value.size(); });
1206
1207 // Pack constants
1208 std::vector<T> constant_values(size);
1209 std::int32_t offset = 0;
1210 for (auto& constant : constants)
1211 {
1212 const std::vector<T>& value = constant->value;
1213 std::copy(value.begin(), value.end(),
1214 std::next(constant_values.begin(), offset));
1215 offset += value.size();
1216 }
1217
1218 return constant_values;
1219}
1220
1221} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:26
A timer can be used for timing tasks. The basic usage is.
Definition Timer.h:31
Constant value which can be attached to a Form.
Definition Constant.h:23
Degree-of-freedom map.
Definition DofMap.h:76
std::span< const std::int32_t > cell_dofs(std::int32_t c) const
Local-to-global mapping of dofs on a cell.
Definition DofMap.h:130
int bs() const noexcept
Return the block size for the dofmap.
Definition DofMap.cpp:189
The class represents the degree-of-freedom (dofs) for an element. Dofs are associated with a mesh ent...
Definition ElementDofLayout.h:31
Represents a mathematical expression evaluated at a pre-defined set of points on the reference cell.
Definition Expression.h:41
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Get coefficients.
Definition Expression.h:112
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Expression.h:132
A representation of finite element variational forms.
Definition Form.h:66
const std::vector< std::shared_ptr< const FunctionSpace< U > > > & function_spaces() const
Return function spaces for all arguments.
Definition Form.h:143
int rank() const
Rank of the form (bilinear form = 2, linear form = 1, functional = 0, etc)
Definition Form.h:134
std::shared_ptr< const mesh::Mesh< U > > mesh() const
Extract common mesh for the form.
Definition Form.h:138
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell. Used to pack data for multiple coefficients in...
Definition Form.h:241
std::span< const std::int32_t > domain(IntegralType type, int i) const
Get the list of cell indices for the ith integral (kernel) for the cell domain type.
Definition Form.h:218
std::vector< int > integral_ids(IntegralType type) const
Get the IDs for integrals (kernels) for given integral type. The IDs correspond to the domain IDs whi...
Definition Form.h:192
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:166
const std::vector< std::shared_ptr< const Function< T, U > > > & coefficients() const
Access coefficients.
Definition Form.h:228
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:35
This class represents a function in a finite element function space , given by.
Definition Function.h:45
std::shared_ptr< const FunctionSpace< U > > function_space() const
Access the function space.
Definition Function.h:140
std::shared_ptr< const la::Vector< T > > x() const
Underlying vector.
Definition Function.h:146
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition AdjacencyList.h:28
std::span< T > links(std::size_t node)
Get the links (edges) for given node.
Definition AdjacencyList.h:112
Sparsity pattern data structure that can be used to initialize sparse matrices. After assembly,...
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:44
Finite element cell kernel concept.
Definition utils.h:122
Concepts for function that returns cell index.
Definition utils.h:832
void pack(std::span< T > coeffs, std::int32_t cell, int bs, std::span< const T > v, std::span< const std::uint32_t > cell_info, const DofMap &dofmap, auto transform)
Pack a single coefficient for a single cell.
Definition utils.h:803
std::array< std::int32_t, 2 *num_cells > get_cell_facet_pairs(std::int32_t f, const std::span< const std::int32_t > &cells, const graph::AdjacencyList< std::int32_t > &c_to_f)
Helper function to get an array of of (cell, local_facet) pairs corresponding to a given facet index.
Definition utils.h:63
void pack_coefficient_entity(std::span< T > c, int cstride, const Function< T, U > &u, std::span< const std::uint32_t > cell_info, std::span< const std::int32_t > entities, std::size_t estride, FetchCells auto &&fetch_cells, std::int32_t offset)
Pack a single coefficient for a set of active entities.
Definition utils.h:853
Functions supporting mesh operations.
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
void cells(la::SparsityPattern &pattern, std::span< const std::int32_t > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:18
void interior_facets(la::SparsityPattern &pattern, std::span< const std::int32_t > facets, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over interior facets and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:28
Finite element method functionality.
Definition assemble_matrix_impl.h:25
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:146
std::pair< std::vector< T >, int > allocate_coefficient_storage(const Form< T, U > &form, IntegralType integral_type, int id)
Allocate storage for coefficients of a pair (integral_type, id) from a Form.
Definition utils.h:920
std::vector< std::pair< int, std::vector< std::int32_t > > > compute_integration_domains(IntegralType integral_type, const mesh::Topology &topology, std::span< const std::int32_t > entities, int dim, std::span< const int > values)
Given an integral type and mesh tag data, compute the entities that should be integrated over.
Definition utils.cpp:156
Form< T, U > create_form(const ufcx_form &ufcx_form, 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::vector< std::int32_t > > > > &subdomains, std::shared_ptr< const mesh::Mesh< U > > mesh=nullptr)
Create a Form from UFC input.
Definition utils.h:279
std::vector< std::string > get_coefficient_names(const ufcx_form &ufcx_form)
Get the name of each coefficient in a UFC form.
Definition utils.cpp:137
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:135
std::vector< typename U::scalar_type > pack_constants(const U &u)
Pack constants of u of generic type U ready for assembly.
Definition utils.h:1196
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_function_space=nullptr)
Create Expression from UFC.
Definition utils.h:1050
IntegralType
Type of integral.
Definition Form.h:33
@ interior_facet
Interior facet.
@ exterior_facet
Exterior facet.
FunctionSpace< T > create_functionspace(std::shared_ptr< mesh::Mesh< T > > mesh, const basix::FiniteElement< T > &e, const std::vector< std::size_t > &value_shape={}, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn=nullptr)
Create a function space from a Basix element.
Definition utils.h:669
la::SparsityPattern create_sparsity_pattern(const Form< T, U > &a)
Create a sparsity pattern for a given form.
Definition utils.h:160
ElementDofLayout create_element_dof_layout(const ufcx_dofmap &dofmap, const mesh::CellType cell_type, const std::vector< int > &parent_map={})
Create an ElementDofLayout from a ufcx_dofmap.
Definition utils.cpp:32
DofMap create_dofmap(MPI_Comm comm, const ElementDofLayout &layout, mesh::Topology &topology, std::function< void(const std::span< std::int32_t > &, std::uint32_t)> unpermute_dofs, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn)
Create a dof map on mesh.
Definition utils.cpp:98
FunctionSpace(U mesh, V element, W dofmap) -> FunctionSpace< typename std::remove_cvref< typename U::element_type >::type::geometry_type::value_type >
Type deduction.
void pack_coefficients(const Form< T, U > &form, IntegralType integral_type, int id, std::span< T > c, int cstride)
Pack coefficients of a Form for a given integral type and domain id.
Definition utils.h:974