Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/df/dd3/interpolate_8h_source.html
DOLFINx 0.6.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
interpolate.h
1// Copyright (C) 2020-2022 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 <dolfinx/common/IndexMap.h>
15#include <dolfinx/geometry/BoundingBoxTree.h>
16#include <dolfinx/geometry/utils.h>
17#include <dolfinx/mesh/Mesh.h>
18#include <functional>
19#include <numeric>
20#include <span>
21#include <vector>
22
23namespace dolfinx::fem
24{
25template <typename T>
26class Function;
27
38std::vector<double> interpolation_coords(const fem::FiniteElement& element,
39 const mesh::Mesh& mesh,
40 std::span<const std::int32_t> cells);
41namespace impl
42{
63template <typename T>
64void scatter_values(const MPI_Comm& comm,
65 std::span<const std::int32_t> src_ranks,
66 std::span<const std::int32_t> dest_ranks,
67 std::span<const T> send_values,
68 const std::array<std::size_t, 2>& s_shape,
69 std::span<T> recv_values)
70{
71 const std::size_t block_size = s_shape[1];
72 assert(src_ranks.size() * block_size == send_values.size());
73 assert(recv_values.size() == dest_ranks.size() * block_size);
74
75 // Build unique set of the sorted src_ranks
76 std::vector<std::int32_t> out_ranks(src_ranks.size());
77 out_ranks.assign(src_ranks.begin(), src_ranks.end());
78 out_ranks.erase(std::unique(out_ranks.begin(), out_ranks.end()),
79 out_ranks.end());
80 out_ranks.reserve(out_ranks.size() + 1);
81
82 // Remove negative entries from dest_ranks
83 std::vector<std::int32_t> in_ranks;
84 in_ranks.reserve(dest_ranks.size());
85 std::copy_if(dest_ranks.begin(), dest_ranks.end(),
86 std::back_inserter(in_ranks),
87 [](auto rank) { return rank >= 0; });
88
89 // Create unique set of sorted in-ranks
90 std::sort(in_ranks.begin(), in_ranks.end());
91 in_ranks.erase(std::unique(in_ranks.begin(), in_ranks.end()), in_ranks.end());
92 in_ranks.reserve(in_ranks.size() + 1);
93
94 // Create neighborhood communicator
95 MPI_Comm reverse_comm;
96 MPI_Dist_graph_create_adjacent(
97 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
98 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL, false, &reverse_comm);
99
100 std::vector<std::int32_t> comm_to_output;
101 std::vector<std::int32_t> recv_sizes(in_ranks.size());
102 recv_sizes.reserve(1);
103 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
104 {
105 // Build map from parent to neighborhood communicator ranks
106 std::map<std::int32_t, std::int32_t> rank_to_neighbor;
107 for (std::size_t i = 0; i < in_ranks.size(); i++)
108 rank_to_neighbor[in_ranks[i]] = i;
109
110 // Compute receive sizes
111 std::for_each(
112 dest_ranks.begin(), dest_ranks.end(),
113 [&dest_ranks, &rank_to_neighbor, &recv_sizes, block_size](auto rank)
114 {
115 if (rank >= 0)
116 {
117 const int neighbor = rank_to_neighbor[rank];
118 recv_sizes[neighbor] += block_size;
119 }
120 });
121
122 // Compute receiving offsets
123 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
124 std::next(recv_offsets.begin(), 1));
125
126 // Compute map from receiving values to position in recv_values
127 comm_to_output.resize(recv_offsets.back() / block_size);
128 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
129 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
130 {
131 if (const std::int32_t rank = dest_ranks[i]; rank >= 0)
132 {
133 const int neighbor = rank_to_neighbor[rank];
134 int insert_pos = recv_offsets[neighbor] + recv_counter[neighbor];
135 comm_to_output[insert_pos / block_size] = i * block_size;
136 recv_counter[neighbor] += block_size;
137 }
138 }
139 }
140
141 std::vector<std::int32_t> send_sizes(out_ranks.size());
142 send_sizes.reserve(1);
143 {
144 // Compute map from parent mpi rank to neigbor rank for outgoing data
145 std::map<std::int32_t, std::int32_t> rank_to_neighbor;
146 for (std::size_t i = 0; i < out_ranks.size(); i++)
147 rank_to_neighbor[out_ranks[i]] = i;
148
149 // Compute send sizes
150 std::for_each(src_ranks.begin(), src_ranks.end(),
151 [&rank_to_neighbor, &send_sizes, block_size](auto rank)
152 { send_sizes[rank_to_neighbor[rank]] += block_size; });
153 }
154
155 // Compute sending offsets
156 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
157 std::partial_sum(send_sizes.begin(), send_sizes.end(),
158 std::next(send_offsets.begin(), 1));
159
160 std::stringstream cc;
161 // Send values to dest ranks
162 std::vector<T> values(recv_offsets.back());
163 values.reserve(1);
164 MPI_Neighbor_alltoallv(send_values.data(), send_sizes.data(),
165 send_offsets.data(), dolfinx::MPI::mpi_type<T>(),
166 values.data(), recv_sizes.data(), recv_offsets.data(),
167 dolfinx::MPI::mpi_type<T>(), reverse_comm);
168 MPI_Comm_free(&reverse_comm);
169
170 // Insert values received from neighborhood communicator in output span
171 std::fill(recv_values.begin(), recv_values.end(), T(0));
172 for (std::size_t i = 0; i < comm_to_output.size(); i++)
173 {
174 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
175 auto vals_from = std::next(values.begin(), i * block_size);
176 std::copy_n(vals_from, block_size, vals);
177 }
178};
179
188template <typename U, typename V, typename T>
189void interpolation_apply(const U& Pi, const V& data, std::span<T> coeffs,
190 int bs)
191{
192 static_assert(U::rank() == 2, "Must be rank 2");
193 static_assert(V::rank() == 2, "Must be rank 2");
194
195 // Compute coefficients = Pi * x (matrix-vector multiply)
196 if (bs == 1)
197 {
198 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
199 for (std::size_t i = 0; i < Pi.extent(0); ++i)
200 {
201 coeffs[i] = 0.0;
202 for (std::size_t k = 0; k < data.extent(1); ++k)
203 for (std::size_t j = 0; j < data.extent(0); ++j)
204 coeffs[i] += Pi(i, k * data.extent(0) + j) * data(j, k);
205 }
206 }
207 else
208 {
209 const std::size_t cols = Pi.extent(1);
210 assert(data.extent(0) == Pi.extent(1));
211 assert(data.extent(1) == bs);
212 for (int k = 0; k < bs; ++k)
213 {
214 for (std::size_t i = 0; i < Pi.extent(0); ++i)
215 {
216 T acc = 0;
217 for (std::size_t j = 0; j < cols; ++j)
218 acc += Pi(i, j) * data(j, k);
219 coeffs[bs * i + k] = acc;
220 }
221 }
222 }
223}
224
235template <typename T>
236void interpolate_same_map(Function<T>& u1, const Function<T>& u0,
237 std::span<const std::int32_t> cells)
238{
239 auto V0 = u0.function_space();
240 assert(V0);
241 auto V1 = u1.function_space();
242 assert(V1);
243 auto mesh = V0->mesh();
244 assert(mesh);
245
246 std::shared_ptr<const FiniteElement> element0 = V0->element();
247 assert(element0);
248 std::shared_ptr<const FiniteElement> element1 = V1->element();
249 assert(element1);
250
251 const int tdim = mesh->topology().dim();
252 auto map = mesh->topology().index_map(tdim);
253 assert(map);
254 std::span<T> u1_array = u1.x()->mutable_array();
255 std::span<const T> u0_array = u0.x()->array();
256
257 std::span<const std::uint32_t> cell_info;
258 if (element1->needs_dof_transformations()
259 or element0->needs_dof_transformations())
260 {
261 mesh->topology_mutable().create_entity_permutations();
262 cell_info = std::span(mesh->topology().get_cell_permutation_info());
263 }
264
265 // Get dofmaps
266 auto dofmap1 = V1->dofmap();
267 auto dofmap0 = V0->dofmap();
268
269 // Get block sizes and dof transformation operators
270 const int bs1 = dofmap1->bs();
271 const int bs0 = dofmap0->bs();
272 auto apply_dof_transformation
273 = element0->get_dof_transformation_function<T>(false, true, false);
274 auto apply_inverse_dof_transform
275 = element1->get_dof_transformation_function<T>(true, true, false);
276
277 // Create working array
278 std::vector<T> local0(element0->space_dimension());
279 std::vector<T> local1(element1->space_dimension());
280
281 // Create interpolation operator
282 const auto [i_m, im_shape]
283 = element1->create_interpolation_operator(*element0);
284
285 // Iterate over mesh and interpolate on each cell
286 for (auto c : cells)
287 {
288 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(c);
289 for (std::size_t i = 0; i < dofs0.size(); ++i)
290 for (int k = 0; k < bs0; ++k)
291 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
292
293 apply_dof_transformation(local0, cell_info, c, 1);
294
295 // FIXME: Get compile-time ranges from Basix
296 // Apply interpolation operator
297 std::fill(local1.begin(), local1.end(), 0);
298 for (std::size_t i = 0; i < im_shape[0]; ++i)
299 for (std::size_t j = 0; j < im_shape[1]; ++j)
300 local1[i] += i_m[im_shape[1] * i + j] * local0[j];
301
302 apply_inverse_dof_transform(local1, cell_info, c, 1);
303
304 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(c);
305 for (std::size_t i = 0; i < dofs1.size(); ++i)
306 for (int k = 0; k < bs1; ++k)
307 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
308 }
309}
310
320template <typename T>
321void interpolate_nonmatching_maps(Function<T>& u1, const Function<T>& u0,
322 std::span<const std::int32_t> cells)
323{
324 // Get mesh
325 auto V0 = u0.function_space();
326 assert(V0);
327 auto mesh = V0->mesh();
328 assert(mesh);
329
330 // Mesh dims
331 const int tdim = mesh->topology().dim();
332 const int gdim = mesh->geometry().dim();
333
334 // Get elements
335 auto V1 = u1.function_space();
336 assert(V1);
337 std::shared_ptr<const FiniteElement> element0 = V0->element();
338 assert(element0);
339 std::shared_ptr<const FiniteElement> element1 = V1->element();
340 assert(element1);
341
342 std::span<const std::uint32_t> cell_info;
343 if (element1->needs_dof_transformations()
344 or element0->needs_dof_transformations())
345 {
346 mesh->topology_mutable().create_entity_permutations();
347 cell_info = std::span(mesh->topology().get_cell_permutation_info());
348 }
349
350 // Get dofmaps
351 auto dofmap0 = V0->dofmap();
352 auto dofmap1 = V1->dofmap();
353
354 const auto [X, Xshape] = element1->interpolation_points();
355
356 // Get block sizes and dof transformation operators
357 const int bs0 = element0->block_size();
358 const int bs1 = element1->block_size();
359 const auto apply_dof_transformation0
360 = element0->get_dof_transformation_function<double>(false, false, false);
361 const auto apply_inverse_dof_transform1
362 = element1->get_dof_transformation_function<T>(true, true, false);
363
364 // Get sizes of elements
365 const std::size_t dim0 = element0->space_dimension() / bs0;
366 const std::size_t value_size_ref0 = element0->reference_value_size() / bs0;
367 const std::size_t value_size0 = element0->value_size() / bs0;
368
369 // Get geometry data
370 const CoordinateElement& cmap = mesh->geometry().cmap();
371 const graph::AdjacencyList<std::int32_t>& x_dofmap
372 = mesh->geometry().dofmap();
373 const std::size_t num_dofs_g = cmap.dim();
374 std::span<const double> x_g = mesh->geometry().x();
375
376 namespace stdex = std::experimental;
377 using cmdspan2_t
378 = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
379 using cmdspan4_t
380 = stdex::mdspan<const double, stdex::dextents<std::size_t, 4>>;
381 using mdspan2_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
382 using mdspan3_t = stdex::mdspan<double, stdex::dextents<std::size_t, 3>>;
383 using mdspan3T_t = stdex::mdspan<T, stdex::dextents<std::size_t, 3>>;
384
385 // Evaluate coordinate map basis at reference interpolation points
386 const std::array<std::size_t, 4> phi_shape
387 = cmap.tabulate_shape(1, Xshape[0]);
388 std::vector<double> phi_b(
389 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
390 cmdspan4_t phi(phi_b.data(), phi_shape);
391 cmap.tabulate(1, X, Xshape, phi_b);
392
393 // Evaluate v basis functions at reference interpolation points
394 const auto [_basis_derivatives_reference0, b0shape]
395 = element0->tabulate(X, Xshape, 0);
396 cmdspan4_t basis_derivatives_reference0(_basis_derivatives_reference0.data(),
397 b0shape);
398
399 // Create working arrays
400 std::vector<T> local1(element1->space_dimension());
401 std::vector<T> coeffs0(element0->space_dimension());
402
403 std::vector<double> basis0_b(Xshape[0] * dim0 * value_size0);
404 mdspan3_t basis0(basis0_b.data(), Xshape[0], dim0, value_size0);
405
406 std::vector<double> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
407 mdspan3_t basis_reference0(basis_reference0_b.data(), Xshape[0], dim0,
408 value_size_ref0);
409
410 std::vector<T> values0_b(Xshape[0] * 1 * element1->value_size());
411 mdspan3T_t values0(values0_b.data(), Xshape[0], 1, element1->value_size());
412
413 std::vector<T> mapped_values_b(Xshape[0] * 1 * element1->value_size());
414 mdspan3T_t mapped_values0(mapped_values_b.data(), Xshape[0], 1,
415 element1->value_size());
416
417 std::vector<double> coord_dofs_b(num_dofs_g * gdim);
418 mdspan2_t coord_dofs(coord_dofs_b.data(), num_dofs_g, gdim);
419
420 std::vector<double> J_b(Xshape[0] * gdim * tdim);
421 mdspan3_t J(J_b.data(), Xshape[0], gdim, tdim);
422 std::vector<double> K_b(Xshape[0] * tdim * gdim);
423 mdspan3_t K(K_b.data(), Xshape[0], tdim, gdim);
424 std::vector<double> detJ(Xshape[0]);
425 std::vector<double> det_scratch(2 * gdim * tdim);
426
427 // Get interpolation operator
428 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
429 cmdspan2_t Pi_1(_Pi_1.data(), pi_shape);
430
431 using u_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
432 using U_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
433 using J_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
434 using K_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
435 auto push_forward_fn0
436 = element0->basix_element().map_fn<u_t, U_t, J_t, K_t>();
437
438 using v_t = stdex::mdspan<const T, stdex::dextents<std::size_t, 2>>;
439 using V_t = stdex::mdspan<T, stdex::dextents<std::size_t, 2>>;
440 auto pull_back_fn1 = element1->basix_element().map_fn<V_t, v_t, K_t, J_t>();
441
442 // Iterate over mesh and interpolate on each cell
443 std::span<const T> array0 = u0.x()->array();
444 std::span<T> array1 = u1.x()->mutable_array();
445 for (auto c : cells)
446 {
447 // Get cell geometry (coordinate dofs)
448 auto x_dofs = x_dofmap.links(c);
449 for (std::size_t i = 0; i < num_dofs_g; ++i)
450 {
451 const int pos = 3 * x_dofs[i];
452 for (std::size_t j = 0; j < gdim; ++j)
453 coord_dofs(i, j) = x_g[pos + j];
454 }
455
456 // Compute Jacobians and reference points for current cell
457 std::fill(J_b.begin(), J_b.end(), 0);
458 for (std::size_t p = 0; p < Xshape[0]; ++p)
459 {
460 auto dphi = stdex::submdspan(phi, std::pair(1, tdim + 1), p,
461 stdex::full_extent, 0);
462
463 auto _J = stdex::submdspan(J, p, stdex::full_extent, stdex::full_extent);
464 cmap.compute_jacobian(dphi, coord_dofs, _J);
465 auto _K = stdex::submdspan(K, p, stdex::full_extent, stdex::full_extent);
466 cmap.compute_jacobian_inverse(_J, _K);
467 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
468 }
469
470 // Copy evaluated basis on reference, apply DOF transformations, and
471 // push forward to physical element
472 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
473 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
474 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
475 basis_reference0(k0, k1, k2)
476 = basis_derivatives_reference0(0, k0, k1, k2);
477
478 for (std::size_t p = 0; p < Xshape[0]; ++p)
479 {
480 apply_dof_transformation0(
481 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
482 dim0 * value_size_ref0),
483 cell_info, c, value_size_ref0);
484 }
485
486 for (std::size_t i = 0; i < basis0.extent(0); ++i)
487 {
488 auto _u
489 = stdex::submdspan(basis0, i, stdex::full_extent, stdex::full_extent);
490 auto _U = stdex::submdspan(basis_reference0, i, stdex::full_extent,
491 stdex::full_extent);
492 auto _K = stdex::submdspan(K, i, stdex::full_extent, stdex::full_extent);
493 auto _J = stdex::submdspan(J, i, stdex::full_extent, stdex::full_extent);
494 push_forward_fn0(_u, _U, _J, detJ[i], _K);
495 }
496
497 // Copy expansion coefficients for v into local array
498 const int dof_bs0 = dofmap0->bs();
499 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(c);
500 for (std::size_t i = 0; i < dofs0.size(); ++i)
501 for (int k = 0; k < dof_bs0; ++k)
502 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
503
504 // Evaluate v at the interpolation points (physical space values)
505 for (std::size_t p = 0; p < Xshape[0]; ++p)
506 {
507 for (int k = 0; k < bs0; ++k)
508 {
509 for (std::size_t j = 0; j < value_size0; ++j)
510 {
511 T acc = 0;
512 for (std::size_t i = 0; i < dim0; ++i)
513 acc += coeffs0[bs0 * i + k] * basis0(p, i, j);
514 values0(p, 0, j * bs0 + k) = acc;
515 }
516 }
517 }
518
519 // Pull back the physical values to the u reference
520 for (std::size_t i = 0; i < values0.extent(0); ++i)
521 {
522 auto _u = stdex::submdspan(values0, i, stdex::full_extent,
523 stdex::full_extent);
524 auto _U = stdex::submdspan(mapped_values0, i, stdex::full_extent,
525 stdex::full_extent);
526 auto _K = stdex::submdspan(K, i, stdex::full_extent, stdex::full_extent);
527 auto _J = stdex::submdspan(J, i, stdex::full_extent, stdex::full_extent);
528 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
529 }
530
531 auto values = stdex::submdspan(mapped_values0, stdex::full_extent, 0,
532 stdex::full_extent);
533 interpolation_apply(Pi_1, values, std::span(local1), bs1);
534 apply_inverse_dof_transform1(local1, cell_info, c, 1);
535
536 // Copy local coefficients to the correct position in u dof array
537 const int dof_bs1 = dofmap1->bs();
538 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(c);
539 for (std::size_t i = 0; i < dofs1.size(); ++i)
540 for (int k = 0; k < dof_bs1; ++k)
541 array1[dof_bs1 * dofs1[i] + k] = local1[dof_bs1 * i + k];
542 }
543}
544//----------------------------------------------------------------------------
545} // namespace impl
546
560template <typename T>
561void interpolate(Function<T>& u, std::span<const T> f,
562 std::array<std::size_t, 2> fshape,
563 std::span<const std::int32_t> cells)
564{
565 namespace stdex = std::experimental;
566 using cmdspan2_t
567 = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
568 using cmdspan4_t
569 = stdex::mdspan<const double, stdex::dextents<std::size_t, 4>>;
570 using mdspan2_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
571 using mdspan3_t = stdex::mdspan<double, stdex::dextents<std::size_t, 3>>;
572
573 std::shared_ptr<const FiniteElement> element = u.function_space()->element();
574 assert(element);
575 const int element_bs = element->block_size();
576 if (int num_sub = element->num_sub_elements();
577 num_sub > 0 and num_sub != element_bs)
578 {
579 throw std::runtime_error("Cannot directly interpolate a mixed space. "
580 "Interpolate into subspaces.");
581 }
582
583 if (fshape[0] != (std::size_t)element->value_size())
584 throw std::runtime_error("Interpolation data has the wrong shape/size.");
585
586 // Get mesh
587 assert(u.function_space());
588 auto mesh = u.function_space()->mesh();
589 assert(mesh);
590
591 const int gdim = mesh->geometry().dim();
592 const int tdim = mesh->topology().dim();
593
594 std::span<const std::uint32_t> cell_info;
595 if (element->needs_dof_transformations())
596 {
597 mesh->topology_mutable().create_entity_permutations();
598 cell_info = std::span(mesh->topology().get_cell_permutation_info());
599 }
600
601 const std::size_t f_shape1 = f.size() / element->value_size();
602 stdex::mdspan<const T, stdex::dextents<std::size_t, 2>> _f(f.data(), fshape);
603
604 // Get dofmap
605 const auto dofmap = u.function_space()->dofmap();
606 assert(dofmap);
607 const int dofmap_bs = dofmap->bs();
608
609 // Loop over cells and compute interpolation dofs
610 const int num_scalar_dofs = element->space_dimension() / element_bs;
611 const int value_size = element->value_size() / element_bs;
612
613 std::span<T> coeffs = u.x()->mutable_array();
614 std::vector<T> _coeffs(num_scalar_dofs);
615
616 // This assumes that any element with an identity interpolation matrix
617 // is a point evaluation
618 if (element->map_ident() && element->interpolation_ident())
619 {
620 // Point evaluation element *and* the geometric map is the identity,
621 // e.g. not Piola mapped
622
623 auto apply_inv_transpose_dof_transformation
624 = element->get_dof_transformation_function<T>(true, true, true);
625
626 // Loop over cells
627 for (std::size_t c = 0; c < cells.size(); ++c)
628 {
629 const std::int32_t cell = cells[c];
630 std::span<const std::int32_t> dofs = dofmap->cell_dofs(cell);
631 for (int k = 0; k < element_bs; ++k)
632 {
633 // num_scalar_dofs is the number of interpolation points per
634 // cell in this case (interpolation matrix is identity)
635 std::copy_n(std::next(f.begin(), k * f_shape1 + c * num_scalar_dofs),
636 num_scalar_dofs, _coeffs.begin());
637 apply_inv_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
638 for (int i = 0; i < num_scalar_dofs; ++i)
639 {
640 const int dof = i * element_bs + k;
641 std::div_t pos = std::div(dof, dofmap_bs);
642 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
643 }
644 }
645 }
646 }
647 else if (element->map_ident())
648 {
649 // Not a point evaluation, but the geometric map is the identity,
650 // e.g. not Piola mapped
651
652 const int element_vs = element->value_size() / element_bs;
653
654 if (element_vs > 1 && element_bs > 1)
655 {
656 throw std::runtime_error(
657 "Interpolation into this element not supported.");
658 }
659
660 // Get interpolation operator
661 const auto [_Pi, pi_shape] = element->interpolation_operator();
662 cmdspan2_t Pi(_Pi.data(), pi_shape);
663 const std::size_t num_interp_points = Pi.extent(1);
664 assert(Pi.extent(0) == num_scalar_dofs);
665
666 auto apply_inv_transpose_dof_transformation
667 = element->get_dof_transformation_function<T>(true, true, true);
668
669 // Loop over cells
670 std::vector<T> ref_data_b(num_interp_points);
671 stdex::mdspan<T, stdex::extents<std::size_t, stdex::dynamic_extent, 1>>
672 ref_data(ref_data_b.data(), num_interp_points, 1);
673 for (std::size_t c = 0; c < cells.size(); ++c)
674 {
675 const std::int32_t cell = cells[c];
676 std::span<const std::int32_t> dofs = dofmap->cell_dofs(cell);
677 for (int k = 0; k < element_bs; ++k)
678 {
679 for (int i = 0; i < element_vs; ++i)
680 {
681 std::copy_n(
682 std::next(f.begin(), (i + k) * f_shape1
683 + c * num_interp_points / element_vs),
684 num_interp_points / element_vs,
685 std::next(ref_data_b.begin(),
686 i * num_interp_points / element_vs));
687 }
688 impl::interpolation_apply(Pi, ref_data, std::span(_coeffs), 1);
689 apply_inv_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
690 for (int i = 0; i < num_scalar_dofs; ++i)
691 {
692 const int dof = i * element_bs + k;
693 std::div_t pos = std::div(dof, dofmap_bs);
694 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
695 }
696 }
697 }
698 }
699 else
700 {
701 // Get the interpolation points on the reference cells
702 const auto [X, Xshape] = element->interpolation_points();
703 if (X.empty())
704 {
705 throw std::runtime_error(
706 "Interpolation into this space is not yet supported.");
707 }
708
709 if (_f.extent(1) != cells.size() * Xshape[0])
710 throw std::runtime_error("Interpolation data has the wrong shape.");
711
712 // Get coordinate map
713 const CoordinateElement& cmap = mesh->geometry().cmap();
714
715 // Get geometry data
717 = mesh->geometry().dofmap();
718 const int num_dofs_g = cmap.dim();
719 std::span<const double> x_g = mesh->geometry().x();
720
721 // Create data structures for Jacobian info
722 std::vector<double> J_b(Xshape[0] * gdim * tdim);
723 mdspan3_t J(J_b.data(), Xshape[0], gdim, tdim);
724 std::vector<double> K_b(Xshape[0] * tdim * gdim);
725 mdspan3_t K(K_b.data(), Xshape[0], tdim, gdim);
726 std::vector<double> detJ(Xshape[0]);
727 std::vector<double> det_scratch(2 * gdim * tdim);
728
729 std::vector<double> coord_dofs_b(num_dofs_g * gdim);
730 mdspan2_t coord_dofs(coord_dofs_b.data(), num_dofs_g, gdim);
731
732 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size);
733 stdex::mdspan<T, stdex::dextents<std::size_t, 3>> ref_data(
734 ref_data_b.data(), Xshape[0], 1, value_size);
735
736 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
737 stdex::mdspan<T, stdex::dextents<std::size_t, 3>> _vals(
738 _vals_b.data(), Xshape[0], 1, value_size);
739
740 // Tabulate 1st derivative of shape functions at interpolation
741 // coords
742 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
743 std::vector<double> phi_b(
744 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
745 cmdspan4_t phi(phi_b.data(), phi_shape);
746 cmap.tabulate(1, X, Xshape, phi_b);
747 auto dphi = stdex::submdspan(phi, std::pair(1, tdim + 1),
748 stdex::full_extent, stdex::full_extent, 0);
749
750 const std::function<void(const std::span<T>&,
751 const std::span<const std::uint32_t>&,
752 std::int32_t, int)>
753 apply_inverse_transpose_dof_transformation
754 = element->get_dof_transformation_function<T>(true, true);
755
756 // Get interpolation operator
757 const auto [_Pi, pi_shape] = element->interpolation_operator();
758 cmdspan2_t Pi(_Pi.data(), pi_shape);
759
760 namespace stdex = std::experimental;
761 using u_t = stdex::mdspan<const T, stdex::dextents<std::size_t, 2>>;
762 using U_t = stdex::mdspan<T, stdex::dextents<std::size_t, 2>>;
763 using J_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
764 using K_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
765 auto pull_back_fn = element->basix_element().map_fn<U_t, u_t, J_t, K_t>();
766
767 for (std::size_t c = 0; c < cells.size(); ++c)
768 {
769 const std::int32_t cell = cells[c];
770 auto x_dofs = x_dofmap.links(cell);
771 for (int i = 0; i < num_dofs_g; ++i)
772 {
773 const int pos = 3 * x_dofs[i];
774 for (int j = 0; j < gdim; ++j)
775 coord_dofs(i, j) = x_g[pos + j];
776 }
777
778 // Compute J, detJ and K
779 std::fill(J_b.begin(), J_b.end(), 0);
780 for (std::size_t p = 0; p < Xshape[0]; ++p)
781 {
782 auto _dphi
783 = stdex::submdspan(dphi, stdex::full_extent, p, stdex::full_extent);
784 auto _J
785 = stdex::submdspan(J, p, stdex::full_extent, stdex::full_extent);
786 cmap.compute_jacobian(_dphi, coord_dofs, _J);
787 auto _K
788 = stdex::submdspan(K, p, stdex::full_extent, stdex::full_extent);
789 cmap.compute_jacobian_inverse(_J, _K);
790 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
791 }
792
793 std::span<const std::int32_t> dofs = dofmap->cell_dofs(cell);
794 for (int k = 0; k < element_bs; ++k)
795 {
796 // Extract computed expression values for element block k
797 for (int m = 0; m < value_size; ++m)
798 {
799 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
800 {
801 _vals(k0, 0, m)
802 = f[f_shape1 * (k * value_size + m) + c * Xshape[0] + k0];
803 }
804 }
805
806 // Get element degrees of freedom for block
807 for (std::size_t i = 0; i < Xshape[0]; ++i)
808 {
809 auto _u = stdex::submdspan(_vals, i, stdex::full_extent,
810 stdex::full_extent);
811 auto _U = stdex::submdspan(ref_data, i, stdex::full_extent,
812 stdex::full_extent);
813 auto _K
814 = stdex::submdspan(K, i, stdex::full_extent, stdex::full_extent);
815 auto _J
816 = stdex::submdspan(J, i, stdex::full_extent, stdex::full_extent);
817 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
818 }
819
820 auto ref = stdex::submdspan(ref_data, stdex::full_extent, 0,
821 stdex::full_extent);
822 impl::interpolation_apply(Pi, ref, std::span(_coeffs), element_bs);
823 apply_inverse_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
824
825 // Copy interpolation dofs into coefficient vector
826 assert(_coeffs.size() == num_scalar_dofs);
827 for (int i = 0; i < num_scalar_dofs; ++i)
828 {
829 const int dof = i * element_bs + k;
830 std::div_t pos = std::div(dof, dofmap_bs);
831 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
832 }
833 }
834 }
835 }
836}
837
838namespace impl
839{
840
845template <typename T>
846void interpolate_nonmatching_meshes(Function<T>& u, const Function<T>& v,
847 std::span<const std::int32_t> cells)
848{
849 assert(u.function_space());
850 assert(v.function_space());
851
852 std::shared_ptr<const mesh::Mesh> mesh = u.function_space()->mesh();
853 std::shared_ptr<const mesh::Mesh> mesh_v = v.function_space()->mesh();
854
855 assert(mesh);
856
857 int result;
858 MPI_Comm_compare(mesh->comm(), mesh_v->comm(), &result);
859
860 if (result == MPI_UNEQUAL)
861 throw std::runtime_error("Interpolation on different meshes is only "
862 "supported with the same communicator.");
863
864 MPI_Comm comm = mesh->comm();
865 const int tdim = mesh->topology().dim();
866 const auto cell_map = mesh->topology().index_map(tdim);
867
868 std::shared_ptr<const FiniteElement> element_u
869 = u.function_space()->element();
870 const std::size_t value_size = element_u->value_size();
871
872 // Collect all the points at which values are needed to define the
873 // interpolating function
874 std::vector<double> x;
875 {
876 const std::vector<double> coords_b
877 = fem::interpolation_coords(*element_u, *mesh, cells);
878
879 namespace stdex = std::experimental;
880 using cmdspan2_t
881 = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
882 using mdspan2_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
883 cmdspan2_t coords(coords_b.data(), 3, coords_b.size() / 3);
884 // Transpose interpolation coords
885 x.resize(coords.size());
886 mdspan2_t _x(x.data(), coords_b.size() / 3, 3);
887 for (std::size_t j = 0; j < coords.extent(1); ++j)
888 for (std::size_t i = 0; i < 3; ++i)
889 _x(j, i) = coords(i, j);
890 }
891
892 // Determine ownership of each point
893 auto [dest_ranks, src_ranks, received_points, evaluation_cells]
894 = geometry::determine_point_ownership(*mesh_v, x);
895
896 // Evaluate the interpolating function where possible
897 std::vector<T> send_values(received_points.size() / 3 * value_size);
898 v.eval(received_points, {received_points.size() / 3, (std::size_t)3},
899 evaluation_cells, send_values,
900 {received_points.size() / 3, (std::size_t)value_size});
901
902 // Send values back to owning process
903 std::array<std::size_t, 2> v_shape = {src_ranks.size(), value_size};
904 std::vector<T> values_b(dest_ranks.size() * value_size);
905 impl::scatter_values(comm, src_ranks, dest_ranks,
906 std::span<const T>(send_values), v_shape,
907 std::span<T>(values_b));
908
909 // Transpose received data
910 namespace stdex = std::experimental;
911 stdex::mdspan<const T, stdex::dextents<std::size_t, 2>> values(
912 values_b.data(), dest_ranks.size(), value_size);
913
914 std::vector<T> valuesT_b(value_size * dest_ranks.size());
915 stdex::mdspan<T, stdex::dextents<std::size_t, 2>> valuesT(
916 valuesT_b.data(), value_size, dest_ranks.size());
917 for (std::size_t i = 0; i < values.extent(0); ++i)
918 for (std::size_t j = 0; j < values.extent(1); ++j)
919 valuesT(j, i) = values(i, j);
920
921 // Call local interpolation operator
922 fem::interpolate<T>(u, std::span(valuesT_b.data(), valuesT_b.size()),
923 {valuesT.extent(0), valuesT.extent(1)}, cells);
924}
925
926} // namespace impl
927//----------------------------------------------------------------------------
932template <typename T>
934 std::span<const std::int32_t> cells)
935{
936 assert(u.function_space());
937 assert(v.function_space());
938 std::shared_ptr<const mesh::Mesh> mesh = u.function_space()->mesh();
939 assert(mesh);
940
941 auto cell_map0 = mesh->topology().index_map(mesh->topology().dim());
942 assert(cell_map0);
943 std::size_t num_cells0 = cell_map0->size_local() + cell_map0->num_ghosts();
944 if (u.function_space() == v.function_space() and cells.size() == num_cells0)
945 {
946 // Same function spaces and on whole mesh
947 std::span<T> u1_array = u.x()->mutable_array();
948 std::span<const T> u0_array = v.x()->array();
949 std::copy(u0_array.begin(), u0_array.end(), u1_array.begin());
950 }
951 else
952 {
953 // Get mesh and check that functions share the same mesh
954 if (auto mesh_v = v.function_space()->mesh(); mesh != mesh_v)
955 impl::interpolate_nonmatching_meshes(u, v, cells);
956 else
957 {
958
959 // Get elements and check value shape
960 auto element0 = v.function_space()->element();
961 assert(element0);
962 auto element1 = u.function_space()->element();
963 assert(element1);
964 if (element0->value_shape().size() != element1->value_shape().size()
965 or !std::equal(element0->value_shape().begin(),
966 element0->value_shape().end(),
967 element1->value_shape().begin()))
968 {
969 throw std::runtime_error(
970 "Interpolation: elements have different value dimensions");
971 }
972
973 if (element1 == element0 or *element1 == *element0)
974 {
975 // Same element, different dofmaps (or just a subset of cells)
976
977 const int tdim = mesh->topology().dim();
978 auto cell_map = mesh->topology().index_map(tdim);
979 assert(cell_map);
980
981 assert(element1->block_size() == element0->block_size());
982
983 // Get dofmaps
984 std::shared_ptr<const DofMap> dofmap0 = v.function_space()->dofmap();
985 assert(dofmap0);
986 std::shared_ptr<const DofMap> dofmap1 = u.function_space()->dofmap();
987 assert(dofmap1);
988
989 std::span<T> u1_array = u.x()->mutable_array();
990 std::span<const T> u0_array = v.x()->array();
991
992 // Iterate over mesh and interpolate on each cell
993 const int bs0 = dofmap0->bs();
994 const int bs1 = dofmap1->bs();
995 for (auto c : cells)
996 {
997 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(c);
998 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(c);
999 assert(bs0 * dofs0.size() == bs1 * dofs1.size());
1000 for (std::size_t i = 0; i < dofs0.size(); ++i)
1001 {
1002 for (int k = 0; k < bs0; ++k)
1003 {
1004 int index = bs0 * i + k;
1005 std::div_t dv1 = std::div(index, bs1);
1006 u1_array[bs1 * dofs1[dv1.quot] + dv1.rem]
1007 = u0_array[bs0 * dofs0[i] + k];
1008 }
1009 }
1010 }
1011 }
1012 else if (element1->map_type() == element0->map_type())
1013 {
1014 // Different elements, same basis function map type
1015 impl::interpolate_same_map(u, v, cells);
1016 }
1017 else
1018 {
1019 // Different elements with different maps for basis functions
1020 impl::interpolate_nonmatching_maps(u, v, cells);
1021 }
1022 }
1023 }
1024}
1025
1026} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
A CoordinateElement manages coordinate mappings for isoparametric cells.
Definition: CoordinateElement.h:32
static void compute_jacobian(const U &dphi, const V &cell_geometry, W &&J)
Compute Jacobian for a cell with given geometry using the basis functions and first order derivatives...
Definition: CoordinateElement.h:100
static void compute_jacobian_inverse(const U &J, V &&K)
Compute the inverse of the Jacobian.
Definition: CoordinateElement.h:109
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 FiniteElement::tabulate
Definition: CoordinateElement.cpp:42
void tabulate(int nd, std::span< const double > X, std::array< std::size_t, 2 > shape, std::span< double > basis) const
Evaluate basis values and derivatives at set of points.
Definition: CoordinateElement.cpp:47
int dim() const
The dimension of the geometry element space.
Definition: CoordinateElement.cpp:183
static double compute_jacobian_determinant(const U &J, std::span< typename U::value_type > w)
Compute the determinant of the Jacobian.
Definition: CoordinateElement.h:126
This class represents a function in a finite element function space , given by.
Definition: Function.h:43
std::shared_ptr< const FunctionSpace > function_space() const
Access the function space.
Definition: Function.h:134
std::shared_ptr< const la::Vector< T > > x() const
Underlying vector.
Definition: Function.h:140
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:27
std::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:109
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition: MPI.cpp:63
Finite element method functionality.
Definition: assemble_matrix_impl.h:25
void interpolate(Function< T > &u, std::span< const T > f, std::array< std::size_t, 2 > fshape, std::span< const std::int32_t > cells)
Interpolate an expression f(x) in a finite element space.
Definition: interpolate.h:561
std::vector< double > interpolation_coords(const fem::FiniteElement &element, const mesh::Mesh &mesh, std::span< const std::int32_t > cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition: interpolate.cpp:16