DDC 0.16.0
Loading...
Searching...
No Matches
spline_builder.hpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <array>
8#include <cassert>
9#include <cstddef>
10#include <memory>
11#include <optional>
12#include <stdexcept>
13#include <string>
14#include <tuple>
15#include <utility>
16
17#include <ddc/ddc.hpp>
18
19#include <Kokkos_Core.hpp>
20
21#include "deriv.hpp"
22#include "integrals.hpp"
23#include "math_tools.hpp"
27
28namespace ddc {
29
30/**
31 * @brief An enum determining the backend solver of a SplineBuilder or SplineBuilder2d.
32 *
33 * An enum determining the backend solver of a SplineBuilder or SplineBuilder2d.
34 */
35enum class SplineSolver {
36 GINKGO, ///< Enum member to identify the Ginkgo-based solver (iterative method)
37 LAPACK ///< Enum member to identify the LAPACK-based solver (direct method)
38};
39
40/**
41 * @brief A class for creating a spline approximation of a function.
42 *
43 * A class which contains an operator () which can be used to build a spline approximation
44 * of a function. A spline approximation is represented by coefficients stored in a Chunk
45 * of B-splines. The spline is constructed such that it respects the closure relations
46 * SBCLower and SBCUpper, and it interpolates the function at the points on the interpolation_discrete_dimension
47 * associated with interpolation_discrete_dimension_type.
48 * @tparam ExecSpace The Kokkos execution space on which the spline approximation is performed.
49 * @tparam MemorySpace The Kokkos memory space on which the data (interpolation function and splines coefficients) is stored.
50 * @tparam BSplines The discrete dimension representing the B-splines.
51 * @tparam InterpolationDDim The discrete dimension on which interpolation points are defined.
52 * @tparam SBCLower The lower closure relation.
53 * @tparam SBCUpper The upper closure relation.
54 * @tparam Solver The SplineSolver giving the backend used to perform the spline approximation.
55 */
56template <
57 class ExecSpace,
58 class MemorySpace,
59 class BSplines,
60 class InterpolationDDim,
61 ddc::SplineBuilderClosure SBCLower,
62 ddc::SplineBuilderClosure SBCUpper,
63 SplineSolver Solver>
64class SplineBuilder
65{
66 static_assert(
67 (BSplines::is_periodic() && (SBCLower == ddc::SplineBuilderClosure::PERIODIC)
69 || (!BSplines::is_periodic() && (SBCLower != ddc::SplineBuilderClosure::PERIODIC)
70 && (SBCUpper != ddc::SplineBuilderClosure::PERIODIC)));
71
72public:
73 /// @brief The type of the Kokkos execution space used by this class.
74 using exec_space = ExecSpace;
75
76 /// @brief The type of the Kokkos memory space used by this class.
77 using memory_space = MemorySpace;
78
79 /// @brief The type of the interpolation continuous dimension (continuous dimension of interest) used by this class.
80 using continuous_dimension_type = InterpolationDDim::continuous_dimension_type;
81
82 /// @brief The type of the interpolation discrete dimension (discrete dimension of interest) used by this class.
83 using interpolation_discrete_dimension_type = InterpolationDDim;
84
85 /// @brief The discrete dimension representing the B-splines.
86 using bsplines_type = BSplines;
87
88 /// @brief The type of the Deriv dimension at the boundaries.
89 using deriv_type = ddc::Deriv<continuous_dimension_type>;
90
91 /// @brief The type of the domain for the 1D interpolation mesh used by this class.
92 using interpolation_domain_type = ddc::DiscreteDomain<interpolation_discrete_dimension_type>;
93
94 /**
95 * @brief The type of the whole domain representing interpolation points.
96 *
97 * @tparam The batched discrete domain on which the interpolation points are defined.
98 */
99 template <concepts::discrete_domain BatchedInterpolationDDom>
100 using batched_interpolation_domain_type = BatchedInterpolationDDom;
101
102 /**
103 * @brief The type of the batch domain (obtained by removing the dimension of interest
104 * from the whole domain).
105 *
106 * @tparam The batched discrete domain on which the interpolation points are defined.
107 *
108 * Example: For batched_interpolation_domain_type = DiscreteDomain<X,Y,Z> and a dimension of interest Y,
109 * this is DiscreteDomain<X,Z>
110 */
111 template <concepts::discrete_domain BatchedInterpolationDDom>
112 using batch_domain_type = ddc::
113 remove_dims_of_t<BatchedInterpolationDDom, interpolation_discrete_dimension_type>;
114
115 /**
116 * @brief The type of the whole spline domain (cartesian product of 1D spline domain
117 * and batch domain) preserving the underlying memory layout (order of dimensions).
118 *
119 * @tparam The batched discrete domain on which the interpolation points are defined.
120 *
121 * Example: For batched_interpolation_domain_type = DiscreteDomain<X,Y,Z> and a dimension of interest Y
122 * (associated to a B-splines tag BSplinesY), this is DiscreteDomain<X,BSplinesY,Z>.
123 */
124 template <concepts::discrete_domain BatchedInterpolationDDom>
125 using batched_spline_domain_type = ddc::replace_dim_of_t<
126 BatchedInterpolationDDom,
127 interpolation_discrete_dimension_type,
128 bsplines_type>;
129
130private:
131 /**
132 * @brief The type of the whole spline domain (cartesian product of the 1D spline domain
133 * and the batch domain) with 1D spline dimension being the leading dimension.
134 *
135 * @tparam The batched discrete domain on which the interpolation points are defined.
136 *
137 * Example: For batched_interpolation_domain_type = DiscreteDomain<X,Y,Z> and a dimension of interest Y
138 * (associated to a B-splines tag BSplinesY), this is DiscreteDomain<BSplinesY,X,Z>.
139 */
140 template <concepts::discrete_domain BatchedInterpolationDDom>
141 using batched_spline_tr_domain_type
142 = ddc::detail::convert_type_seq_to_discrete_domain_t<ddc::type_seq_merge_t<
143 ddc::detail::TypeSeq<bsplines_type>,
144 ddc::type_seq_remove_t<
145 ddc::to_type_seq_t<BatchedInterpolationDDom>,
146 ddc::detail::TypeSeq<interpolation_discrete_dimension_type>>>>;
147
148public:
149 /**
150 * @brief The type of the whole Deriv domain (cartesian product of 1D Deriv domain
151 * and batch domain) preserving the underlying memory layout (order of dimensions).
152 *
153 * @tparam The batched discrete domain on which the interpolation points are defined.
154 *
155 * Example: For batched_interpolation_domain_type = DiscreteDomain<X,Y,Z> and a dimension of interest Y,
156 * this is DiscreteDomain<X,Deriv<Y>,Z>
157 */
158 template <concepts::discrete_domain BatchedInterpolationDDom>
159 using batched_derivs_domain_type = ddc::replace_dim_of_t<
160 BatchedInterpolationDDom,
161 interpolation_discrete_dimension_type,
162 deriv_type>;
163
164 /// @brief Indicates if the degree of the splines is odd or even.
165 static constexpr bool s_odd = BSplines::degree() % 2;
166
167 /// @brief The number of equations defining the closure relation at the lower bound.
168 static constexpr int s_nbe_xmin = n_boundary_equations(SBCLower, BSplines::degree());
169
170 /// @brief The number of equations defining the closure relation at the upper bound.
171 static constexpr int s_nbe_xmax = n_boundary_equations(SBCUpper, BSplines::degree());
172
173 /// @brief The number of input values defining the closure relation at the lower bound.
174 static constexpr int s_nbv_xmin = SBCLower == SplineBuilderClosure::HOMOGENEOUS_HERMITE
175 ? 0
176 : n_boundary_equations(SBCLower, BSplines::degree());
177
178 /// @brief The number of input values defining the closure relation at the upper bound.
179 static constexpr int s_nbv_xmax = SBCUpper == SplineBuilderClosure::HOMOGENEOUS_HERMITE
180 ? 0
181 : n_boundary_equations(SBCUpper, BSplines::degree());
182
183 /// @brief The closure relation implemented at the lower bound.
184 static constexpr ddc::SplineBuilderClosure s_sbc_xmin = SBCLower;
185
186 /// @brief The closure relation implemented at the upper bound.
187 static constexpr ddc::SplineBuilderClosure s_sbc_xmax = SBCUpper;
188
189 /// @brief The SplineSolver giving the backend used to perform the spline approximation.
190 static constexpr SplineSolver s_spline_solver = Solver;
191
192private:
193 interpolation_domain_type m_interpolation_domain;
194
195 int m_offset = 0;
196
197 Real m_dx; // average cell size for normalization of derivatives
198
199 // interpolator specific
200 std::unique_ptr<ddc::detail::SplinesLinearProblem<exec_space>> m_matrix;
201
202 std::string m_label;
203
204 /// Calculate offset so that the matrix is diagonally dominant
205 void compute_offset(interpolation_domain_type const& interpolation_domain, int& offset);
206
207public:
208 /**
209 * @brief Build a SplineBuilder acting on interpolation_domain.
210 *
211 * @param label A label used to tag parallel regions and memory allocations for profiling.
212 *
213 * @param interpolation_domain The domain on which the interpolation points are defined.
214 *
215 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
216 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
217 * by the linear solver one-after-the-other).
218 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
219 *
220 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
221 * define the size of a block used by the Block-Jacobi preconditioner.
222 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
223 *
224 * @see MatrixSparse
225 */
226 explicit SplineBuilder(
227 std::string label,
228 interpolation_domain_type const& interpolation_domain,
229 std::optional<std::size_t> cols_per_chunk = std::nullopt,
230 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
231 : m_interpolation_domain(interpolation_domain)
232 , m_dx((ddc::discrete_space<BSplines>().rmax() - ddc::discrete_space<BSplines>().rmin())
233 / ddc::discrete_space<BSplines>().ncells())
234 , m_label(std::move(label))
235 {
236 static_assert(
237 ((SBCLower == SplineBuilderClosure::PERIODIC)
238 == (SBCUpper == SplineBuilderClosure::PERIODIC)),
239 "Incompatible closure relations");
240 check_valid_grid();
241
242 compute_offset(this->interpolation_domain(), m_offset);
243
244 // Calculate block sizes
245 int lower_block_size;
246 int upper_block_size;
247 if constexpr (bsplines_type::is_uniform()) {
248 upper_block_size = compute_block_sizes_uniform(SBCLower, s_nbe_xmin);
249 lower_block_size = compute_block_sizes_uniform(SBCUpper, s_nbe_xmax);
250 } else {
251 upper_block_size = compute_block_sizes_non_uniform(SBCLower, s_nbe_xmin);
252 lower_block_size = compute_block_sizes_non_uniform(SBCUpper, s_nbe_xmax);
253 }
254 allocate_matrix(
255 lower_block_size,
256 upper_block_size,
257 cols_per_chunk,
258 preconditioner_max_block_size);
259 }
260
261 /**
262 * @brief Build a SplineBuilder acting on interpolation_domain.
263 *
264 * @param interpolation_domain The domain on which the interpolation points are defined.
265 *
266 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
267 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
268 * by the linear solver one-after-the-other).
269 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
270 *
271 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
272 * define the size of a block used by the Block-Jacobi preconditioner.
273 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
274 *
275 * @see MatrixSparse
276 */
277 explicit SplineBuilder(
278 interpolation_domain_type const& interpolation_domain,
279 std::optional<std::size_t> cols_per_chunk = std::nullopt,
280 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
282 "no-label",
283 interpolation_domain,
284 cols_per_chunk,
285 preconditioner_max_block_size)
286 {
287 }
288
289 /**
290 * @brief Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
291 *
292 * @param label A label used to tag parallel regions and memory allocations for profiling.
293 *
294 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
295 *
296 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
297 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
298 * by the linear solver one-after-the-other).
299 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
300 *
301 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
302 * define the size of a block used by the Block-Jacobi preconditioner.
303 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
304 *
305 * @see MatrixSparse
306 */
307 template <concepts::discrete_domain BatchedInterpolationDDom>
308 explicit SplineBuilder(
309 std::string label,
310 BatchedInterpolationDDom const& batched_interpolation_domain,
311 std::optional<std::size_t> cols_per_chunk = std::nullopt,
312 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
314 std::move(label),
315 interpolation_domain_type(batched_interpolation_domain),
316 cols_per_chunk,
317 preconditioner_max_block_size)
318 {
319 }
320
321 /**
322 * @brief Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
323 *
324 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
325 *
326 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
327 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
328 * by the linear solver one-after-the-other).
329 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
330 *
331 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
332 * define the size of a block used by the Block-Jacobi preconditioner.
333 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
334 *
335 * @see MatrixSparse
336 */
337 template <concepts::discrete_domain BatchedInterpolationDDom>
338 explicit SplineBuilder(
339 BatchedInterpolationDDom const& batched_interpolation_domain,
340 std::optional<std::size_t> cols_per_chunk = std::nullopt,
341 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
343 "no-label",
344 interpolation_domain_type(batched_interpolation_domain),
345 cols_per_chunk,
346 preconditioner_max_block_size)
347 {
348 }
349
350
351 /// @brief Copy-constructor is deleted.
352 SplineBuilder(SplineBuilder const& x) = delete;
353
354 /** @brief Move-constructs.
355 *
356 * @param x An rvalue to another SplineBuilder.
357 */
358 SplineBuilder(SplineBuilder&& x) = default;
359
360 /// @brief Destructs.
361 ~SplineBuilder() = default;
362
363 /// @brief Copy-assignment is deleted.
364 SplineBuilder& operator=(SplineBuilder const& x) = delete;
365
366 /** @brief Move-assigns.
367 *
368 * @param x An rvalue to another SplineBuilder.
369 * @return A reference to this object.
370 */
371 SplineBuilder& operator=(SplineBuilder&& x) = default;
372
373 /**
374 * @brief Get the domain for the 1D interpolation mesh used by this class.
375 *
376 * This is 1D because it is defined along the dimension of interest.
377 *
378 * @return The 1D domain for the interpolation mesh.
379 */
380 interpolation_domain_type interpolation_domain() const noexcept
381 {
382 return m_interpolation_domain;
383 }
384
385 /**
386 * @brief Get the whole domain representing interpolation points.
387 *
388 * Values of the function must be provided on this domain in order
389 * to build a spline representation of the function (cartesian product of 1D interpolation_domain and batch_domain).
390 *
391 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
392 *
393 * @return The domain for the interpolation mesh.
394 */
395 template <concepts::discrete_domain BatchedInterpolationDDom>
396 BatchedInterpolationDDom batched_interpolation_domain(
397 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
398 {
399 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
400 return batched_interpolation_domain;
401 }
402
403 /**
404 * @brief Get the batch domain.
405 *
406 * Obtained by removing the dimension of interest from the whole interpolation domain.
407 *
408 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
409 *
410 * @return The batch domain.
411 */
412 template <class BatchedInterpolationDDom>
413 batch_domain_type<BatchedInterpolationDDom> batch_domain(
414 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
415 {
416 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
417 return ddc::remove_dims_of(batched_interpolation_domain, interpolation_domain());
418 }
419
420 /**
421 * @brief Get the 1D domain on which spline coefficients are defined.
422 *
423 * The 1D spline domain corresponding to the dimension of interest.
424 *
425 * @return The 1D domain for the spline coefficients.
426 */
427 ddc::DiscreteDomain<bsplines_type> spline_domain() const noexcept
428 {
429 return ddc::discrete_space<bsplines_type>().full_domain();
430 }
431
432 /**
433 * @brief Get the whole domain on which spline coefficients are defined.
434 *
435 * Spline approximations (spline-transformed functions) are computed on this domain.
436 *
437 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
438 *
439 * @return The domain for the spline coefficients.
440 */
441 template <class BatchedInterpolationDDom>
442 batched_spline_domain_type<BatchedInterpolationDDom> batched_spline_domain(
443 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
444 {
445 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
446 return ddc::replace_dim_of<
447 interpolation_discrete_dimension_type,
448 bsplines_type>(batched_interpolation_domain, spline_domain());
449 }
450
451private:
452 /**
453 * @brief Get the whole domain on which spline coefficients are defined, with the dimension of interest being the leading dimension.
454 *
455 * This is used internally due to solver limitation and because it may be beneficial to computation performance. For LAPACK backend and non-periodic closure relation, we are using SplinesLinearSolver3x3Blocks which requires upper_block_size additional rows for internal operations.
456 *
457 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
458 *
459 * @return The (transposed) domain for the spline coefficients.
460 */
461 template <class BatchedInterpolationDDom>
462 batched_spline_tr_domain_type<BatchedInterpolationDDom> batched_spline_tr_domain(
463 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
464 {
465 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
466 return batched_spline_tr_domain_type<BatchedInterpolationDDom>(
467 ddc::replace_dim_of<bsplines_type, bsplines_type>(
468 batched_spline_domain(batched_interpolation_domain),
469 ddc::DiscreteDomain<bsplines_type>(
470 ddc::DiscreteElement<bsplines_type>(0),
471 ddc::DiscreteVector<bsplines_type>(
472 m_matrix->required_number_of_rhs_rows()))));
473 }
474
475public:
476 /**
477 * @brief Get the whole domain on which derivatives on lower boundary are defined.
478 *
479 * This is only used with SplineBuilderClosure::HERMITE closure relations.
480 *
481 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
482 *
483 * @return The domain for the Derivs values.
484 */
485 template <class BatchedInterpolationDDom>
486 batched_derivs_domain_type<BatchedInterpolationDDom> batched_derivs_xmin_domain(
487 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
488 {
489 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
490 return ddc::replace_dim_of<interpolation_discrete_dimension_type, deriv_type>(
491 batched_interpolation_domain,
492 ddc::DiscreteDomain<deriv_type>(
493 ddc::DiscreteElement<deriv_type>(1),
494 ddc::DiscreteVector<deriv_type>(s_nbv_xmin)));
495 }
496
497 /**
498 * @brief Get the whole domain on which derivatives on upper boundary are defined.
499 *
500 * This is only used with SplineBuilderClosure::HERMITE closure relations.
501 *
502 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
503 *
504 * @return The domain for the Derivs values.
505 */
506 template <class BatchedInterpolationDDom>
507 batched_derivs_domain_type<BatchedInterpolationDDom> batched_derivs_xmax_domain(
508 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
509 {
510 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
511 return ddc::replace_dim_of<interpolation_discrete_dimension_type, deriv_type>(
512 batched_interpolation_domain,
513 ddc::DiscreteDomain<deriv_type>(
514 ddc::DiscreteElement<deriv_type>(1),
515 ddc::DiscreteVector<deriv_type>(s_nbv_xmax)));
516 }
517
518 /**
519 * @brief Compute a spline approximation of a function.
520 *
521 * Use the values of a function (defined on
522 * SplineBuilder::batched_interpolation_domain) and the derivatives of the
523 * function at the boundaries (in the case of SplineBuilderClosure::HERMITE only, defined
524 * on SplineBuilder::batched_derivs_xmin_domain and SplineBuilder::batched_derivs_xmax_domain)
525 * to calculate a spline approximation of this function.
526 *
527 * The spline approximation is stored as a ChunkSpan of coefficients
528 * associated with B-splines.
529 *
530 * @param[out] spline The coefficients of the spline computed by this SplineBuilder.
531 * @param[in] vals The values of the function on the interpolation mesh.
532 * @param[in] derivs_xmin The values of the derivatives at the lower boundary
533 * (used only with SplineBuilderClosure::HERMITE lower closure relation).
534 * @param[in] derivs_xmax The values of the derivatives at the upper boundary
535 * (used only with SplineBuilderClosure::HERMITE upper closure relation).
536 */
537 template <class Layout, class BatchedInterpolationDDom>
538 void operator()(
539 ddc::ChunkSpan<
540 Real,
541 batched_spline_domain_type<BatchedInterpolationDDom>,
542 Layout,
543 memory_space> spline,
544 ddc::ChunkSpan<Real const, BatchedInterpolationDDom, Layout, memory_space> vals,
545 std::optional<ddc::ChunkSpan<
546 Real const,
547 batched_derivs_domain_type<BatchedInterpolationDDom>,
548 Layout,
549 memory_space>> derivs_xmin
550 = std::nullopt,
551 std::optional<ddc::ChunkSpan<
552 Real const,
553 batched_derivs_domain_type<BatchedInterpolationDDom>,
554 Layout,
555 memory_space>> derivs_xmax
556 = std::nullopt) const;
557
558 /**
559 * @brief Compute the quadrature coefficients associated to the b-splines used by this SplineBuilder.
560 *
561 * Those coefficients can be used to perform integration way faster than SplineEvaluator::integrate().
562 *
563 * This function solves matrix equation A^t*Q=integral_bsplines. In case of HERMITE closure relations,
564 * integral_bsplines contains the integral coefficients at the boundaries, and Q thus has to
565 * be split in three parts (quadrature coefficients for the derivatives at lower boundary,
566 * for the values inside the domain and for the derivatives at upper boundary).
567 *
568 * A discrete function f can then be integrated using sum_j Q_j*f_j for j in interpolation_domain.
569 * If closure relation is HERMITE, sum_j Qderiv_j*(d^j f/dx^j) for j in derivs_domain
570 * must be added at the boundary.
571 *
572 * Please refer to section 2.8.1 of Emily's Bourne phd (https://theses.fr/2022AIXM0412) for more information and to
573 * the (Non)PeriodicSplineBuilderTest for example usage to compute integrals.
574 *
575 * @tparam OutMemorySpace The Kokkos::MemorySpace on which the quadrature coefficients are be returned
576 * (but they are computed on ExecSpace then copied).
577 *
578 * @return A tuple containing the three Chunks containing the quadrature coefficients (if HERMITE
579 * is not used, first and third are empty).
580 */
581 template <class OutMemorySpace = MemorySpace>
582 std::tuple<
583 ddc::Chunk<
584 Real,
586 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
587 ddc::KokkosAllocator<Real, OutMemorySpace>>,
588 ddc::Chunk<
589 Real,
590 ddc::DiscreteDomain<InterpolationDDim>,
591 ddc::KokkosAllocator<Real, OutMemorySpace>>,
592 ddc::Chunk<
593 Real,
595 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
596 ddc::KokkosAllocator<Real, OutMemorySpace>>>
598
599private:
600 static int compute_block_sizes_uniform(ddc::SplineBuilderClosure bound_cond, int nbc);
601
602 static int compute_block_sizes_non_uniform(ddc::SplineBuilderClosure bound_cond, int nbc);
603
604 void allocate_matrix(
605 int lower_block_size,
606 int upper_block_size,
607 std::optional<std::size_t> cols_per_chunk = std::nullopt,
608 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt);
609
610 void build_matrix_system();
611
612 void check_valid_grid();
613
614 template <class KnotElement>
615 static void check_n_points_in_cell(int n_points_in_cell, KnotElement current_cell_end_idx);
616};
617
618template <
619 class ExecSpace,
620 class MemorySpace,
621 class BSplines,
622 class InterpolationDDim,
623 ddc::SplineBuilderClosure SBCLower,
624 ddc::SplineBuilderClosure SBCUpper,
625 SplineSolver Solver>
626void SplineBuilder<
627 ExecSpace,
628 MemorySpace,
629 BSplines,
630 InterpolationDDim,
631 SBCLower,
632 SBCUpper,
633 Solver>::compute_offset(interpolation_domain_type const& interpolation_domain, int& offset)
634{
635 if constexpr (bsplines_type::is_periodic()) {
636 // Calculate offset so that the matrix is diagonally dominant
637 std::array<Real, bsplines_type::degree() + 1> values_ptr;
638 Kokkos::mdspan<Real, Kokkos::extents<std::size_t, bsplines_type::degree() + 1>> const
639 values(values_ptr.data());
640 ddc::DiscreteElement<interpolation_discrete_dimension_type> start(
641 interpolation_domain.front());
642 auto jmin = ddc::discrete_space<BSplines>()
643 .eval_basis(values, ddc::coordinate(start + BSplines::degree()));
644 if constexpr (bsplines_type::degree() % 2 == 0) {
645 offset = jmin.uid() - start.uid() + bsplines_type::degree() / 2 - BSplines::degree();
646 } else {
647 int const mid = bsplines_type::degree() / 2;
648 offset = jmin.uid() - start.uid()
649 + (DDC_MDSPAN_ACCESS_OP(values, mid) > DDC_MDSPAN_ACCESS_OP(values, mid + 1)
650 ? mid
651 : mid + 1)
652 - BSplines::degree();
653 }
654 } else {
655 offset = 0;
656 }
657}
658
659template <
660 class ExecSpace,
661 class MemorySpace,
662 class BSplines,
663 class InterpolationDDim,
664 ddc::SplineBuilderClosure SBCLower,
665 ddc::SplineBuilderClosure SBCUpper,
666 SplineSolver Solver>
667int SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
668 compute_block_sizes_uniform(ddc::SplineBuilderClosure const bound_cond, int const nbc)
669{
670 if (bound_cond == ddc::SplineBuilderClosure::PERIODIC) {
671 return static_cast<int>(bsplines_type::degree()) / 2;
672 }
673
674 if (bound_cond == ddc::SplineBuilderClosure::HERMITE
676 return nbc;
677 }
678
679 if (bound_cond == ddc::SplineBuilderClosure::GREVILLE) {
680 return static_cast<int>(bsplines_type::degree()) - 1;
681 }
682
683 throw std::runtime_error("ddc::SplineBuilderClosure not handled");
684}
685
686template <
687 class ExecSpace,
688 class MemorySpace,
689 class BSplines,
690 class InterpolationDDim,
691 ddc::SplineBuilderClosure SBCLower,
692 ddc::SplineBuilderClosure SBCUpper,
693 SplineSolver Solver>
694int SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
695 compute_block_sizes_non_uniform(ddc::SplineBuilderClosure const bound_cond, int const nbc)
696{
698 || bound_cond == ddc::SplineBuilderClosure::GREVILLE) {
699 return static_cast<int>(bsplines_type::degree()) - 1;
700 }
701
702 if (bound_cond == ddc::SplineBuilderClosure::HERMITE
704 return nbc + 1;
705 }
706
707 throw std::runtime_error("ddc::SplineBuilderClosure not handled");
708}
709
710template <
711 class ExecSpace,
712 class MemorySpace,
713 class BSplines,
714 class InterpolationDDim,
715 ddc::SplineBuilderClosure SBCLower,
716 ddc::SplineBuilderClosure SBCUpper,
717 SplineSolver Solver>
718void SplineBuilder<
719 ExecSpace,
720 MemorySpace,
721 BSplines,
722 InterpolationDDim,
723 SBCLower,
724 SBCUpper,
725 Solver>::
726 allocate_matrix(
727 [[maybe_unused]] int lower_block_size,
728 [[maybe_unused]] int upper_block_size,
729 std::optional<std::size_t> cols_per_chunk,
730 std::optional<unsigned int> preconditioner_max_block_size)
731{
732 // Special case: linear spline
733 // No need for matrix assembly
734 // (disabled)
735 // if constexpr (bsplines_type::degree() == 1) {
736 // return;
737 // }
738
739 if constexpr (Solver == ddc::SplineSolver::LAPACK) {
740 int upper_band_width;
741 if (bsplines_type::is_uniform()) {
742 upper_band_width = bsplines_type::degree() / 2;
743 } else {
744 upper_band_width = bsplines_type::degree() - 1;
745 }
746 if constexpr (bsplines_type::is_periodic()) {
747 m_matrix = ddc::detail::SplinesLinearProblemMaker::make_new_periodic_band_matrix<
748 ExecSpace>(
749 ddc::discrete_space<BSplines>().nbasis(),
750 upper_band_width,
751 upper_band_width,
752 bsplines_type::is_uniform());
753 } else {
754 m_matrix = ddc::detail::SplinesLinearProblemMaker::
755 make_new_block_matrix_with_band_main_block<ExecSpace>(
756 ddc::discrete_space<BSplines>().nbasis(),
757 upper_band_width,
758 upper_band_width,
759 bsplines_type::is_uniform(),
760 lower_block_size,
761 upper_block_size);
762 }
763 } else if constexpr (Solver == ddc::SplineSolver::GINKGO) {
764 m_matrix = ddc::detail::SplinesLinearProblemMaker::make_new_sparse<ExecSpace>(
765 ddc::discrete_space<BSplines>().nbasis(),
766 cols_per_chunk,
767 preconditioner_max_block_size);
768 }
769
770 build_matrix_system();
771
772 m_matrix->setup_solver();
773}
774
775template <
776 class ExecSpace,
777 class MemorySpace,
778 class BSplines,
779 class InterpolationDDim,
780 ddc::SplineBuilderClosure SBCLower,
781 ddc::SplineBuilderClosure SBCUpper,
782 SplineSolver Solver>
783void SplineBuilder<
784 ExecSpace,
785 MemorySpace,
786 BSplines,
787 InterpolationDDim,
788 SBCLower,
789 SBCUpper,
790 Solver>::build_matrix_system()
791{
792 // Hermite closure relations at xmin, if any
793 if constexpr (
796 std::array<Real, (bsplines_type::degree() / 2 + 1) * (bsplines_type::degree() + 1)>
797 derivs_ptr;
798 Kokkos::mdspan<double, Kokkos::dextents<std::size_t, 2>> const
799 derivs(derivs_ptr.data(),
800 bsplines_type::degree() + 1,
801 bsplines_type::degree() / 2 + 1);
802 ddc::discrete_space<BSplines>().eval_basis_and_n_derivs(
803 derivs,
804 ddc::discrete_space<BSplines>().rmin(),
805 s_nbe_xmin);
806
807 // In order to improve the condition number of the matrix, we normalize
808 // all derivatives by multiplying the i-th derivative by dx^i
809 for (std::size_t i = 0; i < bsplines_type::degree() + 1; ++i) {
810 for (std::size_t j = 1; j < bsplines_type::degree() / 2 + 1; ++j) {
811 DDC_MDSPAN_ACCESS_OP(derivs, i, j) *= ddc::detail::ipow(m_dx, j);
812 }
813 }
814
815 if constexpr (s_nbe_xmin > 0) {
816 // iterate only to deg as last bspline is 0
817 for (std::size_t i = 0; i < s_nbe_xmin; ++i) {
818 for (std::size_t j = 0; j < bsplines_type::degree(); ++j) {
819 m_matrix->set_element(i, j, DDC_MDSPAN_ACCESS_OP(derivs, j, i + s_odd));
820 }
821 }
822 }
823 }
824
825 // Interpolation points
826 std::array<Real, bsplines_type::degree() + 1> values_ptr;
827 Kokkos::mdspan<Real, Kokkos::extents<std::size_t, bsplines_type::degree() + 1>> const values(
828 values_ptr.data());
829
830 int start = interpolation_domain().front().uid();
831 ddc::host_for_each(interpolation_domain(), [&](auto ix) {
832 auto jmin = ddc::discrete_space<BSplines>().eval_basis(
833 values,
834 ddc::coordinate(ddc::DiscreteElement<interpolation_discrete_dimension_type>(ix)));
835 for (std::size_t s = 0; s < bsplines_type::degree() + 1; ++s) {
836 int const j = ddc::detail::
837 modulo(int(jmin.uid() - m_offset + s),
838 static_cast<int>(ddc::discrete_space<BSplines>().nbasis()));
839 m_matrix->set_element(
840 ix.uid() - start + s_nbe_xmin,
841 j,
842 DDC_MDSPAN_ACCESS_OP(values, s));
843 }
844 });
845
846 // Hermite closure relations at xmax, if any
847 if constexpr (
850 std::array<Real, (bsplines_type::degree() / 2 + 1) * (bsplines_type::degree() + 1)>
851 derivs_ptr;
852 Kokkos::mdspan<
853 Real,
854 Kokkos::extents<
855 std::size_t,
856 bsplines_type::degree() + 1,
857 bsplines_type::degree() / 2 + 1>> const derivs(derivs_ptr.data());
858
859 ddc::discrete_space<BSplines>().eval_basis_and_n_derivs(
860 derivs,
861 ddc::discrete_space<BSplines>().rmax(),
862 s_nbe_xmax);
863
864 // In order to improve the condition number of the matrix, we normalize
865 // all derivatives by multiplying the i-th derivative by dx^i
866 for (std::size_t i = 0; i < bsplines_type::degree() + 1; ++i) {
867 for (std::size_t j = 1; j < bsplines_type::degree() / 2 + 1; ++j) {
868 DDC_MDSPAN_ACCESS_OP(derivs, i, j) *= ddc::detail::ipow(m_dx, j);
869 }
870 }
871
872 if constexpr (s_nbe_xmax > 0) {
873 int const i0 = ddc::discrete_space<BSplines>().nbasis() - s_nbe_xmax;
874 int const j0 = ddc::discrete_space<BSplines>().nbasis() - bsplines_type::degree();
875 for (std::size_t j = 0; j < bsplines_type::degree(); ++j) {
876 for (std::size_t i = 0; i < s_nbe_xmax; ++i) {
877 m_matrix->set_element(
878 i0 + i,
879 j0 + j,
880 DDC_MDSPAN_ACCESS_OP(derivs, j + 1, i + s_odd));
881 }
882 }
883 }
884 }
885}
886
887template <
888 class ExecSpace,
889 class MemorySpace,
890 class BSplines,
891 class InterpolationDDim,
895template <class Layout, class BatchedInterpolationDDom>
896void SplineBuilder<
897 ExecSpace,
898 MemorySpace,
899 BSplines,
900 InterpolationDDim,
901 SBCLower,
902 SBCUpper,
903 Solver>::
904operator()(
905 ddc::ChunkSpan<
906 Real,
907 batched_spline_domain_type<BatchedInterpolationDDom>,
908 Layout,
909 memory_space> spline,
910 ddc::ChunkSpan<Real const, BatchedInterpolationDDom, Layout, memory_space> vals,
911 std::optional<ddc::ChunkSpan<
912 Real const,
913 batched_derivs_domain_type<BatchedInterpolationDDom>,
914 Layout,
915 memory_space>> const derivs_xmin,
916 std::optional<ddc::ChunkSpan<
917 Real const,
918 batched_derivs_domain_type<BatchedInterpolationDDom>,
919 Layout,
920 memory_space>> const derivs_xmax) const
921{
922 auto const batched_interpolation_domain = vals.domain();
923
924 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
925 assert(batch_domain_type<BatchedInterpolationDDom>(batched_interpolation_domain)
926 == batch_domain_type<BatchedInterpolationDDom>(spline.domain()));
927
928 if (batch_domain(batched_interpolation_domain).empty()) {
929 return;
930 }
931
932 assert(vals.template extent<interpolation_discrete_dimension_type>()
933 == ddc::discrete_space<bsplines_type>().nbasis() - s_nbe_xmin - s_nbe_xmax);
934
935 if constexpr (SBCLower == SplineBuilderClosure::HERMITE) {
936 assert(ddc::DiscreteElement<deriv_type>(derivs_xmin->domain().front()).uid() == s_odd);
937 assert(derivs_xmin.has_value() || s_nbe_xmin == 0);
938 } else {
939 assert(!derivs_xmin.has_value() || derivs_xmin->template extent<deriv_type>() == 0);
940 }
941 if constexpr (SBCUpper == SplineBuilderClosure::HERMITE) {
942 assert(ddc::DiscreteElement<deriv_type>(derivs_xmax->domain().front()).uid() == s_odd);
943 assert(derivs_xmax.has_value() || s_nbe_xmax == 0);
944 } else {
945 assert(!derivs_xmax.has_value() || derivs_xmax->template extent<deriv_type>() == 0);
946 }
947
948 // Hermite closure relations at xmin, if any
949 // NOTE: For consistency with the linear system, the i-th derivative
950 // provided by the user must be multiplied by dx^i
951 if constexpr (SBCLower == SplineBuilderClosure::HERMITE) {
952 assert(derivs_xmin->template extent<deriv_type>() == s_nbe_xmin);
953 auto derivs_xmin_values = *derivs_xmin;
954 auto const dx_proxy = m_dx;
955 auto const odd_proxy = s_odd;
956 ddc::parallel_for_each(
957 "ddc_splines_hermite_compute_lower_coefficients",
958 exec_space(),
959 batch_domain(batched_interpolation_domain),
960 KOKKOS_LAMBDA(
961 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type j) {
962 for (int i = 0; i < s_nbe_xmin; ++i) {
963 spline(ddc::DiscreteElement<bsplines_type>(i), j)
964 = derivs_xmin_values(
965 ddc::DiscreteElement<deriv_type>(i + odd_proxy),
966 j)
967 * ddc::detail::ipow(dx_proxy, i + odd_proxy);
968 }
969 });
970 } else if constexpr (SBCLower == SplineBuilderClosure::HOMOGENEOUS_HERMITE) {
971 ddc::DiscreteDomain<bsplines_type> const dx_splines(
972 ddc::DiscreteElement<bsplines_type>(0),
973 ddc::DiscreteVector<bsplines_type>(s_nbe_xmin));
974 batched_spline_domain_type<BatchedInterpolationDDom> const
975 dx_spline_domain(dx_splines, batch_domain(batched_interpolation_domain));
976 ddc::parallel_fill(exec_space(), spline[dx_spline_domain], 0.0);
977 }
978
979 // Fill spline with vals (to work in spline afterward and preserve vals)
980 ddc::parallel_fill(
981 exec_space(),
982 spline[ddc::DiscreteDomain<bsplines_type>(
983 ddc::DiscreteElement<bsplines_type>(s_nbe_xmin),
984 ddc::DiscreteVector<bsplines_type>(m_offset))],
985 0.);
986 // NOTE: We rely on Kokkos::deep_copy because ddc::parallel_deepcopy do not support
987 // different domain-typed Chunks.
988 Kokkos::deep_copy(
989 exec_space(),
990 spline[ddc::DiscreteDomain<bsplines_type>(
991 ddc::DiscreteElement<bsplines_type>(s_nbe_xmin + m_offset),
992 ddc::DiscreteVector<bsplines_type>(static_cast<std::size_t>(
993 vals.domain()
994 .template extent<
995 interpolation_discrete_dimension_type>())))]
996 .allocation_kokkos_view(),
997 vals.allocation_kokkos_view());
998
999
1000
1001 // Hermite closure relations at xmax, if any
1002 // NOTE: For consistency with the linear system, the i-th derivative
1003 // provided by the user must be multiplied by dx^i
1004 auto const& nbasis_proxy = ddc::discrete_space<bsplines_type>().nbasis();
1005 if constexpr (SBCUpper == SplineBuilderClosure::HERMITE) {
1006 assert(derivs_xmax->template extent<deriv_type>() == s_nbe_xmax);
1007 auto derivs_xmax_values = *derivs_xmax;
1008 auto const dx_proxy = m_dx;
1009 auto const odd_proxy = s_odd;
1010 ddc::parallel_for_each(
1011 "ddc_splines_hermite_compute_upper_coefficients",
1012 exec_space(),
1013 batch_domain(batched_interpolation_domain),
1014 KOKKOS_LAMBDA(
1015 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type j) {
1016 for (int i = 0; i < s_nbe_xmax; ++i) {
1017 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy - s_nbe_xmax + i),
1018 j)
1019 = derivs_xmax_values(
1020 ddc::DiscreteElement<deriv_type>(i + odd_proxy),
1021 j)
1022 * ddc::detail::ipow(dx_proxy, i + odd_proxy);
1023 }
1024 });
1025 } else if constexpr (SBCUpper == SplineBuilderClosure::HOMOGENEOUS_HERMITE) {
1026 ddc::DiscreteDomain<bsplines_type> const dx_splines(
1027 ddc::DiscreteElement<bsplines_type>(nbasis_proxy - s_nbe_xmax),
1028 ddc::DiscreteVector<bsplines_type>(s_nbe_xmax));
1029 batched_spline_domain_type<BatchedInterpolationDDom> const
1030 dx_spline_domain(dx_splines, batch_domain(batched_interpolation_domain));
1031 ddc::parallel_fill(exec_space(), spline[dx_spline_domain], 0.0);
1032 }
1033
1034 // Allocate and fill a transposed version of spline in order to get dimension of interest as last dimension (optimal for GPU, necessary for Ginkgo). Also select only relevant rows in case of periodic boundaries
1035 auto const& offset_proxy = m_offset;
1036 ddc::Chunk spline_tr_alloc(
1037 m_label + " > spline_tr (ddc::SplineBuilder::operator())",
1038 batched_spline_tr_domain(batched_interpolation_domain),
1039 ddc::KokkosAllocator<Real, memory_space>());
1040 ddc::ChunkSpan const spline_tr = spline_tr_alloc.span_view();
1041 ddc::parallel_for_each(
1042 m_label + " > ddc_splines_transpose_rhs",
1043 exec_space(),
1044 batch_domain(batched_interpolation_domain),
1045 KOKKOS_LAMBDA(
1046 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const j) {
1047 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
1048 spline_tr(ddc::DiscreteElement<bsplines_type>(i), j)
1049 = spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j);
1050 }
1051 });
1052 // Create a 2D Kokkos::View to manage spline_tr as a matrix
1053 Kokkos::View<Real**, Kokkos::LayoutRight, exec_space> const bcoef_section(
1054 spline_tr.data_handle(),
1055 static_cast<std::size_t>(spline_tr.template extent<bsplines_type>()),
1056 batch_domain(batched_interpolation_domain).size());
1057 // Compute spline coef
1058 m_matrix->solve(bcoef_section, false);
1059 // Transpose back spline_tr into spline.
1060 ddc::parallel_for_each(
1061 m_label + " > ddc_splines_transpose_back_rhs",
1062 exec_space(),
1063 batch_domain(batched_interpolation_domain),
1064 KOKKOS_LAMBDA(
1065 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const j) {
1066 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
1067 spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j)
1068 = spline_tr(ddc::DiscreteElement<bsplines_type>(i), j);
1069 }
1070 });
1071
1072 // Duplicate the lower spline coefficients to the upper side in case of periodic boundaries
1073 if (bsplines_type::is_periodic()) {
1074 ddc::parallel_for_each(
1075 m_label + " > ddc_splines_periodic_rows_duplicate_rhs",
1076 exec_space(),
1077 batch_domain(batched_interpolation_domain),
1078 KOKKOS_LAMBDA(
1079 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const
1080 j) {
1081 if (offset_proxy != 0) {
1082 for (int i = 0; i < offset_proxy; ++i) {
1083 spline(ddc::DiscreteElement<bsplines_type>(i), j) = spline(
1084 ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i),
1085 j);
1086 }
1087 for (std::size_t i = offset_proxy; i < bsplines_type::degree(); ++i) {
1088 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i), j)
1089 = spline(ddc::DiscreteElement<bsplines_type>(i), j);
1090 }
1091 }
1092 for (std::size_t i(0); i < bsplines_type::degree(); ++i) {
1093 ddc::DiscreteElement<bsplines_type> const i_start(i);
1094 ddc::DiscreteElement<bsplines_type> const i_end(nbasis_proxy + i);
1095
1096 spline(i_end, j) = spline(i_start, j);
1097 }
1098 });
1099 }
1100}
1101
1102template <
1103 class ExecSpace,
1104 class MemorySpace,
1105 class BSplines,
1106 class InterpolationDDim,
1110template <class OutMemorySpace>
1111std::tuple<
1112 ddc::Chunk<
1113 Real,
1115 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
1116 ddc::KokkosAllocator<Real, OutMemorySpace>>,
1117 ddc::Chunk<
1118 Real,
1119 ddc::DiscreteDomain<InterpolationDDim>,
1120 ddc::KokkosAllocator<Real, OutMemorySpace>>,
1121 ddc::Chunk<
1122 Real,
1124 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
1125 ddc::KokkosAllocator<Real, OutMemorySpace>>>
1126SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
1128{
1129 // Compute integrals of bsplines
1130 ddc::Chunk integral_bsplines(spline_domain(), ddc::KokkosAllocator<Real, MemorySpace>());
1131 ddc::integrals(ExecSpace(), integral_bsplines.span_view());
1132
1133 // Remove additional B-splines in the periodic case (cf. UniformBSplines::full_domain() documentation)
1134 ddc::ChunkSpan const integral_bsplines_without_periodic_additional_bsplines
1135 = integral_bsplines[spline_domain().take_first(
1136 ddc::DiscreteVector<bsplines_type>(m_matrix->size()))];
1137
1138 // Allocate mirror with additional rows (cf. SplinesLinearProblem3x3Blocks documentation)
1139 Kokkos::View<Real**, Kokkos::LayoutRight, MemorySpace> const
1140 integral_bsplines_mirror_with_additional_allocation(
1141 m_label + " > integral_bsplines_mirror_with_additional_allocation",
1142 m_matrix->required_number_of_rhs_rows(),
1143 1);
1144
1145 // Extract relevant subview
1146 Kokkos::View<Real*, Kokkos::LayoutRight, MemorySpace> const integral_bsplines_mirror = Kokkos::
1147 subview(integral_bsplines_mirror_with_additional_allocation,
1148 std::
1149 pair {static_cast<std::size_t>(0),
1150 integral_bsplines_without_periodic_additional_bsplines.size()},
1151 0);
1152
1153 // Solve matrix equation A^t*X=integral_bsplines
1154 Kokkos::deep_copy(
1155 integral_bsplines_mirror,
1156 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view());
1157 m_matrix->solve(integral_bsplines_mirror_with_additional_allocation, true);
1158 Kokkos::deep_copy(
1159 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view(),
1160 integral_bsplines_mirror);
1161
1162 // Slice into three ChunkSpan corresponding to lower derivatives, function values and upper derivatives
1163 ddc::ChunkSpan const coefficients_derivs_xmin
1164 = integral_bsplines_without_periodic_additional_bsplines[spline_domain().take_first(
1165 ddc::DiscreteVector<bsplines_type>(s_nbv_xmin))];
1166 ddc::ChunkSpan const coefficients = integral_bsplines_without_periodic_additional_bsplines
1168 .remove_first(ddc::DiscreteVector<bsplines_type>(s_nbv_xmin))
1169 .take_first(
1170 ddc::DiscreteVector<bsplines_type>(
1171 ddc::discrete_space<bsplines_type>().nbasis() - s_nbv_xmin
1172 - s_nbv_xmax))];
1173 ddc::ChunkSpan const coefficients_derivs_xmax
1174 = integral_bsplines_without_periodic_additional_bsplines
1176 .remove_first(
1177 ddc::DiscreteVector<bsplines_type>(
1178 s_nbv_xmin + coefficients.size()))
1179 .take_first(ddc::DiscreteVector<bsplines_type>(s_nbv_xmax))];
1180
1181 // Multiply derivatives coefficients by dx^n
1182 auto const dx_proxy = m_dx;
1183 auto const odd_proxy = s_odd;
1184 ddc::parallel_for_each(
1185 exec_space(),
1186 coefficients_derivs_xmin.domain(),
1187 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1188 coefficients_derivs_xmin(i) *= ddc::detail::
1189 ipow(dx_proxy,
1190 static_cast<std::size_t>(get<bsplines_type>(
1191 (i - coefficients_derivs_xmin.domain().front()) + odd_proxy)));
1192 });
1193 ddc::parallel_for_each(
1194 exec_space(),
1195 coefficients_derivs_xmax.domain(),
1196 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1197 coefficients_derivs_xmax(i) *= ddc::detail::
1198 ipow(dx_proxy,
1199 static_cast<std::size_t>(get<bsplines_type>(
1200 (i - coefficients_derivs_xmax.domain().front()) + odd_proxy)));
1201 });
1202
1203 ddc::DiscreteElement<deriv_type> const first_deriv(s_odd);
1204 // Allocate Chunk on deriv_type and interpolation_discrete_dimension_type and copy quadrature coefficients into it
1205 ddc::Chunk coefficients_derivs_xmin_out(
1207 deriv_type>(first_deriv, ddc::DiscreteVector<deriv_type>(s_nbv_xmin)),
1208 ddc::KokkosAllocator<Real, OutMemorySpace>());
1209 ddc::Chunk coefficients_out(
1210 interpolation_domain().take_first(
1211 ddc::DiscreteVector<interpolation_discrete_dimension_type>(
1212 coefficients.size())),
1213 ddc::KokkosAllocator<Real, OutMemorySpace>());
1214 ddc::Chunk coefficients_derivs_xmax_out(
1216 deriv_type>(first_deriv, ddc::DiscreteVector<deriv_type>(s_nbv_xmax)),
1217 ddc::KokkosAllocator<Real, OutMemorySpace>());
1218 Kokkos::deep_copy(
1219 coefficients_derivs_xmin_out.allocation_kokkos_view(),
1220 coefficients_derivs_xmin.allocation_kokkos_view());
1221 Kokkos::deep_copy(
1222 coefficients_out.allocation_kokkos_view(),
1223 coefficients.allocation_kokkos_view());
1224 Kokkos::deep_copy(
1225 coefficients_derivs_xmax_out.allocation_kokkos_view(),
1226 coefficients_derivs_xmax.allocation_kokkos_view());
1227 return std::make_tuple(
1228 std::move(coefficients_derivs_xmin_out),
1229 std::move(coefficients_out),
1230 std::move(coefficients_derivs_xmax_out));
1231}
1232
1233template <
1234 class ExecSpace,
1235 class MemorySpace,
1236 class BSplines,
1237 class InterpolationDDim,
1241template <class KnotElement>
1242void SplineBuilder<
1243 ExecSpace,
1244 MemorySpace,
1245 BSplines,
1246 InterpolationDDim,
1247 SBCLower,
1248 SBCUpper,
1249 Solver>::
1250 check_n_points_in_cell(int const n_points_in_cell, KnotElement const current_cell_end_idx)
1251{
1252 if (n_points_in_cell > BSplines::degree() + 1) {
1253 KnotElement const rmin_idx = ddc::discrete_space<BSplines>().break_point_domain().front();
1254 int const failed_cell = (current_cell_end_idx - rmin_idx).value();
1255 throw std::runtime_error(
1256 "The spline problem is overconstrained. There are "
1257 + std::to_string(n_points_in_cell) + " points in the " + std::to_string(failed_cell)
1258 + "-th cell.");
1259 }
1260}
1261
1262template <
1263 class ExecSpace,
1264 class MemorySpace,
1265 class BSplines,
1266 class InterpolationDDim,
1267 ddc::SplineBuilderClosure SBCLower,
1268 ddc::SplineBuilderClosure SBCUpper,
1269 SplineSolver Solver>
1270void SplineBuilder<
1271 ExecSpace,
1272 MemorySpace,
1273 BSplines,
1274 InterpolationDDim,
1275 SBCLower,
1276 SBCUpper,
1277 Solver>::check_valid_grid()
1278{
1279 std::size_t const n_interp_points = interpolation_domain().size();
1280 std::size_t const expected_npoints
1281 = ddc::discrete_space<BSplines>().nbasis() - s_nbe_xmin - s_nbe_xmax;
1282 if (n_interp_points != expected_npoints) {
1283 throw std::runtime_error(
1284 "Incorrect number of points supplied to NonUniformInterpolationPoints. "
1285 "(Received : "
1286 + std::to_string(n_interp_points)
1287 + ", expected : " + std::to_string(expected_npoints));
1288 }
1289 int n_points_in_cell = 0;
1290 auto current_cell_end_idx = ddc::discrete_space<BSplines>().break_point_domain().front() + 1;
1291 ddc::host_for_each(interpolation_domain(), [&](auto idx) {
1292 ddc::Coordinate<continuous_dimension_type> const point = ddc::coordinate(idx);
1293 if (point > ddc::coordinate(current_cell_end_idx)) {
1294 // Check the points found in the previous cell
1295 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1296 // Initialise the number of points in the subsequent cell, including the new point
1297 n_points_in_cell = 1;
1298 // Move to the next cell
1299 current_cell_end_idx += 1;
1300 } else if (point == ddc::coordinate(current_cell_end_idx)) {
1301 // Check the points found in the previous cell including the point on the boundary
1302 check_n_points_in_cell(n_points_in_cell + 1, current_cell_end_idx);
1303 // Initialise the number of points in the subsequent cell, including the point on the boundary
1304 n_points_in_cell = 1;
1305 // Move to the next cell
1306 current_cell_end_idx += 1;
1307 } else {
1308 // Indicate that the point is in the cell
1309 n_points_in_cell += 1;
1310 }
1311 });
1312 // Check the number of points in the final cell
1313 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1314}
1315
1316} // namespace ddc
friend class ChunkSpan
friend class Chunk
Definition chunk.hpp:82
friend class DiscreteDomain
KOKKOS_FUNCTION constexpr bool operator!=(DiscreteVector< OTags... > const &rhs) const noexcept
A class which provides helper functions to initialise the Greville points from a B-Spline definition.
static ddc::DiscreteDomain< Sampling > get_domain()
Get the domain which gives us access to all of the Greville points.
Helper class for the initialisation of the mesh of interpolation points.
static auto get_sampling()
Get the sampling of interpolation points.
static ddc::DiscreteDomain< Sampling > get_domain()
Get the domain which can be used to access the interpolation points in the sampling.
Storage class of the static attributes of the discrete dimension.
KOKKOS_INLINE_FUNCTION std::size_t ncells() const noexcept
Returns the number of cells over which the B-splines are defined.
Impl(Impl &&x)=default
Move-constructs.
KOKKOS_INLINE_FUNCTION std::size_t nbasis() const noexcept
Returns the number of basis functions.
KOKKOS_INLINE_FUNCTION ddc::DiscreteElement< knot_discrete_dimension_type > get_last_support_knot(discrete_element_type const &ix) const
Returns the coordinate of the last support knot associated to a DiscreteElement identifying a B-splin...
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis_and_n_derivs(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 2 > > derivs, ddc::Coordinate< CDim > const &x, std::size_t n) const
Evaluates non-zero B-spline values and derivatives at a given coordinate.
Impl(Impl< DDim, OriginMemorySpace > const &impl)
Copy-constructs from another Impl with a different Kokkos memory space.
Impl(Impl const &x)=default
Copy-constructs.
KOKKOS_INLINE_FUNCTION discrete_element_type eval_deriv(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 1 > > derivs, ddc::Coordinate< CDim > const &x) const
Evaluates non-zero B-spline derivatives at a given coordinate.
KOKKOS_INLINE_FUNCTION ddc::DiscreteDomain< knot_discrete_dimension_type > break_point_domain() const
Returns the discrete domain which describes the break points.
Impl & operator=(Impl const &x)=default
Copy-assigns.
KOKKOS_INLINE_FUNCTION Real length() const noexcept
Returns the length of the domain.
KOKKOS_INLINE_FUNCTION std::size_t npoints() const noexcept
The number of break points.
KOKKOS_INLINE_FUNCTION ddc::DiscreteElement< knot_discrete_dimension_type > get_first_support_knot(discrete_element_type const &ix) const
Returns the coordinate of the first support knot associated to a DiscreteElement identifying a B-spli...
Impl(RandomIt breaks_begin, RandomIt breaks_end)
Constructs an Impl by iterating over a range of break points from begin to end.
KOKKOS_INLINE_FUNCTION discrete_domain_type full_domain() const
Returns the discrete domain including eventual additional B-splines in the periodic case.
KOKKOS_INLINE_FUNCTION std::size_t size() const noexcept
Returns the number of elements necessary to construct a spline representation of a function.
Impl(std::initializer_list< ddc::Coordinate< CDim > > breaks)
Constructs an Impl using a brace-list, i.e.
Impl & operator=(Impl &&x)=default
Move-assigns.
KOKKOS_INLINE_FUNCTION ddc::Coordinate< CDim > rmin() const noexcept
Returns the coordinate of the first break point of the domain on which the B-splines are defined.
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 1 > > values, ddc::Coordinate< CDim > const &x) const
Evaluates non-zero B-splines at a given coordinate.
~Impl()=default
Destructs.
KOKKOS_INLINE_FUNCTION ddc::Coordinate< CDim > rmax() const noexcept
Returns the coordinate of the last break point of the domain on which the B-splines are defined.
Impl(std::vector< ddc::Coordinate< CDim > > const &breaks)
Constructs an Impl using a std::vector.
The type of a non-uniform 1D spline basis (B-spline).
static constexpr std::size_t degree() noexcept
The degree of B-splines.
static constexpr bool is_periodic() noexcept
Indicates if the B-splines are periodic or not.
static constexpr bool is_uniform() noexcept
Indicates if the B-splines are uniform or not (this is not the case here).
NonUniformPointSampling models a non-uniform discretization of the CDim segment .
A class for creating a spline approximation of a function.
std::tuple< ddc::Chunk< Real, ddc::DiscreteDomain< ddc::Deriv< typename InterpolationDDim::continuous_dimension_type > >, ddc::KokkosAllocator< Real, OutMemorySpace > >, ddc::Chunk< Real, ddc::DiscreteDomain< InterpolationDDim >, ddc::KokkosAllocator< Real, OutMemorySpace > >, ddc::Chunk< Real, ddc::DiscreteDomain< ddc::Deriv< typename InterpolationDDim::continuous_dimension_type > >, ddc::KokkosAllocator< Real, OutMemorySpace > > > quadrature_coefficients() const
Compute the quadrature coefficients associated to the b-splines used by this SplineBuilder.
ddc::DiscreteDomain< bsplines_type > spline_domain() const noexcept
Get the 1D domain on which spline coefficients are defined.
SplineBuilder(BatchedInterpolationDDom const &batched_interpolation_domain, std::optional< std::size_t > cols_per_chunk=std::nullopt, std::optional< unsigned int > preconditioner_max_block_size=std::nullopt)
Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
SplineBuilder(SplineBuilder const &x)=delete
Copy-constructor is deleted.
interpolation_domain_type interpolation_domain() const noexcept
Get the domain for the 1D interpolation mesh used by this class.
batched_derivs_domain_type< BatchedInterpolationDDom > batched_derivs_xmax_domain(BatchedInterpolationDDom const &batched_interpolation_domain) const noexcept
Get the whole domain on which derivatives on upper boundary are defined.
static constexpr int s_nbe_xmin
The number of equations defining the closure relation at the lower bound.
static constexpr ddc::SplineBuilderClosure s_sbc_xmin
The closure relation implemented at the lower bound.
SplineBuilder(std::string label, BatchedInterpolationDDom const &batched_interpolation_domain, std::optional< std::size_t > cols_per_chunk=std::nullopt, std::optional< unsigned int > preconditioner_max_block_size=std::nullopt)
Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
batch_domain_type< BatchedInterpolationDDom > batch_domain(BatchedInterpolationDDom const &batched_interpolation_domain) const noexcept
Get the batch domain.
SplineBuilder & operator=(SplineBuilder &&x)=default
Move-assigns.
static constexpr int s_nbe_xmax
The number of equations defining the closure relation at the upper bound.
static constexpr SplineSolver s_spline_solver
The SplineSolver giving the backend used to perform the spline approximation.
static constexpr ddc::SplineBuilderClosure s_sbc_xmax
The closure relation implemented at the upper bound.
BatchedInterpolationDDom batched_interpolation_domain(BatchedInterpolationDDom const &batched_interpolation_domain) const noexcept
Get the whole domain representing interpolation points.
batched_spline_domain_type< BatchedInterpolationDDom > batched_spline_domain(BatchedInterpolationDDom const &batched_interpolation_domain) const noexcept
Get the whole domain on which spline coefficients are defined.
SplineBuilder(interpolation_domain_type const &interpolation_domain, std::optional< std::size_t > cols_per_chunk=std::nullopt, std::optional< unsigned int > preconditioner_max_block_size=std::nullopt)
Build a SplineBuilder acting on interpolation_domain.
static constexpr bool s_odd
Indicates if the degree of the splines is odd or even.
void operator()(ddc::ChunkSpan< Real, batched_spline_domain_type< BatchedInterpolationDDom >, Layout, memory_space > spline, ddc::ChunkSpan< Real const, BatchedInterpolationDDom, Layout, memory_space > vals, std::optional< ddc::ChunkSpan< Real const, batched_derivs_domain_type< BatchedInterpolationDDom >, Layout, memory_space > > derivs_xmin=std::nullopt, std::optional< ddc::ChunkSpan< Real const, batched_derivs_domain_type< BatchedInterpolationDDom >, Layout, memory_space > > derivs_xmax=std::nullopt) const
Compute a spline approximation of a function.
batched_derivs_domain_type< BatchedInterpolationDDom > batched_derivs_xmin_domain(BatchedInterpolationDDom const &batched_interpolation_domain) const noexcept
Get the whole domain on which derivatives on lower boundary are defined.
SplineBuilder(std::string label, interpolation_domain_type const &interpolation_domain, std::optional< std::size_t > cols_per_chunk=std::nullopt, std::optional< unsigned int > preconditioner_max_block_size=std::nullopt)
Build a SplineBuilder acting on interpolation_domain.
SplineBuilder(SplineBuilder &&x)=default
Move-constructs.
static constexpr int s_nbv_xmax
The number of input values defining the closure relation at the upper bound.
~SplineBuilder()=default
Destructs.
static constexpr int s_nbv_xmin
The number of input values defining the closure relation at the lower bound.
SplineBuilder & operator=(SplineBuilder const &x)=delete
Copy-assignment is deleted.
Storage class of the static attributes of the discrete dimension.
KOKKOS_INLINE_FUNCTION std::size_t ncells() const noexcept
Returns the number of cells over which the B-splines are defined.
KOKKOS_INLINE_FUNCTION Real length() const noexcept
Returns the length of the domain.
KOKKOS_INLINE_FUNCTION ddc::Coordinate< CDim > rmin() const noexcept
Returns the coordinate of the lower bound of the domain on which the B-splines are defined.
Impl(Impl const &x)=default
Copy-constructs.
KOKKOS_INLINE_FUNCTION discrete_domain_type full_domain() const
Returns the discrete domain including eventual additional B-splines in the periodic case.
KOKKOS_INLINE_FUNCTION discrete_element_type eval_deriv(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 1 > > derivs, ddc::Coordinate< CDim > const &x) const
Evaluates non-zero B-spline derivatives at a given coordinate.
~Impl()=default
Destructs.
Impl(Impl< DDim, OriginMemorySpace > const &impl)
Copy-constructs from another Impl with a different Kokkos memory space.
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis_and_n_derivs(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 2 > > derivs, ddc::Coordinate< CDim > const &x, std::size_t n) const
Evaluates non-zero B-spline values and derivatives at a given coordinate.
Impl & operator=(Impl &&x)=default
Move-assigns.
KOKKOS_INLINE_FUNCTION ddc::Coordinate< CDim > rmax() const noexcept
Returns the coordinate of the upper bound of the domain on which the B-splines are defined.
KOKKOS_INLINE_FUNCTION ddc::DiscreteElement< knot_discrete_dimension_type > get_last_support_knot(discrete_element_type const &ix) const
Returns the coordinate of the last support knot associated to a DiscreteElement identifying a B-splin...
KOKKOS_INLINE_FUNCTION ddc::DiscreteDomain< knot_discrete_dimension_type > break_point_domain() const
Returns the discrete domain which describes the break points.
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis(Kokkos::mdspan< double, Kokkos::dextents< std::size_t, 1 > > values, ddc::Coordinate< CDim > const &x) const
Evaluates non-zero B-splines at a given coordinate.
KOKKOS_INLINE_FUNCTION ddc::DiscreteElement< knot_discrete_dimension_type > get_first_support_knot(discrete_element_type const &ix) const
Returns the coordinate of the first support knot associated to a DiscreteElement identifying a B-spli...
KOKKOS_INLINE_FUNCTION std::size_t nbasis() const noexcept
Returns the number of basis functions.
Impl & operator=(Impl const &x)=default
Copy-assigns.
KOKKOS_INLINE_FUNCTION std::size_t size() const noexcept
Returns the number of elements necessary to construct a spline representation of a function.
Impl(Impl &&x)=default
Move-constructs.
Impl(ddc::Coordinate< CDim > rmin, ddc::Coordinate< CDim > rmax, std::size_t ncells)
Constructs a spline basis (B-splines) with n equidistant knots over .
The type of a uniform 1D spline basis (B-spline).
static constexpr bool is_uniform() noexcept
Indicates if the B-splines are uniform or not (this is the case here).
static constexpr std::size_t degree() noexcept
The degree of B-splines.
static constexpr bool is_periodic() noexcept
Indicates if the B-splines are periodic or not.
UniformPointSampling models a uniform discretization of the provided continuous dimension.
#define DDC_BUILD_DEPRECATED_CODE
Definition config.hpp:7
The top-level namespace of DDC.
constexpr bool is_uniform_bsplines_v
Indicates if a tag corresponds to uniform B-splines or not.
SplineSolver
An enum determining the backend solver of a SplineBuilder or SplineBuilder2d.
@ LAPACK
Enum member to identify the LAPACK-based solver (direct method)
@ GINKGO
Enum member to identify the Ginkgo-based solver (iterative method)
constexpr int n_boundary_equations(ddc::SplineBuilderClosure const sbc, std::size_t const degree)
Return the number of equations needed to describe a given closure relation.
ddc::ChunkSpan< Real, ddc::DiscreteDomain< DDim >, Layout, MemorySpace > integrals(ExecSpace const &execution_space, ddc::ChunkSpan< Real, ddc::DiscreteDomain< DDim >, Layout, MemorySpace > int_vals)
Compute the integrals of the B-splines.
constexpr bool is_non_uniform_bsplines_v
Indicates if a tag corresponds to non-uniform B-splines or not.
SplineBuilderClosure
An enum representing a spline closure relation.
@ HOMOGENEOUS_HERMITE
Homogeneous Hermite closure relation (derivatives are 0)
@ GREVILLE
Use Greville points instead of conditions on derivative for B-Spline interpolation.
@ HERMITE
Hermite closure relation.
@ PERIODIC
Periodic closure relation u(1)=u(n)
A templated struct representing a discrete dimension storing the derivatives of a function along a co...
Definition deriv.hpp:15
If the type DDim is a B-spline, defines type to the discrete dimension of the associated knots.
A functor for describing a spline boundary value by a constant extrapolation for 2D evaluator.
KOKKOS_FUNCTION Real operator()(CoordType coord_extrap, ddc::ChunkSpan< Real const, ddc::DiscreteDomain< BSplines... >, Layout, MemorySpace > const spline_coef) const
Get the value of the function on B-splines at a coordinate outside the domain.
ConstantExtrapolationRule(ddc::Coordinate< DimI > eval_pos)
Instantiate a ConstantExtrapolationRule.
A functor describing a null extrapolation boundary value for 1D spline evaluator.
KOKKOS_FUNCTION Real operator()(CoordType, ChunkSpan) const
Evaluates the spline at a coordinate outside of the domain.
A functor to represent periodic extrapolation in a 1D spline evaluator.
KOKKOS_FUNCTION Real operator()(CoordType, ChunkSpan) const
This function should never be called.