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 <vector>
26
27namespace dolfinx::fem
28{
29template <dolfinx::scalar T, std::floating_point U>
30class Function;
31
42template <std::floating_point T>
43std::vector<T> interpolation_coords(const fem::FiniteElement<T>& element,
45 mesh::CellRange auto&& cells)
46{
47 // Find CoordinateElement appropriate to element
48 auto cmap_index = [&geometry](mesh::CellType cell_type)
49 {
50 for (std::size_t i = 0; i < geometry.cmaps().size(); ++i)
51 {
52 if (geometry.cmaps().at(i).cell_shape() == cell_type)
53 return i;
54 }
55 throw std::runtime_error("Cannot find CoordinateElement for FiniteElement");
56 };
57 int index = cmap_index(element.cell_type());
58
59 // Get geometry data and the element coordinate map
60 const std::size_t gdim = geometry.dim();
61 auto x_dofmap = geometry.dofmaps().at(index);
62 std::span<const T> x_g = geometry.x();
63
64 const CoordinateElement<T>& cmap = geometry.cmaps().at(index);
65 const std::size_t num_dofs_g = cmap.dim();
66
67 // Get the interpolation points on the reference cells
68 const auto [X, Xshape] = element.interpolation_points();
69
70 // Evaluate coordinate element basis at reference points
71 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(0, Xshape[0]);
72 std::vector<T> phi_b(
73 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
74 md::mdspan<const T, md::extents<std::size_t, 1, md::dynamic_extent,
75 md::dynamic_extent, 1>>
76 phi_full(phi_b.data(), phi_shape);
77 cmap.tabulate(0, X, Xshape, phi_b);
78 auto phi = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
79
80 // Push reference coordinates (X) forward to the physical coordinates
81 // (x) for each cell
82 std::vector<T> coordinate_dofs(num_dofs_g * gdim, 0);
83 std::vector<T> x(3 * (cells.size() * Xshape[0]), 0);
84 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
85 {
86 // Get geometry data for current cell
87 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
88 for (std::size_t i = 0; i < x_dofs.size(); ++i)
89 {
90 std::copy_n(std::next(x_g.begin(), 3 * x_dofs[i]), gdim,
91 std::next(coordinate_dofs.begin(), i * gdim));
92 }
93
94 // Push forward coordinates (X -> x)
95 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
96 for (std::size_t p = 0; p < Xshape[0]; ++p)
97 {
98 for (std::size_t j = 0; j < gdim; ++j)
99 {
100 T acc = 0;
101 for (std::size_t k = 0; k < num_dofs_g; ++k)
102 acc += phi(p, k) * coordinate_dofs[k * gdim + j];
103 x[j * (cells.size() * Xshape[0]) + offset * Xshape[0] + p] = acc;
104 }
105 }
106 }
107
108 return x;
109}
110
127template <dolfinx::scalar T, std::floating_point U>
128void interpolate(Function<T, U>& u, std::span<const T> f,
129 std::array<std::size_t, 2> fshape,
130 mesh::CellRange auto&& cells);
131
132namespace impl
133{
135template <typename T, std::size_t D>
136using mdspan_t = md::mdspan<T, md::dextents<std::size_t, D>>;
137
157template <dolfinx::scalar T>
158void scatter_values(MPI_Comm comm, std::span<const std::int32_t> src_ranks,
159 std::span<const std::int32_t> dest_ranks,
160 mdspan_t<const T, 2> send_values, std::span<T> recv_values)
161{
162 const std::size_t block_size = send_values.extent(1);
163 assert(src_ranks.size() * block_size == send_values.size());
164 assert(recv_values.size() == dest_ranks.size() * block_size);
165
166 // Build unique set of the sorted src_ranks
167 std::vector<std::int32_t> out_ranks(src_ranks.size());
168 out_ranks.assign(src_ranks.begin(), src_ranks.end());
169 auto [unique_end, range_end] = std::ranges::unique(out_ranks);
170 out_ranks.erase(unique_end, range_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 {
182 std::ranges::sort(in_ranks);
183 auto [unique_end, range_end] = std::ranges::unique(in_ranks);
184 in_ranks.erase(unique_end, range_end);
185 }
186 in_ranks.reserve(in_ranks.size() + 1);
187
188 // Create neighborhood communicator
189 MPI_Comm reverse_comm;
190 MPI_Dist_graph_create_adjacent(
191 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
192 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL, false, &reverse_comm);
193
194 std::vector<std::int32_t> comm_to_output;
195 std::vector<std::int32_t> recv_sizes(in_ranks.size());
196 recv_sizes.reserve(1);
197 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
198 {
199 // Build map from parent to neighborhood communicator ranks
200 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
201 rank_to_neighbor.reserve(in_ranks.size());
202 for (std::size_t i = 0; i < in_ranks.size(); i++)
203 rank_to_neighbor.push_back({in_ranks[i], i});
204 std::ranges::sort(rank_to_neighbor);
205
206 // Compute receive sizes
207 std::ranges::for_each(
208 dest_ranks,
209 [&rank_to_neighbor, &recv_sizes, block_size](auto rank)
210 {
211 if (rank >= 0)
212 {
213 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
214 std::ranges::less(),
215 [](auto e) { return e.first; });
216 assert(it != rank_to_neighbor.end() and it->first == rank);
217 recv_sizes[it->second] += block_size;
218 }
219 });
220
221 // Compute receiving offsets
222 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
223 std::next(recv_offsets.begin(), 1));
224
225 // Compute map from receiving values to position in recv_values
226 comm_to_output.resize(recv_offsets.back() / block_size);
227 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
228 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
229 {
230 if (const std::int32_t rank = dest_ranks[i]; rank >= 0)
231 {
232 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
233 std::ranges::less(),
234 [](auto e) { return e.first; });
235 assert(it != rank_to_neighbor.end() and it->first == rank);
236 int insert_pos = recv_offsets[it->second] + recv_counter[it->second];
237 comm_to_output[insert_pos / block_size] = i * block_size;
238 recv_counter[it->second] += block_size;
239 }
240 }
241 }
242
243 std::vector<std::int32_t> send_sizes(out_ranks.size());
244 send_sizes.reserve(1);
245 {
246 // Compute map from parent MPI rank to neighbor rank for outgoing
247 // data. `out_ranks` is sorted, so rank_to_neighbor will be sorted
248 // too.
249 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
250 rank_to_neighbor.reserve(out_ranks.size());
251 for (std::size_t i = 0; i < out_ranks.size(); i++)
252 rank_to_neighbor.push_back({out_ranks[i], i});
253
254 // Compute send sizes. As `src_ranks` is sorted, we can move 'start'
255 // in search forward.
256 auto start = rank_to_neighbor.begin();
257 std::ranges::for_each(
258 src_ranks,
259 [&rank_to_neighbor, &send_sizes, block_size, &start](auto rank)
260 {
261 auto it = std::ranges::lower_bound(start, rank_to_neighbor.end(),
262 rank, std::ranges::less(),
263 [](auto e) { return e.first; });
264 assert(it != rank_to_neighbor.end() and it->first == rank);
265 send_sizes[it->second] += block_size;
266 start = it;
267 });
268 }
269
270 // Compute sending offsets
271 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
272 std::partial_sum(send_sizes.begin(), send_sizes.end(),
273 std::next(send_offsets.begin(), 1));
274
275 // Send values to dest ranks
276 std::vector<T> values(recv_offsets.back());
277 values.reserve(1);
278 MPI_Neighbor_alltoallv(send_values.data_handle(), send_sizes.data(),
279 send_offsets.data(), dolfinx::MPI::mpi_t<T>,
280 values.data(), recv_sizes.data(), recv_offsets.data(),
281 dolfinx::MPI::mpi_t<T>, reverse_comm);
282 MPI_Comm_free(&reverse_comm);
283
284 // Insert values received from neighborhood communicator in output
285 // span
286 std::ranges::fill(recv_values, T{0});
287 for (std::size_t i = 0; i < comm_to_output.size(); i++)
288 {
289 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
290 auto vals_from = std::next(values.begin(), i * block_size);
291 std::copy_n(vals_from, block_size, vals);
292 }
293};
294
303template <dolfinx::MDSpanRank2 U, dolfinx::MDSpanRank2 V, dolfinx::scalar T>
304void interpolation_apply(U&& Pi, V&& data, std::span<T> coeffs, int bs)
305{
306 // Geometry (real) scalar type, taken from the interpolation operator Pi
307 // rather than scalar_value_t<T> so it is independent of the value scalar T.
308 using X = typename std::remove_cvref_t<U>::value_type;
309
310 // Compute coefficients = Pi * x (matrix-vector multiply)
311 if (bs == 1)
312 {
313 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
314 for (std::size_t i = 0; i < Pi.extent(0); ++i)
315 {
316 coeffs[i] = 0.0;
317 for (std::size_t k = 0; k < data.extent(1); ++k)
318 for (std::size_t j = 0; j < data.extent(0); ++j)
319 coeffs[i]
320 += static_cast<X>(Pi(i, k * data.extent(0) + j)) * data(j, k);
321 }
322 }
323 else
324 {
325 assert(data.extent(0) == Pi.extent(1));
326 assert(static_cast<int>(data.extent(1)) == bs);
327 std::size_t cols = Pi.extent(1);
328 for (int k = 0; k < bs; ++k)
329 {
330 for (std::size_t i = 0; i < Pi.extent(0); ++i)
331 {
332 T acc = 0;
333 for (std::size_t j = 0; j < cols; ++j)
334 acc += static_cast<X>(Pi(i, j)) * data(j, k);
335 coeffs[bs * i + k] = acc;
336 }
337 }
338 }
339}
340
360template <dolfinx::scalar T, std::floating_point U>
361void interpolate_same_map(Function<T, U>& u1, mesh::CellRange auto&& cells1,
362 const Function<T, U>& u0,
363 mesh::CellRange auto&& cells0)
364{
365 auto V0 = u0.function_space();
366 assert(V0);
367 auto V1 = u1.function_space();
368 assert(V1);
369 auto mesh0 = V0->mesh();
370 assert(mesh0);
371
372 auto mesh1 = V1->mesh();
373 assert(mesh1);
374
375 auto element0 = V0->element();
376 assert(element0);
377 auto element1 = V1->element();
378 assert(element1);
379
380 assert(mesh0->topology()->dim());
381 const int tdim = mesh0->topology()->dim();
382 auto map = mesh0->topology()->index_map(tdim);
383 assert(map);
384 std::span<T> u1_array = u1.x()->array();
385 std::span<const T> u0_array = u0.x()->array();
386
387 std::span<const std::uint32_t> cell_info0;
388 std::span<const std::uint32_t> cell_info1;
389 if (element1->needs_dof_transformations()
390 or element0->needs_dof_transformations())
391 {
392 mesh0->topology_mutable()->create_entity_permutations();
393 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
394 mesh1->topology_mutable()->create_entity_permutations();
395 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
396 }
397
398 // Get dofmaps
399 auto dofmap1 = V1->dofmap();
400 auto dofmap0 = V0->dofmap();
401
402 // Get block sizes and dof transformation operators
403 const int bs1 = dofmap1->bs();
404 const int bs0 = dofmap0->bs();
405 auto apply_dof_transformation = element0->template dof_transformation_fn<T>(
407 auto apply_inverse_dof_transform
408 = element1->template dof_transformation_fn<T>(
410
411 // Create working array
412 std::vector<T> local0(element0->space_dimension());
413 std::vector<T> local1(element1->space_dimension());
414
415 // Create interpolation operator
416 auto [i_m, im_shape] = element1->create_interpolation_operator(*element0);
417
418 // Iterate over mesh and interpolate on each cell
419 using X = U; // geometry (real) type, independent of the value scalar T
420 if (cells0.size() != cells1.size())
421 throw std::runtime_error("Length of cells0 and cells1 must match.");
422 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
423 cell0_it != cells0.end() and cell1_it != cells1.end();
424 ++cell0_it, ++cell1_it)
425 {
426 // Pack and transform cell dofs to reference ordering
427 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
428 for (std::size_t i = 0; i < dofs0.size(); ++i)
429 for (int k = 0; k < bs0; ++k)
430 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
431
432 apply_dof_transformation(local0, cell_info0, *cell0_it, 1);
433
434 // FIXME: Get compile-time ranges from Basix
435 // Apply interpolation operator
436 std::ranges::fill(local1, 0);
437 for (std::size_t i = 0; i < im_shape[0]; ++i)
438 for (std::size_t j = 0; j < im_shape[1]; ++j)
439 local1[i] += static_cast<X>(i_m[im_shape[1] * i + j]) * local0[j];
440
441 apply_inverse_dof_transform(local1, cell_info1, *cell1_it, 1);
442 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
443 for (std::size_t i = 0; i < dofs1.size(); ++i)
444 for (int k = 0; k < bs1; ++k)
445 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
446 }
447}
448
463template <dolfinx::scalar T, std::floating_point U>
464void interpolate_nonmatching_maps(Function<T, U>& u1,
465 mesh::CellRange auto&& cells1,
466 const Function<T, U>& u0,
467 mesh::CellRange auto&& cells0)
468{
469 // Get mesh
470 auto V0 = u0.function_space();
471 assert(V0);
472 auto mesh0 = V0->mesh();
473 assert(mesh0);
474
475 // Mesh dims
476 const int tdim = mesh0->topology()->dim();
477 const int gdim = mesh0->geometry().dim();
478
479 // Get elements
480 auto V1 = u1.function_space();
481 assert(V1);
482 auto mesh1 = V1->mesh();
483 assert(mesh1);
484 auto element0 = V0->element();
485 assert(element0);
486 auto element1 = V1->element();
487 assert(element1);
488
489 std::span<const std::uint32_t> cell_info0;
490 std::span<const std::uint32_t> cell_info1;
491 if (element1->needs_dof_transformations()
492 or element0->needs_dof_transformations())
493 {
494 mesh0->topology_mutable()->create_entity_permutations();
495 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
496 mesh1->topology_mutable()->create_entity_permutations();
497 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
498 }
499
500 // Get dofmaps
501 auto dofmap0 = V0->dofmap();
502 auto dofmap1 = V1->dofmap();
503
504 const auto [X, Xshape] = element1->interpolation_points();
505
506 // Get block sizes and dof transformation operators
507 const int bs0 = element0->block_size();
508 const int bs1 = element1->block_size();
509 auto apply_dof_transformation0 = element0->template dof_transformation_fn<U>(
511 auto apply_inv_dof_transform1 = element1->template dof_transformation_fn<T>(
513
514 // Get sizes of elements
515 const std::size_t dim0 = element0->space_dimension() / bs0;
516 const std::size_t value_size_ref0 = element0->reference_value_size();
517 const std::size_t value_size0 = V0->element()->reference_value_size();
518
519 const CoordinateElement<U>& cmap = mesh0->geometry().cmaps().front();
520 auto x_dofmap = mesh0->geometry().dofmaps().front();
521 std::span<const U> x_g = mesh0->geometry().x();
522
523 // (0) is derivative index, (1) is the point index, (2) is the basis
524 // function index and (3) is the basis function component.
525
526 // Evaluate coordinate map basis at reference interpolation points
527 const std::array<std::size_t, 4> phi_shape
528 = cmap.tabulate_shape(1, Xshape[0]);
529 std::vector<U> phi_b(
530 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
531 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
532 md::dynamic_extent, md::dynamic_extent, 1>>
533 phi(phi_b.data(), phi_shape);
534 cmap.tabulate(1, X, Xshape, phi_b);
535
536 // Evaluate v basis functions at reference interpolation points
537 const auto [_basis_derivatives_reference0, b0shape]
538 = element0->tabulate(X, Xshape, 0);
539 md::mdspan<const U, std::extents<std::size_t, 1, md::dynamic_extent,
540 md::dynamic_extent, md::dynamic_extent>>
541 basis_derivatives_reference0(_basis_derivatives_reference0.data(),
542 b0shape);
543
544 // Create working arrays
545 std::vector<T> local1(element1->space_dimension());
546 std::vector<T> coeffs0(element0->space_dimension());
547
548 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
549 md::mdspan<U, std::dextents<std::size_t, 3>> basis0(
550 basis0_b.data(), Xshape[0], dim0, value_size0);
551
552 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
553 md::mdspan<U, std::dextents<std::size_t, 3>> basis_reference0(
554 basis_reference0_b.data(), Xshape[0], dim0, value_size_ref0);
555
556 std::vector<T> values0_b(Xshape[0] * 1 * V1->element()->value_size());
557 md::mdspan<
558 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
559 values0(values0_b.data(), Xshape[0], 1, V1->element()->value_size());
560
561 std::vector<T> mapped_values_b(Xshape[0] * 1 * V1->element()->value_size());
562 md::mdspan<
563 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
564 mapped_values0(mapped_values_b.data(), Xshape[0], 1,
565 V1->element()->value_size());
566
567 const std::size_t num_dofs_g = cmap.dim();
568 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
569 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
570 num_dofs_g, gdim);
571
572 std::vector<U> J_b(Xshape[0] * gdim * tdim);
573 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
574 tdim);
575 std::vector<U> K_b(Xshape[0] * tdim * gdim);
576 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
577 gdim);
578 std::vector<U> detJ(Xshape[0]);
579 std::vector<U> det_scratch(2 * gdim * tdim);
580
581 // Get interpolation operator
582 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
583 impl::mdspan_t<const U, 2> Pi_1(_Pi_1.data(), pi_shape);
584
585 using u_t = md::mdspan<U, std::dextents<std::size_t, 2>>;
586 using U_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
587 using J_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
588 using K_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
589 auto push_forward_fn0
590 = element0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
591
592 using v_t = md::mdspan<const T, std::dextents<std::size_t, 2>>;
593 using V_t = decltype(md::submdspan(mapped_values0, 0, md::full_extent,
594 md::full_extent));
595 auto pull_back_fn1
596 = element1->basix_element().template map_fn<V_t, v_t, K_t, J_t>();
597
598 // Iterate over mesh and interpolate on each cell
599 std::span<const T> array0 = u0.x()->array();
600 std::span<T> array1 = u1.x()->array();
601 if (cells0.size() != cells1.size())
602 throw std::runtime_error("Length of cells0 and cells1 must match.");
603 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
604 cell0_it != cells0.end() and cell1_it != cells1.end();
605 ++cell0_it, ++cell1_it)
606 {
607 // Get cell geometry (coordinate dofs)
608 auto x_dofs = md::submdspan(x_dofmap, *cell0_it, md::full_extent);
609 for (std::size_t i = 0; i < num_dofs_g; ++i)
610 {
611 const int pos = 3 * x_dofs[i];
612 for (int j = 0; j < gdim; ++j)
613 coord_dofs(i, j) = x_g[pos + j];
614 }
615
616 // Compute Jacobians and reference points for current cell
617 std::ranges::fill(J_b, 0);
618 for (std::size_t p = 0; p < Xshape[0]; ++p)
619 {
620 auto dphi
621 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
622 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
623 cmap.compute_jacobian(dphi, coord_dofs, _J);
624 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
625 cmap.compute_jacobian_inverse(_J, _K);
626 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
627 }
628
629 // Copy evaluated basis on reference, apply DOF transformations, and
630 // push forward to physical element
631 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
632 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
633 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
634 basis_reference0(k0, k1, k2)
635 = basis_derivatives_reference0(0, k0, k1, k2);
636
637 for (std::size_t p = 0; p < Xshape[0]; ++p)
638 {
639 apply_dof_transformation0(
640 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
641 dim0 * value_size_ref0),
642 cell_info0, *cell0_it, value_size_ref0);
643 }
644
645 for (std::size_t i = 0; i < basis0.extent(0); ++i)
646 {
647 auto _u = md::submdspan(basis0, i, md::full_extent, md::full_extent);
648 auto _U = md::submdspan(basis_reference0, i, md::full_extent,
649 md::full_extent);
650 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
651 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
652 push_forward_fn0(_u, _U, _J, detJ[i], _K);
653 }
654
655 // Copy expansion coefficients for v into local array
656 const int dof_bs0 = dofmap0->bs();
657 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
658 for (std::size_t i = 0; i < dofs0.size(); ++i)
659 for (int k = 0; k < dof_bs0; ++k)
660 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
661
662 // Evaluate v at the interpolation points (physical space values)
663 using X = U; // geometry (real) type, independent of the value scalar T
664 for (std::size_t p = 0; p < Xshape[0]; ++p)
665 {
666 for (int k = 0; k < bs0; ++k)
667 {
668 for (std::size_t j = 0; j < value_size0; ++j)
669 {
670 T acc = 0;
671 for (std::size_t i = 0; i < dim0; ++i)
672 acc += coeffs0[bs0 * i + k] * static_cast<X>(basis0(p, i, j));
673 values0(p, 0, j * bs0 + k) = acc;
674 }
675 }
676 }
677
678 // Pull back the physical values to the u reference
679 for (std::size_t i = 0; i < values0.extent(0); ++i)
680 {
681 auto _u = md::submdspan(values0, i, md::full_extent, md::full_extent);
682 auto _U
683 = md::submdspan(mapped_values0, i, md::full_extent, md::full_extent);
684 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
685 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
686 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
687 }
688
689 auto values
690 = md::submdspan(mapped_values0, md::full_extent, 0, md::full_extent);
691 interpolation_apply(Pi_1, values, std::span(local1), bs1);
692 apply_inv_dof_transform1(local1, cell_info1, *cell1_it, 1);
693
694 // Copy local coefficients to the correct position in u dof array
695 const int dof_bs1 = dofmap1->bs();
696 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
697 for (std::size_t i = 0; i < dofs1.size(); ++i)
698 for (int k = 0; k < dof_bs1; ++k)
699 array1[dof_bs1 * dofs1[i] + k] = local1[dof_bs1 * i + k];
700 }
701}
702
714template <dolfinx::scalar T, std::floating_point U>
715void point_evaluation(const FiniteElement<U>& element, bool symmetric,
716 const DofMap& dofmap, mesh::CellRange auto&& cells,
717 std::span<const std::uint32_t> cell_info,
718 std::span<const T> f, std::array<std::size_t, 2> fshape,
719 std::span<T> coeffs)
720{
721 // Point evaluation element *and* the geometric map is the identity,
722 // e.g. not Piola mapped
723
724 const int element_bs = element.block_size();
725 const int num_scalar_dofs = element.space_dimension() / element_bs;
726 const int dofmap_bs = dofmap.bs();
727
728 auto apply_inv_transpose_dof_transformation
729 = element.template dof_transformation_fn<T>(
731 std::vector<T> coeffs_b(num_scalar_dofs);
732
733 // Skip the div/mod below when block sizes match (the common case)
734 const bool same_bs = (dofmap_bs == element_bs);
735
736 if (symmetric)
737 {
738 std::size_t matrix_size = 0;
739 while (matrix_size * matrix_size < fshape[0])
740 ++matrix_size;
741
742 // Loop over cells
743 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
744 {
745 // The entries of a symmetric matrix are numbered (for an
746 // example 4x4 element):
747 // 0 * * *
748 // 1 2 * *
749 // 3 4 5 *
750 // 6 7 8 9
751 // The loop extracts these elements. In this loop, row is the
752 // row of this matrix, and (k - rowstart) is the column
753 std::size_t row = 0;
754 std::size_t rowstart = 0;
755 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
756 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
757 for (int k = 0; k < element_bs; ++k)
758 {
759 if (k - rowstart > row)
760 {
761 ++row;
762 rowstart = k;
763 }
764
765 // num_scalar_dofs is the number of interpolation points per
766 // cell in this case (interpolation matrix is identity)
767 std::copy_n(
768 std::next(f.begin(), (row * matrix_size + k - rowstart) * fshape[1]
769 + offset * num_scalar_dofs),
770 num_scalar_dofs, coeffs_b.data());
771 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
772 1);
773 if (same_bs)
774 {
775 for (int i = 0; i < num_scalar_dofs; ++i)
776 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
777 }
778 else
779 {
780 for (int i = 0; i < num_scalar_dofs; ++i)
781 {
782 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
783 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
784 }
785 }
786 }
787 }
788 }
789 else
790 {
791 // Loop over cells
792 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
793 {
794 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
795 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
796 for (int k = 0; k < element_bs; ++k)
797 {
798 // num_scalar_dofs is the number of interpolation points per
799 // cell in this case (interpolation matrix is identity)
800 std::copy_n(
801 std::next(f.begin(), k * fshape[1] + offset * num_scalar_dofs),
802 num_scalar_dofs, coeffs_b.data());
803 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
804 1);
805 if (same_bs)
806 {
807 for (int i = 0; i < num_scalar_dofs; ++i)
808 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
809 }
810 else
811 {
812 for (int i = 0; i < num_scalar_dofs; ++i)
813 {
814 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
815 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
816 }
817 }
818 }
819 }
820 }
821}
822
834template <dolfinx::scalar T, std::floating_point U>
835void identity_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
836 const DofMap& dofmap,
837 mesh::CellRange auto&& cells,
838 std::span<const std::uint32_t> cell_info,
839 std::span<const T> f,
840 std::array<std::size_t, 2> fshape,
841 std::span<T> coeffs)
842{
843 // Not a point evaluation, but the geometric map is the identity,
844 // e.g. not Piola mapped
845
846 if (symmetric)
847 throw std::runtime_error("Interpolation into this element not supported.");
848
849 const int element_bs = element.block_size();
850 const int num_scalar_dofs = element.space_dimension() / element_bs;
851 const int dofmap_bs = dofmap.bs();
852
853 const int element_vs = element.reference_value_size();
854 if (element_vs > 1 and element_bs > 1)
855 throw std::runtime_error("Interpolation into this element not supported.");
856
857 // Get interpolation operator
858 const auto [_Pi, pi_shape] = element.interpolation_operator();
859 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
860 const std::size_t num_interp_points = Pi.extent(1);
861 assert(static_cast<int>(Pi.extent(0)) == num_scalar_dofs);
862
863 auto apply_inv_transpose_dof_transformation
864 = element.template dof_transformation_fn<T>(
866
867 // Skip the div/mod below when block sizes match (the common case)
868 const bool same_bs = (dofmap_bs == element_bs);
869
870 // Loop over cells
871 std::vector<T> ref_data_b(num_interp_points);
872 md::mdspan<T, md::extents<std::size_t, md::dynamic_extent, 1>> ref_data(
873 ref_data_b.data(), num_interp_points, 1);
874 std::vector<T> coeffs_b(num_scalar_dofs);
875 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
876 {
877 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
878 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
879 for (int k = 0; k < element_bs; ++k)
880 {
881 for (int i = 0; i < element_vs; ++i)
882 {
883 std::copy_n(
884 std::next(f.begin(), (i + k) * fshape[1]
885 + offset * num_interp_points / element_vs),
886 num_interp_points / element_vs,
887 std::next(ref_data_b.begin(), i * num_interp_points / element_vs));
888 }
889
890 impl::interpolation_apply(Pi, ref_data, std::span(coeffs_b), 1);
891 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
892 if (same_bs)
893 {
894 for (int i = 0; i < num_scalar_dofs; ++i)
895 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
896 }
897 else
898 {
899 for (int i = 0; i < num_scalar_dofs; ++i)
900 {
901 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
902 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
903 }
904 }
905 }
906 }
907}
908
921template <dolfinx::scalar T, std::floating_point U>
922void piola_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
923 const DofMap& dofmap, mesh::CellRange auto&& cells,
924 std::span<const std::uint32_t> cell_info,
925 std::span<const T> f,
926 std::array<std::size_t, 2> fshape,
927 const mesh::Mesh<U>& mesh, std::span<T> coeffs)
928{
929 if (symmetric)
930 throw std::runtime_error("Interpolation into this element not supported.");
931
932 const int gdim = mesh.geometry().dim();
933 assert(mesh.topology());
934 const int tdim = mesh.topology()->dim();
935
936 const int element_bs = element.block_size();
937 const int num_scalar_dofs = element.space_dimension() / element_bs;
938 const int value_size = element.reference_value_size();
939 const int dofmap_bs = dofmap.bs();
940
941 // Skip the div/mod below when block sizes match (the common case)
942 const bool same_bs = (dofmap_bs == element_bs);
943
944 md::mdspan<const T, md::dextents<std::size_t, 2>> _f(f.data(), fshape);
945
946 // Get the interpolation points on the reference cells
947 const auto [X, Xshape] = element.interpolation_points();
948 if (X.empty())
949 {
950 throw std::runtime_error(
951 "Interpolation into this space is not yet supported.");
952 }
953
954 if (_f.extent(1) != cells.size() * Xshape[0])
955 throw std::runtime_error("Interpolation data has the wrong shape.");
956
957 // Get coordinate map
958 const CoordinateElement<U>& cmap = mesh.geometry().cmaps().front();
959
960 // Get geometry data
961 auto x_dofmap = mesh.geometry().dofmaps().front();
962 const int num_dofs_g = cmap.dim();
963 std::span<const U> x_g = mesh.geometry().x();
964
965 // Create data structures for Jacobian info
966 std::vector<U> J_b(Xshape[0] * gdim * tdim);
967 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
968 tdim);
969 std::vector<U> K_b(Xshape[0] * tdim * gdim);
970 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
971 gdim);
972 std::vector<U> detJ(Xshape[0]);
973 std::vector<U> det_scratch(2 * gdim * tdim);
974
975 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
976 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
977 num_dofs_g, gdim);
978 const std::size_t value_size_ref = element.reference_value_size();
979 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size_ref);
980 md::mdspan<
981 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
982 ref_data(ref_data_b.data(), Xshape[0], 1, value_size_ref);
983
984 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
985 md::mdspan<
986 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
987 _vals(_vals_b.data(), Xshape[0], 1, value_size);
988
989 // Tabulate 1st derivative of shape functions at interpolation
990 // coords
991 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
992 std::vector<U> phi_b(
993 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
994 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
995 md::dynamic_extent, md::dynamic_extent, 1>>
996 phi(phi_b.data(), phi_shape);
997 cmap.tabulate(1, X, Xshape, phi_b);
998 auto dphi = md::submdspan(phi, std::pair(1, tdim + 1), md::full_extent,
999 md::full_extent, 0);
1000
1001 std::function<void(std::span<T>, std::span<const std::uint32_t>, std::int32_t,
1002 int)>
1003 apply_inv_trans_dof_transformation
1004 = element.template dof_transformation_fn<T>(
1006
1007 // Get interpolation operator
1008 const auto [_Pi, pi_shape] = element.interpolation_operator();
1009 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
1010
1011 using u_t = md::mdspan<const T, md::dextents<std::size_t, 2>>;
1012 using U_t
1013 = decltype(md::submdspan(ref_data, 0, md::full_extent, md::full_extent));
1014 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1015 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1016 auto pull_back_fn
1017 = element.basix_element().template map_fn<U_t, u_t, J_t, K_t>();
1018
1019 std::vector<T> coeffs_b(num_scalar_dofs);
1020 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
1021 {
1022 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
1023 for (int i = 0; i < num_dofs_g; ++i)
1024 {
1025 const int pos = 3 * x_dofs[i];
1026 for (int j = 0; j < gdim; ++j)
1027 coord_dofs(i, j) = x_g[pos + j];
1028 }
1029
1030 // Compute J, detJ and K
1031 std::ranges::fill(J_b, 0);
1032 for (std::size_t p = 0; p < Xshape[0]; ++p)
1033 {
1034 auto _dphi = md::submdspan(dphi, md::full_extent, p, md::full_extent);
1035 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
1036 cmap.compute_jacobian(_dphi, coord_dofs, _J);
1037 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
1038 cmap.compute_jacobian_inverse(_J, _K);
1039 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
1040 }
1041
1042 const std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
1043 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
1044 for (int k = 0; k < element_bs; ++k)
1045 {
1046 // Extract computed expression values for element block k
1047 for (int m = 0; m < value_size; ++m)
1048 {
1049 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
1050 {
1051 _vals(k0, 0, m)
1052 = f[fshape[1] * (k * value_size + m) + offset * Xshape[0] + k0];
1053 }
1054 }
1055
1056 // Get element degrees of freedom for block
1057 for (std::size_t i = 0; i < Xshape[0]; ++i)
1058 {
1059 auto _u = md::submdspan(_vals, i, md::full_extent, md::full_extent);
1060 auto _U = md::submdspan(ref_data, i, md::full_extent, md::full_extent);
1061 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
1062 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
1063 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
1064 }
1065
1066 auto ref = md::submdspan(ref_data, md::full_extent, 0, md::full_extent);
1067 impl::interpolation_apply(Pi, ref, std::span(coeffs_b), element_bs);
1068 apply_inv_trans_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
1069
1070 // Copy interpolation dofs into coefficient vector
1071 assert(coeffs_b.size() == static_cast<std::size_t>(num_scalar_dofs));
1072 if (same_bs)
1073 {
1074 for (int i = 0; i < num_scalar_dofs; ++i)
1075 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
1076 }
1077 else
1078 {
1079 for (int i = 0; i < num_scalar_dofs; ++i)
1080 {
1081 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
1082 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
1083 }
1084 }
1085 }
1086 }
1087}
1088
1089//----------------------------------------------------------------------------
1090} // namespace impl
1091
1110template <std::floating_point T>
1112 const mesh::Geometry<T>& geometry0, const FiniteElement<T>& element0,
1113 const mesh::Mesh<T>& mesh1, mesh::CellRange auto&& cells, T padding)
1114{
1115 // Collect all the points at which values are needed to define the
1116 // interpolating function
1117 std::vector<T> coords = interpolation_coords(element0, geometry0, cells);
1118
1119 // Transpose interpolation coords
1120 std::vector<T> x(coords.size());
1121 std::size_t num_points = coords.size() / 3;
1122 for (std::size_t i = 0; i < num_points; ++i)
1123 for (std::size_t j = 0; j < 3; ++j)
1124 x[3 * i + j] = coords[i + j * num_points];
1125
1126 // Determine ownership of each point
1127 return geometry::determine_point_ownership<T>(mesh1, x, padding,
1128 std::nullopt);
1129}
1130
1131template <dolfinx::scalar T, std::floating_point U>
1132void interpolate(Function<T, U>& u, std::span<const T> f,
1133 std::array<std::size_t, 2> fshape,
1134 mesh::CellRange auto&& cells)
1135{
1136 // TODO: Index for mixed-topology, zero for now
1137 const int index = 0;
1138 auto element = u.function_space()->elements(index);
1139 assert(element);
1140 const int element_bs = element->block_size();
1141 if (int num_sub = element->num_sub_elements();
1142 num_sub > 0 and num_sub != element_bs)
1143 {
1144 throw std::runtime_error("Cannot directly interpolate a mixed space. "
1145 "Interpolate into subspaces.");
1146 }
1147
1148 // Get mesh
1149 assert(u.function_space());
1150 auto mesh = u.function_space()->mesh();
1151 assert(mesh);
1152
1153 if (fshape[0]
1154 != (std::size_t)u.function_space()->elements(index)->value_size()
1155 or f.size() != fshape[0] * fshape[1])
1156 {
1157 throw std::runtime_error("Interpolation data has the wrong shape/size.");
1158 }
1159
1160 spdlog::debug("Check for dof transformation");
1161 std::span<const std::uint32_t> cell_info;
1162 if (element->needs_dof_transformations())
1163 {
1164 mesh->topology_mutable()->create_entity_permutations();
1165 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
1166 }
1167
1168 // Get dofmap
1169 spdlog::debug("Interpolate: get dofmap");
1170 const auto dofmap = u.function_space()->dofmaps().at(index);
1171 assert(dofmap);
1172
1173 // Result will be stored to coeffs
1174 std::span<T> coeffs = u.x()->array();
1175
1176 if (bool symmetric = u.function_space()->symmetric();
1177 element->map_ident() and element->interpolation_ident())
1178 {
1179 // This assumes that any element with an identity interpolation
1180 // matrix is a point evaluation
1181 spdlog::debug("Interpolate: point evaluation");
1182 impl::point_evaluation(*element, symmetric, *dofmap, cells, cell_info, f,
1183 fshape, coeffs);
1184 }
1185 else if (element->map_ident())
1186 {
1187 spdlog::debug("Interpolate: identity-mapped evaluation");
1188 impl::identity_mapped_evaluation(*element, symmetric, *dofmap, cells,
1189 cell_info, f, fshape, coeffs);
1190 }
1191 else
1192 {
1193 spdlog::debug("Interpolate: Piola-mapped evaluation");
1194 impl::piola_mapped_evaluation(*element, symmetric, *dofmap, cells,
1195 cell_info, f, fshape, *mesh, coeffs);
1196 }
1197}
1198
1215template <dolfinx::scalar T, std::floating_point U>
1217 mesh::CellRange auto&& cells, double tol, int maxit,
1218 const geometry::PointOwnershipData<U>& interpolation_data)
1219{
1220 auto mesh1 = u1.function_space()->mesh();
1221 assert(mesh1);
1222 MPI_Comm comm = mesh1->comm();
1223 {
1224 assert(u0.function_space());
1225 auto mesh0 = u0.function_space()->mesh();
1226 assert(mesh0);
1227 int result;
1228 MPI_Comm_compare(comm, mesh0->comm(), &result);
1229 if (result == MPI_UNEQUAL)
1230 {
1231 throw std::runtime_error("Interpolation on different meshes is only "
1232 "supported on the same communicator.");
1233 }
1234 }
1235
1236 assert(mesh1->topology());
1237 auto cell_map = mesh1->topology()->index_map(mesh1->topology()->dim());
1238 assert(cell_map);
1239 auto element1 = u1.function_space()->element();
1240 assert(element1);
1241 const std::size_t value_size = element1->value_size();
1242
1243 const std::vector<int>& dest_ranks = interpolation_data.src_owner;
1244 const std::vector<int>& src_ranks = interpolation_data.dest_owners;
1245 const std::vector<U>& recv_points = interpolation_data.dest_points;
1246 const std::vector<std::int32_t>& evaluation_cells
1247 = interpolation_data.dest_cells;
1248
1249 // Evaluate the interpolating function where possible
1250 std::vector<T> send_values(recv_points.size() / 3 * value_size);
1251 u0.eval(recv_points, {recv_points.size() / 3, (std::size_t)3},
1252 evaluation_cells, send_values, {recv_points.size() / 3, value_size},
1253 tol, maxit);
1254
1255 // Send values back to owning process
1256 std::vector<T> values_b(dest_ranks.size() * value_size);
1257 md::mdspan<const T, md::dextents<std::size_t, 2>> _send_values(
1258 send_values.data(), src_ranks.size(), value_size);
1259 impl::scatter_values(comm, src_ranks, dest_ranks, _send_values,
1260 std::span(values_b));
1261
1262 // Transpose received data
1263 md::mdspan<const T, md::dextents<std::size_t, 2>> values(
1264 values_b.data(), dest_ranks.size(), value_size);
1265 std::vector<T> valuesT_b(value_size * dest_ranks.size());
1266 md::mdspan<T, md::dextents<std::size_t, 2>> valuesT(
1267 valuesT_b.data(), value_size, dest_ranks.size());
1268 for (std::size_t i = 0; i < values.extent(0); ++i)
1269 for (std::size_t j = 0; j < values.extent(1); ++j)
1270 valuesT(j, i) = values(i, j);
1271
1272 // Call local interpolation operator
1273 fem::interpolate<T>(u1, valuesT_b, {valuesT.extent(0), valuesT.extent(1)},
1274 cells);
1275}
1276
1293template <dolfinx::scalar T, std::floating_point U>
1295 const Function<T, U>& u0, mesh::CellRange auto&& cells0)
1296{
1297 if (cells0.size() != cells1.size())
1298 throw std::runtime_error("Length of cell lists do not match.");
1299
1300 auto V1 = u1.function_space();
1301 assert(V1);
1302 auto V0 = u0.function_space();
1303 assert(V0);
1304
1305 // Get elements and check value shape
1306 auto e0 = V0->element();
1307 assert(e0);
1308 auto e1 = V1->element();
1309 assert(e1);
1310 if (!std::ranges::equal(e0->value_shape(), e1->value_shape()))
1311 {
1312 throw std::runtime_error(
1313 "Interpolation: elements have different value dimensions");
1314 }
1315
1316 if (V1->mesh() == V0->mesh() and (e1 == e0 or *e1 == *e0))
1317 {
1318 // Same element and same mesh
1319 if (e1->block_size() != e0->block_size())
1320 throw std::runtime_error("Mismatch in element block size.");
1321
1322 // Get dofmaps
1323 std::shared_ptr<const DofMap> dofmap0 = V0->dofmap();
1324 assert(dofmap0);
1325 std::shared_ptr<const DofMap> dofmap1 = V1->dofmap();
1326 assert(dofmap1);
1327
1328 // Iterate over mesh and interpolate on each cell
1329 const int bs0 = dofmap0->bs();
1330 const int bs1 = dofmap1->bs();
1331 std::span<T> u1_array = u1.x()->array();
1332 std::span<const T> u0_array = u0.x()->array();
1333 assert(cells0.size() == cells1.size());
1334 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
1335 cell0_it != cells0.end() and cell1_it != cells1.end();
1336 ++cell0_it, ++cell1_it)
1337
1338 {
1339 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
1340 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
1341 assert(bs0 * dofs0.size() == bs1 * dofs1.size());
1342 for (std::size_t i = 0; i < dofs0.size(); ++i)
1343 {
1344 for (int k = 0; k < bs0; ++k)
1345 {
1346 int index = bs0 * i + k;
1347 std::div_t dv1 = std::div(index, bs1);
1348 u1_array[bs1 * dofs1[dv1.quot] + dv1.rem]
1349 = u0_array[bs0 * dofs0[i] + k];
1350 }
1351 }
1352 }
1353 }
1354 else if (e1->map_type() == e0->map_type())
1355 {
1356 // Different elements, same basis function map type
1357 impl::interpolate_same_map(u1, cells1, u0, cells0);
1358 }
1359 else
1360 {
1361 // Different elements with different maps for basis functions
1362 impl::interpolate_nonmatching_maps(u1, cells1, u0, cells0);
1363 }
1364}
1365
1375template <dolfinx::scalar T, std::floating_point U>
1377 std::ranges::input_range auto&& cells)
1378{
1379 assert(u1.function_space());
1380 assert(u0.function_space());
1381 if (u1.function_space()->mesh() == u0.function_space()->mesh())
1382 interpolate<T, U>(u1, cells, u0, cells);
1383 else
1384 throw std::runtime_error("Meshes do no match.");
1385}
1386
1396template <dolfinx::scalar T, std::floating_point U>
1398{
1399 assert(u1.function_space());
1400 assert(u0.function_space());
1401 if (auto V1 = u1.function_space(); V1 == u0.function_space())
1402 std::ranges::copy(u0.x()->array(), u1.x()->array().begin());
1403 else
1404 {
1405 auto mesh = V1->mesh();
1406 assert(mesh);
1407 assert(mesh->topology());
1408 auto map = mesh->topology()->index_map(mesh->topology()->dim());
1409 assert(map);
1410 std::int32_t num_cells = map->size_local() + map->num_ghosts();
1411 interpolate<T, U>(u1, u0, std::ranges::views::iota(0, num_cells));
1412 }
1413}
1414} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:38
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:59
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:52
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:222
Model of a finite element.
Definition FiniteElement.h:57
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:464
mesh::CellType cell_type() const noexcept
Cell shape that the element is defined on.
Definition FiniteElement.cpp:279
Definition Function.h:47
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:147
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:457
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:153
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:37
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
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:320
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:23
void interpolate(Function< T, U > &u, std::span< const T > f, std::array< std::size_t, 2 > fshape, mesh::CellRange auto &&cells)
Interpolate an evaluated expression f(x) in a finite element space.
Definition interpolate.h:1132
@ transpose
Transpose.
Definition FiniteElement.h:28
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:30
@ standard
Standard.
Definition FiniteElement.h:27
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:43
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)
Generate data needed to interpolate finite element fem::Function's across different meshes.
Definition interpolate.h:1111
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)
Given a set of points, determine which process is colliding, using the GJK algorithm on cells to dete...
Definition utils.h:684
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
CellType
Cell type identifier.
Definition cell_types.h:22
Information on the ownership of points distributed across processes.
Definition utils.h:30
std::vector< T > dest_points
Points that are owned by current process.
Definition utils.h:35
std::vector< std::int32_t > dest_cells
Definition utils.h:37
std::vector< int > dest_owners
Ranks that sent dest_points to current process.
Definition utils.h:34
std::vector< int > src_owner
Definition utils.h:31