DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
interpolate.h
1// Copyright (C) 2020-2026 Garth N. Wells, Igor A. Baratta, Massimiliano Leoni
2// and Jørgen S.Dokken
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include "CoordinateElement.h"
11#include "DofMap.h"
12#include "FiniteElement.h"
13#include "FunctionSpace.h"
14#include <algorithm>
15#include <basix/mdspan.hpp>
16#include <concepts>
17#include <dolfinx/common/IndexMap.h>
18#include <dolfinx/common/types.h>
19#include <dolfinx/geometry/utils.h>
20#include <dolfinx/mesh/Mesh.h>
21#include <functional>
22#include <numeric>
23#include <ranges>
24#include <span>
25#include <stdexcept>
26#include <vector>
27
28namespace dolfinx::fem
29{
30template <dolfinx::scalar T, std::floating_point U>
31class Function;
32
43template <std::floating_point T>
44std::vector<T> interpolation_coords(const fem::FiniteElement<T>& element,
46 mesh::CellRange auto&& cells)
47{
48 // Find CoordinateElement appropriate to element
49 auto cmap_index = [&geometry](mesh::CellType cell_type)
50 {
51 for (std::size_t i = 0; i < geometry.cmaps().size(); ++i)
52 {
53 if (geometry.cmaps().at(i).cell_shape() == cell_type)
54 return i;
55 }
56 throw std::runtime_error("Cannot find CoordinateElement for FiniteElement");
57 };
58 int index = cmap_index(element.cell_type());
59
60 // Get geometry data and the element coordinate map
61 const std::size_t gdim = geometry.dim();
62 auto x_dofmap = geometry.dofmaps().at(index);
63 std::span<const T> x_g = geometry.x();
64
65 const CoordinateElement<T>& cmap = geometry.cmaps().at(index);
66 const std::size_t num_dofs_g = cmap.dim();
67
68 // Get the interpolation points on the reference cells
69 const auto [X, Xshape] = element.interpolation_points();
70
71 // Evaluate coordinate element basis at reference points
72 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(0, Xshape[0]);
73 std::vector<T> phi_b(
74 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
75 md::mdspan<const T, md::extents<std::size_t, 1, md::dynamic_extent,
76 md::dynamic_extent, 1>>
77 phi_full(phi_b.data(), phi_shape);
78 cmap.tabulate(0, X, Xshape, phi_b);
79 auto phi = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
80
81 // Push reference coordinates (X) forward to the physical coordinates
82 // (x) for each cell
83 std::vector<T> coordinate_dofs(num_dofs_g * gdim, 0);
84 std::vector<T> x(3 * (cells.size() * Xshape[0]), 0);
85 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
86 {
87 // Get geometry data for current cell
88 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
89 for (std::size_t i = 0; i < x_dofs.size(); ++i)
90 {
91 std::copy_n(std::next(x_g.begin(), 3 * x_dofs[i]), gdim,
92 std::next(coordinate_dofs.begin(), i * gdim));
93 }
94
95 // Push forward coordinates (X -> x)
96 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
97 for (std::size_t p = 0; p < Xshape[0]; ++p)
98 {
99 for (std::size_t j = 0; j < gdim; ++j)
100 {
101 T acc = 0;
102 for (std::size_t k = 0; k < num_dofs_g; ++k)
103 acc += phi(p, k) * coordinate_dofs[k * gdim + j];
104 x[j * (cells.size() * Xshape[0]) + offset * Xshape[0] + p] = acc;
105 }
106 }
107 }
108
109 return x;
110}
111
128template <dolfinx::scalar T, std::floating_point U>
129void interpolate(Function<T, U>& u, std::span<const T> f,
130 std::array<std::size_t, 2> fshape,
131 mesh::CellRange auto&& cells);
132
133namespace impl
134{
136template <typename T, std::size_t D>
137using mdspan_t = md::mdspan<T, md::dextents<std::size_t, D>>;
138
158template <dolfinx::scalar T>
159void scatter_values(MPI_Comm comm, std::span<const std::int32_t> src_ranks,
160 std::span<const std::int32_t> dest_ranks,
161 mdspan_t<const T, 2> send_values, std::span<T> recv_values)
162{
163 const std::size_t block_size = send_values.extent(1);
164 assert(src_ranks.size() * block_size == send_values.size());
165 assert(recv_values.size() == dest_ranks.size() * block_size);
166
167 // Build unique set of the sorted src_ranks
168 std::vector<std::int32_t> out_ranks(src_ranks.size());
169 out_ranks.assign(src_ranks.begin(), src_ranks.end());
170 out_ranks.erase(std::ranges::unique(out_ranks).begin(), out_ranks.end());
171 out_ranks.reserve(out_ranks.size() + 1);
172
173 // Remove negative entries from dest_ranks
174 std::vector<std::int32_t> in_ranks;
175 in_ranks.reserve(dest_ranks.size());
176 std::copy_if(dest_ranks.begin(), dest_ranks.end(),
177 std::back_inserter(in_ranks),
178 [](auto rank) { return rank >= 0; });
179
180 // Create unique set of sorted in-ranks
181 std::ranges::sort(in_ranks);
182 in_ranks.erase(std::ranges::unique(in_ranks).begin(), in_ranks.end());
183 in_ranks.reserve(in_ranks.size() + 1);
184
185 // Create neighborhood communicator
186 MPI_Comm reverse_comm;
187 MPI_Dist_graph_create_adjacent(
188 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
189 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL, false, &reverse_comm);
190
191 std::vector<std::int32_t> comm_to_output;
192 std::vector<std::int32_t> recv_sizes(in_ranks.size());
193 recv_sizes.reserve(1);
194 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
195 {
196 // Build map from parent to neighborhood communicator ranks
197 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
198 rank_to_neighbor.reserve(in_ranks.size());
199 for (std::size_t i = 0; i < in_ranks.size(); i++)
200 rank_to_neighbor.push_back({in_ranks[i], i});
201 std::ranges::sort(rank_to_neighbor);
202
203 // Compute receive sizes
204 std::ranges::for_each(
205 dest_ranks,
206 [&rank_to_neighbor, &recv_sizes, block_size](auto rank)
207 {
208 if (rank >= 0)
209 {
210 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
211 std::ranges::less(),
212 [](auto e) { return e.first; });
213 assert(it != rank_to_neighbor.end() and it->first == rank);
214 recv_sizes[it->second] += block_size;
215 }
216 });
217
218 // Compute receiving offsets
219 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
220 std::next(recv_offsets.begin(), 1));
221
222 // Compute map from receiving values to position in recv_values
223 comm_to_output.resize(recv_offsets.back() / block_size);
224 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
225 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
226 {
227 if (const std::int32_t rank = dest_ranks[i]; rank >= 0)
228 {
229 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
230 std::ranges::less(),
231 [](auto e) { return e.first; });
232 assert(it != rank_to_neighbor.end() and it->first == rank);
233 int insert_pos = recv_offsets[it->second] + recv_counter[it->second];
234 comm_to_output[insert_pos / block_size] = i * block_size;
235 recv_counter[it->second] += block_size;
236 }
237 }
238 }
239
240 std::vector<std::int32_t> send_sizes(out_ranks.size());
241 send_sizes.reserve(1);
242 {
243 // Compute map from parent MPI rank to neighbor rank for outgoing
244 // data. `out_ranks` is sorted, so rank_to_neighbor will be sorted
245 // too.
246 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
247 rank_to_neighbor.reserve(out_ranks.size());
248 for (std::size_t i = 0; i < out_ranks.size(); i++)
249 rank_to_neighbor.push_back({out_ranks[i], i});
250
251 // Compute send sizes. As `src_ranks` is sorted, we can move 'start'
252 // in search forward.
253 auto start = rank_to_neighbor.begin();
254 std::ranges::for_each(
255 src_ranks,
256 [&rank_to_neighbor, &send_sizes, block_size, &start](auto rank)
257 {
258 auto it = std::ranges::lower_bound(start, rank_to_neighbor.end(),
259 rank, std::ranges::less(),
260 [](auto e) { return e.first; });
261 assert(it != rank_to_neighbor.end() and it->first == rank);
262 send_sizes[it->second] += block_size;
263 start = it;
264 });
265 }
266
267 // Compute sending offsets
268 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
269 std::partial_sum(send_sizes.begin(), send_sizes.end(),
270 std::next(send_offsets.begin(), 1));
271
272 // Send values to dest ranks
273 std::vector<T> values(recv_offsets.back());
274 values.reserve(1);
275 MPI_Neighbor_alltoallv(send_values.data_handle(), send_sizes.data(),
276 send_offsets.data(), dolfinx::MPI::mpi_t<T>,
277 values.data(), recv_sizes.data(), recv_offsets.data(),
278 dolfinx::MPI::mpi_t<T>, reverse_comm);
279 MPI_Comm_free(&reverse_comm);
280
281 // Insert values received from neighborhood communicator in output
282 // span
283 std::ranges::fill(recv_values, T{0});
284 for (std::size_t i = 0; i < comm_to_output.size(); i++)
285 {
286 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
287 auto vals_from = std::next(values.begin(), i * block_size);
288 std::copy_n(vals_from, block_size, vals);
289 }
290};
291
300template <dolfinx::MDSpanRank2 U, dolfinx::MDSpanRank2 V, dolfinx::scalar T>
301void interpolation_apply(U&& Pi, V&& data, std::span<T> coeffs, int bs)
302{
303 // Geometry (real) scalar type, taken from the interpolation operator Pi
304 // rather than scalar_value_t<T> so it is independent of the value scalar T.
305 using X = typename std::remove_cvref_t<U>::value_type;
306
307 // Compute coefficients = Pi * x (matrix-vector multiply)
308 if (bs == 1)
309 {
310 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
311 for (std::size_t i = 0; i < Pi.extent(0); ++i)
312 {
313 coeffs[i] = 0.0;
314 for (std::size_t k = 0; k < data.extent(1); ++k)
315 for (std::size_t j = 0; j < data.extent(0); ++j)
316 coeffs[i]
317 += static_cast<X>(Pi(i, k * data.extent(0) + j)) * data(j, k);
318 }
319 }
320 else
321 {
322 assert(data.extent(0) == Pi.extent(1));
323 assert(static_cast<int>(data.extent(1)) == bs);
324 std::size_t cols = Pi.extent(1);
325 for (int k = 0; k < bs; ++k)
326 {
327 for (std::size_t i = 0; i < Pi.extent(0); ++i)
328 {
329 T acc = 0;
330 for (std::size_t j = 0; j < cols; ++j)
331 acc += static_cast<X>(Pi(i, j)) * data(j, k);
332 coeffs[bs * i + k] = acc;
333 }
334 }
335 }
336}
337
357template <dolfinx::scalar T, std::floating_point U>
358void interpolate_same_map(Function<T, U>& u1, mesh::CellRange auto&& cells1,
359 const Function<T, U>& u0,
360 mesh::CellRange auto&& cells0)
361{
362 auto V0 = u0.function_space();
363 assert(V0);
364 auto V1 = u1.function_space();
365 assert(V1);
366 auto mesh0 = V0->mesh();
367 assert(mesh0);
368
369 auto mesh1 = V1->mesh();
370 assert(mesh1);
371
372 auto element0 = V0->element();
373 assert(element0);
374 auto element1 = V1->element();
375 assert(element1);
376
377 assert(mesh0->topology()->dim());
378 const int tdim = mesh0->topology()->dim();
379 auto map = mesh0->topology()->index_map(tdim);
380 assert(map);
381 std::span<T> u1_array = u1.x()->array();
382 std::span<const T> u0_array = u0.x()->array();
383
384 std::span<const std::uint32_t> cell_info0;
385 std::span<const std::uint32_t> cell_info1;
386 if (element1->needs_dof_transformations()
387 or element0->needs_dof_transformations())
388 {
389 mesh0->topology_mutable()->create_cell_permutations();
390 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
391 mesh1->topology_mutable()->create_cell_permutations();
392 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
393 }
394
395 // Get dofmaps
396 auto dofmap1 = V1->dofmap();
397 auto dofmap0 = V0->dofmap();
398
399 // Get block sizes and dof transformation operators
400 const int bs1 = dofmap1->bs();
401 const int bs0 = dofmap0->bs();
402 auto apply_dof_transformation = element0->template dof_transformation_fn<T>(
404 auto apply_inverse_dof_transform
405 = element1->template dof_transformation_fn<T>(
407
408 // Create working array
409 std::vector<T> local0(element0->space_dimension());
410 std::vector<T> local1(element1->space_dimension());
411
412 // Create interpolation operator
413 auto [i_m, im_shape] = element1->create_interpolation_operator(*element0);
414
415 // Iterate over mesh and interpolate on each cell
416 using X = U; // geometry (real) type, independent of the value scalar T
417 if (cells0.size() != cells1.size())
418 throw std::invalid_argument("Length of cells0 and cells1 must match.");
419 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
420 cell0_it != cells0.end() and cell1_it != cells1.end();
421 ++cell0_it, ++cell1_it)
422 {
423 // Pack and transform cell dofs to reference ordering
424 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
425 for (std::size_t i = 0; i < dofs0.size(); ++i)
426 for (int k = 0; k < bs0; ++k)
427 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
428
429 if (apply_dof_transformation)
430 apply_dof_transformation(local0, cell_info0, *cell0_it, 1);
431
432 // FIXME: Get compile-time ranges from Basix
433 // Apply interpolation operator
434 std::ranges::fill(local1, 0);
435 for (std::size_t i = 0; i < im_shape[0]; ++i)
436 for (std::size_t j = 0; j < im_shape[1]; ++j)
437 local1[i] += static_cast<X>(i_m[im_shape[1] * i + j]) * local0[j];
438
439 if (apply_inverse_dof_transform)
440 apply_inverse_dof_transform(local1, cell_info1, *cell1_it, 1);
441 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
442 for (std::size_t i = 0; i < dofs1.size(); ++i)
443 for (int k = 0; k < bs1; ++k)
444 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
445 }
446}
447
462template <dolfinx::scalar T, std::floating_point U>
463void interpolate_nonmatching_maps(Function<T, U>& u1,
464 mesh::CellRange auto&& cells1,
465 const Function<T, U>& u0,
466 mesh::CellRange auto&& cells0)
467{
468 // Get mesh
469 auto V0 = u0.function_space();
470 assert(V0);
471 auto mesh0 = V0->mesh();
472 assert(mesh0);
473
474 // Mesh dims
475 const int tdim = mesh0->topology()->dim();
476 const int gdim = mesh0->geometry().dim();
477
478 // Get elements
479 auto V1 = u1.function_space();
480 assert(V1);
481 auto mesh1 = V1->mesh();
482 assert(mesh1);
483 auto element0 = V0->element();
484 assert(element0);
485 auto element1 = V1->element();
486 assert(element1);
487
488 std::span<const std::uint32_t> cell_info0;
489 std::span<const std::uint32_t> cell_info1;
490 if (element1->needs_dof_transformations()
491 or element0->needs_dof_transformations())
492 {
493 mesh0->topology_mutable()->create_cell_permutations();
494 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
495 mesh1->topology_mutable()->create_cell_permutations();
496 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
497 }
498
499 // Get dofmaps
500 auto dofmap0 = V0->dofmap();
501 auto dofmap1 = V1->dofmap();
502
503 const auto [X, Xshape] = element1->interpolation_points();
504
505 // Get block sizes and dof transformation operators
506 const int bs0 = element0->block_size();
507 const int bs1 = element1->block_size();
508 auto apply_dof_transformation0 = element0->template dof_transformation_fn<U>(
510 auto apply_inv_dof_transform1 = element1->template dof_transformation_fn<T>(
512
513 // Get sizes of elements
514 const std::size_t dim0 = element0->space_dimension() / bs0;
515 const std::size_t value_size_ref0 = element0->reference_value_size();
516 // basis0 holds values pushed forward to the physical cell, one block
517 // at a time, so it is sized with the physical (base) value size.
518 const std::size_t value_size0 = V0->element()->physical_base_value_size();
519
520 const CoordinateElement<U>& cmap = mesh0->geometry().cmaps().front();
521 auto x_dofmap = mesh0->geometry().dofmaps().front();
522 std::span<const U> x_g = mesh0->geometry().x();
523
524 // (0) is derivative index, (1) is the point index, (2) is the basis
525 // function index and (3) is the basis function component.
526
527 // Evaluate coordinate map basis at reference interpolation points
528 const std::array<std::size_t, 4> phi_shape
529 = cmap.tabulate_shape(1, Xshape[0]);
530 std::vector<U> phi_b(
531 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
532 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
533 md::dynamic_extent, md::dynamic_extent, 1>>
534 phi(phi_b.data(), phi_shape);
535 cmap.tabulate(1, X, Xshape, phi_b);
536
537 // Evaluate v basis functions at reference interpolation points
538 const auto [_basis_derivatives_reference0, b0shape]
539 = element0->tabulate(X, Xshape, 0);
540 md::mdspan<const U, std::extents<std::size_t, 1, md::dynamic_extent,
541 md::dynamic_extent, md::dynamic_extent>>
542 basis_derivatives_reference0(_basis_derivatives_reference0.data(),
543 b0shape);
544
545 // Create working arrays
546 std::vector<T> local1(element1->space_dimension());
547 std::vector<T> coeffs0(element0->space_dimension());
548
549 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
550 md::mdspan<U, std::dextents<std::size_t, 3>> basis0(
551 basis0_b.data(), Xshape[0], dim0, value_size0);
552
553 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
554 md::mdspan<U, std::dextents<std::size_t, 3>> basis_reference0(
555 basis_reference0_b.data(), Xshape[0], dim0, value_size_ref0);
556
557 // values0 holds physical values (the value shapes of the two elements
558 // have been checked to be equal); mapped_values0 holds them pulled
559 // back to the reference cell of element 1, where each of its bs1
560 // blocks has reference_value_size components.
561 const std::size_t value_size1 = V1->element()->value_size();
562 const std::size_t value_size_ref1
563 = element1->reference_value_size() * static_cast<std::size_t>(bs1);
564 std::vector<T> values0_b(Xshape[0] * 1 * value_size1);
565 md::mdspan<
566 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
567 values0(values0_b.data(), Xshape[0], 1, value_size1);
568
569 std::vector<T> mapped_values_b(Xshape[0] * 1 * value_size_ref1);
570 md::mdspan<
571 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
572 mapped_values0(mapped_values_b.data(), Xshape[0], 1, value_size_ref1);
573
574 const std::size_t num_dofs_g = cmap.dim();
575 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
576 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
577 num_dofs_g, gdim);
578
579 std::vector<U> J_b(Xshape[0] * gdim * tdim);
580 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
581 tdim);
582 std::vector<U> K_b(Xshape[0] * tdim * gdim);
583 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
584 gdim);
585 std::vector<U> detJ(Xshape[0]);
586 std::vector<U> det_scratch(2 * gdim * tdim);
587
588 // Get interpolation operator
589 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
590 impl::mdspan_t<const U, 2> Pi_1(_Pi_1.data(), pi_shape);
591
592 using u_t = md::mdspan<U, std::dextents<std::size_t, 2>>;
593 using U_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
594 using J_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
595 using K_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
596 auto push_forward_fn0
597 = element0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
598
599 using v_t = md::mdspan<const T, std::dextents<std::size_t, 2>>;
600 using V_t = decltype(md::submdspan(mapped_values0, 0, md::full_extent,
601 md::full_extent));
602 auto pull_back_fn1
603 = element1->basix_element().template map_fn<V_t, v_t, K_t, J_t>();
604
605 // Iterate over mesh and interpolate on each cell
606 std::span<const T> array0 = u0.x()->array();
607 std::span<T> array1 = u1.x()->array();
608 if (cells0.size() != cells1.size())
609 throw std::invalid_argument("Length of cells0 and cells1 must match.");
610 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
611 cell0_it != cells0.end() and cell1_it != cells1.end();
612 ++cell0_it, ++cell1_it)
613 {
614 // Get cell geometry (coordinate dofs)
615 auto x_dofs = md::submdspan(x_dofmap, *cell0_it, md::full_extent);
616 for (std::size_t i = 0; i < num_dofs_g; ++i)
617 {
618 const int pos = 3 * x_dofs[i];
619 for (int j = 0; j < gdim; ++j)
620 coord_dofs(i, j) = x_g[pos + j];
621 }
622
623 // Compute Jacobians and reference points for current cell
624 std::ranges::fill(J_b, 0);
625 for (std::size_t p = 0; p < Xshape[0]; ++p)
626 {
627 auto dphi
628 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
629 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
630 cmap.compute_jacobian(dphi, coord_dofs, _J);
631 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
632 cmap.compute_jacobian_inverse(_J, _K);
633 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
634 }
635
636 // Copy evaluated basis on reference, apply DOF transformations, and
637 // push forward to physical element
638 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
639 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
640 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
641 basis_reference0(k0, k1, k2)
642 = basis_derivatives_reference0(0, k0, k1, k2);
643
644 if (apply_dof_transformation0)
645 {
646 for (std::size_t p = 0; p < Xshape[0]; ++p)
647 {
648 apply_dof_transformation0(
649 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
650 dim0 * value_size_ref0),
651 cell_info0, *cell0_it, value_size_ref0);
652 }
653 }
654
655 for (std::size_t i = 0; i < basis0.extent(0); ++i)
656 {
657 auto _u = md::submdspan(basis0, i, md::full_extent, md::full_extent);
658 auto _U = md::submdspan(basis_reference0, i, md::full_extent,
659 md::full_extent);
660 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
661 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
662 push_forward_fn0(_u, _U, _J, detJ[i], _K);
663 }
664
665 // Copy expansion coefficients for v into local array
666 const int dof_bs0 = dofmap0->bs();
667 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
668 for (std::size_t i = 0; i < dofs0.size(); ++i)
669 for (int k = 0; k < dof_bs0; ++k)
670 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
671
672 // Evaluate v at the interpolation points (physical space values)
673 using X = U; // geometry (real) type, independent of the value scalar T
674 for (std::size_t p = 0; p < Xshape[0]; ++p)
675 {
676 for (int k = 0; k < bs0; ++k)
677 {
678 for (std::size_t j = 0; j < value_size0; ++j)
679 {
680 T acc = 0;
681 for (std::size_t i = 0; i < dim0; ++i)
682 acc += coeffs0[bs0 * i + k] * static_cast<X>(basis0(p, i, j));
683 values0(p, 0, j * bs0 + k) = acc;
684 }
685 }
686 }
687
688 // Pull back the physical values to the u reference
689 for (std::size_t i = 0; i < values0.extent(0); ++i)
690 {
691 auto _u = md::submdspan(values0, i, md::full_extent, md::full_extent);
692 auto _U
693 = md::submdspan(mapped_values0, i, md::full_extent, md::full_extent);
694 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
695 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
696 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
697 }
698
699 auto values
700 = md::submdspan(mapped_values0, md::full_extent, 0, md::full_extent);
701 interpolation_apply(Pi_1, values, std::span(local1), bs1);
702 if (apply_inv_dof_transform1)
703 apply_inv_dof_transform1(local1, cell_info1, *cell1_it, 1);
704
705 // Copy local coefficients to the correct position in u dof array
706 const int dof_bs1 = dofmap1->bs();
707 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
708 for (std::size_t i = 0; i < dofs1.size(); ++i)
709 for (int k = 0; k < dof_bs1; ++k)
710 array1[dof_bs1 * dofs1[i] + k] = local1[dof_bs1 * i + k];
711 }
712}
713
725template <dolfinx::scalar T, std::floating_point U>
726void point_evaluation(const FiniteElement<U>& element, bool symmetric,
727 const DofMap& dofmap, mesh::CellRange auto&& cells,
728 std::span<const std::uint32_t> cell_info,
729 std::span<const T> f, std::array<std::size_t, 2> fshape,
730 std::span<T> coeffs)
731{
732 // Point evaluation element *and* the geometric map is the identity,
733 // e.g. not Piola mapped
734
735 const int element_bs = element.block_size();
736 const int num_scalar_dofs = element.space_dimension() / element_bs;
737 const int dofmap_bs = dofmap.bs();
738
739 auto apply_inv_transpose_dof_transformation
740 = element.template dof_transformation_fn<T>(
742 std::vector<T> coeffs_b(num_scalar_dofs);
743
744 // Skip the div/mod below when block sizes match (the common case)
745 const bool same_bs = (dofmap_bs == element_bs);
746
747 if (symmetric)
748 {
749 std::size_t matrix_size = 0;
750 while (matrix_size * matrix_size < fshape[0])
751 ++matrix_size;
752
753 // Loop over cells
754 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
755 {
756 // The entries of a symmetric matrix are numbered (for an
757 // example 4x4 element):
758 // 0 * * *
759 // 1 2 * *
760 // 3 4 5 *
761 // 6 7 8 9
762 // The loop extracts these elements. In this loop, row is the
763 // row of this matrix, and (k - rowstart) is the column
764 std::size_t row = 0;
765 std::size_t rowstart = 0;
766 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
767 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
768 for (int k = 0; k < element_bs; ++k)
769 {
770 if (k - rowstart > row)
771 {
772 ++row;
773 rowstart = k;
774 }
775
776 // num_scalar_dofs is the number of interpolation points per
777 // cell in this case (interpolation matrix is identity)
778 std::copy_n(
779 std::next(f.begin(), (row * matrix_size + k - rowstart) * fshape[1]
780 + offset * num_scalar_dofs),
781 num_scalar_dofs, coeffs_b.data());
782 if (apply_inv_transpose_dof_transformation)
783 {
784 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
785 1);
786 }
787 if (same_bs)
788 {
789 for (int i = 0; i < num_scalar_dofs; ++i)
790 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
791 }
792 else
793 {
794 for (int i = 0; i < num_scalar_dofs; ++i)
795 {
796 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
797 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
798 }
799 }
800 }
801 }
802 }
803 else
804 {
805 // Loop over cells
806 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
807 {
808 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
809 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
810 for (int k = 0; k < element_bs; ++k)
811 {
812 // num_scalar_dofs is the number of interpolation points per
813 // cell in this case (interpolation matrix is identity)
814 std::copy_n(
815 std::next(f.begin(), k * fshape[1] + offset * num_scalar_dofs),
816 num_scalar_dofs, coeffs_b.data());
817 if (apply_inv_transpose_dof_transformation)
818 {
819 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
820 1);
821 }
822 if (same_bs)
823 {
824 for (int i = 0; i < num_scalar_dofs; ++i)
825 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
826 }
827 else
828 {
829 for (int i = 0; i < num_scalar_dofs; ++i)
830 {
831 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
832 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
833 }
834 }
835 }
836 }
837 }
838}
839
851template <dolfinx::scalar T, std::floating_point U>
852void identity_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
853 const DofMap& dofmap,
854 mesh::CellRange auto&& cells,
855 std::span<const std::uint32_t> cell_info,
856 std::span<const T> f,
857 std::array<std::size_t, 2> fshape,
858 std::span<T> coeffs)
859{
860 // Not a point evaluation, but the geometric map is the identity,
861 // e.g. not Piola mapped
862
863 if (symmetric)
864 throw std::invalid_argument(
865 "Interpolation into this element not supported.");
866
867 const int element_bs = element.block_size();
868 const int num_scalar_dofs = element.space_dimension() / element_bs;
869 const int dofmap_bs = dofmap.bs();
870
871 // Identity map, so the physical and reference value sizes coincide.
872 const int element_vs = element.reference_value_size();
873 assert(element_vs == element.physical_base_value_size());
874 if (element_vs > 1 and element_bs > 1)
875 throw std::runtime_error("Interpolation into this element not supported.");
876
877 // Get interpolation operator
878 const auto [_Pi, pi_shape] = element.interpolation_operator();
879 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
880 const std::size_t num_interp_points = Pi.extent(1);
881 assert(static_cast<int>(Pi.extent(0)) == num_scalar_dofs);
882
883 auto apply_inv_transpose_dof_transformation
884 = element.template dof_transformation_fn<T>(
886
887 // Skip the div/mod below when block sizes match (the common case)
888 const bool same_bs = (dofmap_bs == element_bs);
889
890 // Loop over cells
891 std::vector<T> ref_data_b(num_interp_points);
892 md::mdspan<T, md::extents<std::size_t, md::dynamic_extent, 1>> ref_data(
893 ref_data_b.data(), num_interp_points, 1);
894 std::vector<T> coeffs_b(num_scalar_dofs);
895 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
896 {
897 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
898 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
899 for (int k = 0; k < element_bs; ++k)
900 {
901 for (int i = 0; i < element_vs; ++i)
902 {
903 std::copy_n(
904 std::next(f.begin(), (i + k) * fshape[1]
905 + offset * num_interp_points / element_vs),
906 num_interp_points / element_vs,
907 std::next(ref_data_b.begin(), i * num_interp_points / element_vs));
908 }
909
910 impl::interpolation_apply(Pi, ref_data, std::span(coeffs_b), 1);
911 if (apply_inv_transpose_dof_transformation)
912 {
913 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
914 1);
915 }
916 if (same_bs)
917 {
918 for (int i = 0; i < num_scalar_dofs; ++i)
919 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
920 }
921 else
922 {
923 for (int i = 0; i < num_scalar_dofs; ++i)
924 {
925 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
926 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
927 }
928 }
929 }
930 }
931}
932
945template <dolfinx::scalar T, std::floating_point U>
946void piola_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
947 const DofMap& dofmap, mesh::CellRange auto&& cells,
948 std::span<const std::uint32_t> cell_info,
949 std::span<const T> f,
950 std::array<std::size_t, 2> fshape,
951 const mesh::Mesh<U>& mesh, std::span<T> coeffs)
952{
953 if (symmetric)
954 throw std::invalid_argument(
955 "Interpolation into this element not supported.");
956
957 const int gdim = mesh.geometry().dim();
958 assert(mesh.topology());
959 const int tdim = mesh.topology()->dim();
960
961 const int element_bs = element.block_size();
962 const int num_scalar_dofs = element.space_dimension() / element_bs;
963 // f holds physical values, one block at a time.
964 const int value_size = element.physical_base_value_size();
965 const int dofmap_bs = dofmap.bs();
966
967 // Skip the div/mod below when block sizes match (the common case)
968 const bool same_bs = (dofmap_bs == element_bs);
969
970 md::mdspan<const T, md::dextents<std::size_t, 2>> _f(f.data(), fshape);
971
972 // Get the interpolation points on the reference cells
973 const auto [X, Xshape] = element.interpolation_points();
974 if (X.empty())
975 {
976 throw std::invalid_argument(
977 "Interpolation into this space is not yet supported.");
978 }
979
980 if (_f.extent(1) != cells.size() * Xshape[0])
981 throw std::invalid_argument("Interpolation data has the wrong shape.");
982
983 // Get coordinate map
984 const CoordinateElement<U>& cmap = mesh.geometry().cmaps().front();
985
986 // Get geometry data
987 auto x_dofmap = mesh.geometry().dofmaps().front();
988 const int num_dofs_g = cmap.dim();
989 std::span<const U> x_g = mesh.geometry().x();
990
991 // Create data structures for Jacobian info
992 std::vector<U> J_b(Xshape[0] * gdim * tdim);
993 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
994 tdim);
995 std::vector<U> K_b(Xshape[0] * tdim * gdim);
996 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
997 gdim);
998 std::vector<U> detJ(Xshape[0]);
999 std::vector<U> det_scratch(2 * gdim * tdim);
1000
1001 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
1002 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
1003 num_dofs_g, gdim);
1004 const std::size_t value_size_ref = element.reference_value_size();
1005 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size_ref);
1006 md::mdspan<
1007 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
1008 ref_data(ref_data_b.data(), Xshape[0], 1, value_size_ref);
1009
1010 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
1011 md::mdspan<
1012 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
1013 _vals(_vals_b.data(), Xshape[0], 1, value_size);
1014
1015 // Tabulate 1st derivative of shape functions at interpolation
1016 // coords
1017 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
1018 std::vector<U> phi_b(
1019 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
1020 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
1021 md::dynamic_extent, md::dynamic_extent, 1>>
1022 phi(phi_b.data(), phi_shape);
1023 cmap.tabulate(1, X, Xshape, phi_b);
1024 auto dphi = md::submdspan(phi, std::pair(1, tdim + 1), md::full_extent,
1025 md::full_extent, 0);
1026
1027 std::function<void(std::span<T>, std::span<const std::uint32_t>, std::int32_t,
1028 int)>
1029 apply_inv_trans_dof_transformation
1030 = element.template dof_transformation_fn<T>(
1032
1033 // Get interpolation operator
1034 const auto [_Pi, pi_shape] = element.interpolation_operator();
1035 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
1036
1037 using u_t = md::mdspan<const T, md::dextents<std::size_t, 2>>;
1038 using U_t
1039 = decltype(md::submdspan(ref_data, 0, md::full_extent, md::full_extent));
1040 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1041 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1042 auto pull_back_fn
1043 = element.basix_element().template map_fn<U_t, u_t, J_t, K_t>();
1044
1045 std::vector<T> coeffs_b(num_scalar_dofs);
1046 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
1047 {
1048 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
1049 for (int i = 0; i < num_dofs_g; ++i)
1050 {
1051 const int pos = 3 * x_dofs[i];
1052 for (int j = 0; j < gdim; ++j)
1053 coord_dofs(i, j) = x_g[pos + j];
1054 }
1055
1056 // Compute J, detJ and K
1057 std::ranges::fill(J_b, 0);
1058 for (std::size_t p = 0; p < Xshape[0]; ++p)
1059 {
1060 auto _dphi = md::submdspan(dphi, md::full_extent, p, md::full_extent);
1061 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
1062 cmap.compute_jacobian(_dphi, coord_dofs, _J);
1063 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
1064 cmap.compute_jacobian_inverse(_J, _K);
1065 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
1066 }
1067
1068 const std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
1069 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
1070 for (int k = 0; k < element_bs; ++k)
1071 {
1072 // Extract computed expression values for element block k
1073 for (int m = 0; m < value_size; ++m)
1074 {
1075 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
1076 {
1077 _vals(k0, 0, m)
1078 = f[fshape[1] * (k * value_size + m) + offset * Xshape[0] + k0];
1079 }
1080 }
1081
1082 // Get element degrees of freedom for block
1083 for (std::size_t i = 0; i < Xshape[0]; ++i)
1084 {
1085 auto _u = md::submdspan(_vals, i, md::full_extent, md::full_extent);
1086 auto _U = md::submdspan(ref_data, i, md::full_extent, md::full_extent);
1087 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
1088 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
1089 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
1090 }
1091
1092 auto ref = md::submdspan(ref_data, md::full_extent, 0, md::full_extent);
1093 impl::interpolation_apply(Pi, ref, std::span(coeffs_b), element_bs);
1094 if (apply_inv_trans_dof_transformation)
1095 apply_inv_trans_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
1096
1097 // Copy interpolation dofs into coefficient vector
1098 assert(coeffs_b.size() == static_cast<std::size_t>(num_scalar_dofs));
1099 if (same_bs)
1100 {
1101 for (int i = 0; i < num_scalar_dofs; ++i)
1102 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
1103 }
1104 else
1105 {
1106 for (int i = 0; i < num_scalar_dofs; ++i)
1107 {
1108 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
1109 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
1110 }
1111 }
1112 }
1113 }
1114}
1115
1116//----------------------------------------------------------------------------
1117} // namespace impl
1118
1144template <std::floating_point T>
1146 const mesh::Geometry<T>& geometry0, const FiniteElement<T>& element0,
1147 const mesh::Mesh<T>& mesh1, mesh::CellRange auto&& cells, T padding,
1148 bool allow_extrapolation = true)
1149{
1150 // Collect all the points at which values are needed to define the
1151 // interpolating function
1152 std::vector<T> coords = interpolation_coords(element0, geometry0, cells);
1153
1154 // Transpose interpolation coords
1155 std::vector<T> x(coords.size());
1156 std::size_t num_points = coords.size() / 3;
1157 for (std::size_t i = 0; i < num_points; ++i)
1158 for (std::size_t j = 0; j < 3; ++j)
1159 x[3 * i + j] = coords[i + j * num_points];
1160
1161 // Determine ownership of each point
1162 return geometry::determine_point_ownership<T>(mesh1, x, padding, std::nullopt,
1163 allow_extrapolation);
1164}
1165
1166template <dolfinx::scalar T, std::floating_point U>
1167void interpolate(Function<T, U>& u, std::span<const T> f,
1168 std::array<std::size_t, 2> fshape,
1169 mesh::CellRange auto&& cells)
1170{
1171 // TODO: Index for mixed-topology, zero for now
1172 const int index = 0;
1173 auto element = u.function_space()->elements(index);
1174 assert(element);
1175 const int element_bs = element->block_size();
1176 if (int num_sub = element->num_sub_elements();
1177 num_sub > 0 and num_sub != element_bs)
1178 {
1179 throw std::invalid_argument("Cannot directly interpolate a mixed space. "
1180 "Interpolate into subspaces.");
1181 }
1182
1183 // Get mesh
1184 assert(u.function_space());
1185 auto mesh = u.function_space()->mesh();
1186 assert(mesh);
1187
1188 if (fshape[0]
1189 != (std::size_t)u.function_space()->elements(index)->value_size()
1190 or f.size() != fshape[0] * fshape[1])
1191 {
1192 throw std::invalid_argument("Interpolation data has the wrong shape/size.");
1193 }
1194
1195 spdlog::debug("Check for dof transformation");
1196 std::span<const std::uint32_t> cell_info;
1197 if (element->needs_dof_transformations())
1198 {
1199 mesh->topology_mutable()->create_cell_permutations();
1200 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
1201 }
1202
1203 // Get dofmap
1204 spdlog::debug("Interpolate: get dofmap");
1205 const auto dofmap = u.function_space()->dofmaps().at(index);
1206 assert(dofmap);
1207
1208 // Result will be stored to coeffs
1209 std::span<T> coeffs = u.x()->array();
1210
1211 if (bool symmetric = u.function_space()->symmetric();
1212 element->map_ident() and element->interpolation_ident())
1213 {
1214 // This assumes that any element with an identity interpolation
1215 // matrix is a point evaluation
1216 spdlog::debug("Interpolate: point evaluation");
1217 impl::point_evaluation(*element, symmetric, *dofmap, cells, cell_info, f,
1218 fshape, coeffs);
1219 }
1220 else if (element->map_ident())
1221 {
1222 spdlog::debug("Interpolate: identity-mapped evaluation");
1223 impl::identity_mapped_evaluation(*element, symmetric, *dofmap, cells,
1224 cell_info, f, fshape, coeffs);
1225 }
1226 else
1227 {
1228 spdlog::debug("Interpolate: Piola-mapped evaluation");
1229 impl::piola_mapped_evaluation(*element, symmetric, *dofmap, cells,
1230 cell_info, f, fshape, *mesh, coeffs);
1231 }
1232}
1233
1250template <dolfinx::scalar T, std::floating_point U>
1252 mesh::CellRange auto&& cells, double tol, int maxit,
1253 const geometry::PointOwnershipData<U>& interpolation_data)
1254{
1255 auto mesh1 = u1.function_space()->mesh();
1256 assert(mesh1);
1257 MPI_Comm comm = mesh1->comm();
1258 {
1259 assert(u0.function_space());
1260 auto mesh0 = u0.function_space()->mesh();
1261 assert(mesh0);
1262 int result;
1263 MPI_Comm_compare(comm, mesh0->comm(), &result);
1264 if (result == MPI_UNEQUAL)
1265 {
1266 throw std::invalid_argument("Interpolation on different meshes is only "
1267 "supported on the same communicator.");
1268 }
1269 }
1270
1271 assert(mesh1->topology());
1272 auto cell_map = mesh1->topology()->index_map(mesh1->topology()->dim());
1273 assert(cell_map);
1274 auto element1 = u1.function_space()->element();
1275 assert(element1);
1276 const std::size_t value_size = element1->value_size();
1277
1278 const std::vector<int>& dest_ranks = interpolation_data.src_owner;
1279 const std::vector<int>& src_ranks = interpolation_data.dest_owners;
1280 const std::vector<U>& recv_points = interpolation_data.dest_points;
1281 const std::vector<std::int32_t>& evaluation_cells
1282 = interpolation_data.dest_cells;
1283
1284 // Evaluate the interpolating function where possible
1285 std::vector<T> send_values(recv_points.size() / 3 * value_size);
1286 u0.eval(recv_points, {recv_points.size() / 3, (std::size_t)3},
1287 evaluation_cells, send_values, {recv_points.size() / 3, value_size},
1288 tol, maxit);
1289
1290 // Send values back to owning process
1291 std::vector<T> values_b(dest_ranks.size() * value_size);
1292 md::mdspan<const T, md::dextents<std::size_t, 2>> _send_values(
1293 send_values.data(), src_ranks.size(), value_size);
1294 impl::scatter_values(comm, src_ranks, dest_ranks, _send_values,
1295 std::span(values_b));
1296
1297 // Transpose received data
1298 md::mdspan<const T, md::dextents<std::size_t, 2>> values(
1299 values_b.data(), dest_ranks.size(), value_size);
1300 std::vector<T> valuesT_b(value_size * dest_ranks.size());
1301 md::mdspan<T, md::dextents<std::size_t, 2>> valuesT(
1302 valuesT_b.data(), value_size, dest_ranks.size());
1303 for (std::size_t i = 0; i < values.extent(0); ++i)
1304 for (std::size_t j = 0; j < values.extent(1); ++j)
1305 valuesT(j, i) = values(i, j);
1306
1307 // Call local interpolation operator
1308 fem::interpolate<T>(u1, valuesT_b, {valuesT.extent(0), valuesT.extent(1)},
1309 cells);
1310}
1311
1328template <dolfinx::scalar T, std::floating_point U>
1330 const Function<T, U>& u0, mesh::CellRange auto&& cells0)
1331{
1332 if (cells0.size() != cells1.size())
1333 throw std::invalid_argument("Length of cell lists do not match.");
1334
1335 auto V1 = u1.function_space();
1336 assert(V1);
1337 auto V0 = u0.function_space();
1338 assert(V0);
1339
1340 // Get elements and check value shape
1341 auto e0 = V0->element();
1342 assert(e0);
1343 auto e1 = V1->element();
1344 assert(e1);
1345 if (!std::ranges::equal(e0->value_shape(), e1->value_shape()))
1346 {
1347 throw std::invalid_argument(
1348 "Interpolation: elements have different value dimensions");
1349 }
1350
1351 if (V1->mesh() == V0->mesh() and (e1 == e0 or *e1 == *e0))
1352 {
1353 // Same element and same mesh
1354 if (e1->block_size() != e0->block_size())
1355 throw std::invalid_argument("Mismatch in element block size.");
1356
1357 // Get dofmaps
1358 std::shared_ptr<const DofMap> dofmap0 = V0->dofmap();
1359 assert(dofmap0);
1360 std::shared_ptr<const DofMap> dofmap1 = V1->dofmap();
1361 assert(dofmap1);
1362
1363 // Iterate over mesh and interpolate on each cell
1364 const int bs0 = dofmap0->bs();
1365 const int bs1 = dofmap1->bs();
1366 std::span<T> u1_array = u1.x()->array();
1367 std::span<const T> u0_array = u0.x()->array();
1368 assert(cells0.size() == cells1.size());
1369 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
1370 cell0_it != cells0.end() and cell1_it != cells1.end();
1371 ++cell0_it, ++cell1_it)
1372
1373 {
1374 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
1375 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
1376 assert(bs0 * dofs0.size() == bs1 * dofs1.size());
1377 for (std::size_t i = 0; i < dofs0.size(); ++i)
1378 {
1379 for (int k = 0; k < bs0; ++k)
1380 {
1381 int index = bs0 * i + k;
1382 std::div_t dv1 = std::div(index, bs1);
1383 u1_array[bs1 * dofs1[dv1.quot] + dv1.rem]
1384 = u0_array[bs0 * dofs0[i] + k];
1385 }
1386 }
1387 }
1388 }
1389 else if (e1->map_type() == e0->map_type())
1390 {
1391 // Different elements, same basis function map type
1392 impl::interpolate_same_map(u1, cells1, u0, cells0);
1393 }
1394 else
1395 {
1396 // Different elements with different maps for basis functions
1397 impl::interpolate_nonmatching_maps(u1, cells1, u0, cells0);
1398 }
1399}
1400
1410template <dolfinx::scalar T, std::floating_point U>
1412 std::ranges::input_range auto&& cells)
1413{
1414 assert(u1.function_space());
1415 assert(u0.function_space());
1416 if (u1.function_space()->mesh() == u0.function_space()->mesh())
1417 interpolate<T, U>(u1, cells, u0, cells);
1418 else
1419 throw std::invalid_argument("Meshes do no match.");
1420}
1421
1431template <dolfinx::scalar T, std::floating_point U>
1433{
1434 assert(u1.function_space());
1435 assert(u0.function_space());
1436 if (auto V1 = u1.function_space(); V1 == u0.function_space())
1437 std::ranges::copy(u0.x()->array(), u1.x()->array().begin());
1438 else
1439 {
1440 auto mesh = V1->mesh();
1441 assert(mesh);
1442 assert(mesh->topology());
1443 auto map = mesh->topology()->index_map(mesh->topology()->dim());
1444 assert(map);
1445 std::int32_t num_cells = map->size_local() + map->num_ghosts();
1446 interpolate<T, U>(u1, u0, std::ranges::views::iota(0, num_cells));
1447 }
1448}
1449} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:39
void tabulate(int nd, std::span< const T > X, std::array< std::size_t, 2 > shape, std::span< T > basis) const
Evaluate basis values and derivatives at set of points.
Definition CoordinateElement.cpp:60
std::array< std::size_t, 4 > tabulate_shape(std::size_t nd, std::size_t num_points) const
Shape of array to fill when calling tabulate.
Definition CoordinateElement.cpp:53
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:223
Model of a finite element.
Definition FiniteElement.h:199
std::pair< std::vector< geometry_type >, std::array< std::size_t, 2 > > interpolation_points() const
Points on the reference cell at which an expression needs to be evaluated in order to interpolate the...
Definition FiniteElement.cpp:537
mesh::CellType cell_type() const noexcept
Cell shape that the element is defined on.
Definition FiniteElement.cpp:333
Definition Function.h:44
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:145
void eval(std::span< const geometry_type > x, std::array< std::size_t, 2 > xshape, mesh::CellRange auto &&cells, std::span< value_type > u, std::array< std::size_t, 2 > ushape, double tol, int maxit) const
Evaluate the Function at points.
Definition Function.h:318
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:151
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:39
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:25
Requirement on range of cell indices.
Definition Topology.h:32
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:326
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:73
void cells(la::SparsityPattern &pattern, const std::pair< R0, R1 > &cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.h:37
Finite element method functionality.
Definition assemble_expression_impl.h:22
geometry::PointOwnershipData< T > create_interpolation_data(const mesh::Geometry< T > &geometry0, const FiniteElement< T > &element0, const mesh::Mesh< T > &mesh1, mesh::CellRange auto &&cells, T padding, bool allow_extrapolation=true)
Generate data needed to interpolate finite element fem::Function's across different meshes.
Definition interpolate.h:1145
@ transpose
Transpose.
Definition FiniteElement.h:32
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:34
@ standard
Standard.
Definition FiniteElement.h:31
std::vector< T > interpolation_coords(const fem::FiniteElement< T > &element, const mesh::Geometry< T > &geometry, mesh::CellRange auto &&cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition interpolate.h:44
void interpolate(Function< T, U > &u1, mesh::CellRange auto &&cells1, const Expression< T, U > &e0, mesh::CellRange auto &&cells0)
Interpolate an Expression into a Function over a subset of cells.
Definition expression_evaluate.h:165
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:24
PointOwnershipData< T > determine_point_ownership(const mesh::Mesh< T > &mesh, std::span< const T > points, T padding, std::optional< std::span< const std::int32_t > > cells, bool find_closest_cell=true)
Determine, for a set of points, the owning process of the cell (if any) that contains each point.
Definition utils.h:817
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
CellType
Cell type identifier.
Definition cell_types.h:24
Information on the ownership of points distributed across processes.
Definition utils.h:35
std::vector< T > dest_points
Points that are owned by current process.
Definition utils.h:40
std::vector< std::int32_t > dest_cells
Definition utils.h:42
std::vector< int > dest_owners
Ranks that sent dest_points to current process.
Definition utils.h:39
std::vector< int > src_owner
Definition utils.h:36