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