DDC 0.9.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 <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>> m_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 m_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 m_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 m_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 m_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 m_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 m_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::host_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 m_matrix->set_element(
751 ix.uid() - start + s_nbc_xmin,
752 j,
753 DDC_MDSPAN_ACCESS_OP(values, s));
754 }
755 });
756
757 // Hermite boundary conditions at xmax, if any
758 if constexpr (BcUpper == ddc::BoundCond::HERMITE) {
759 std::array<double, (bsplines_type::degree() / 2 + 1) * (bsplines_type::degree() + 1)>
760 derivs_ptr;
761 Kokkos::mdspan<
762 double,
763 Kokkos::extents<
764 std::size_t,
765 bsplines_type::degree() + 1,
766 bsplines_type::degree() / 2 + 1>> const derivs(derivs_ptr.data());
767
768 ddc::discrete_space<BSplines>().eval_basis_and_n_derivs(
769 derivs,
770 ddc::discrete_space<BSplines>().rmax(),
771 s_nbc_xmax);
772
773 // In order to improve the condition number of the matrix, we normalize
774 // all derivatives by multiplying the i-th derivative by dx^i
775 for (std::size_t i = 0; i < bsplines_type::degree() + 1; ++i) {
776 for (std::size_t j = 1; j < bsplines_type::degree() / 2 + 1; ++j) {
777 DDC_MDSPAN_ACCESS_OP(derivs, i, j) *= ddc::detail::ipow(m_dx, j);
778 }
779 }
780
781 int const i0 = ddc::discrete_space<BSplines>().nbasis() - s_nbc_xmax;
782 int const j0 = ddc::discrete_space<BSplines>().nbasis() - bsplines_type::degree();
783 for (std::size_t j = 0; j < bsplines_type::degree(); ++j) {
784 for (std::size_t i = 0; i < s_nbc_xmax; ++i) {
785 m_matrix->set_element(
786 i0 + i,
787 j0 + j,
788 DDC_MDSPAN_ACCESS_OP(derivs, j + 1, i + s_odd));
789 }
790 }
791 }
792}
793
794template <
795 class ExecSpace,
796 class MemorySpace,
797 class BSplines,
798 class InterpolationDDim,
802template <class Layout, class BatchedInterpolationDDom>
803void SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, BcLower, BcUpper, Solver>::
804operator()(
805 ddc::ChunkSpan<
806 double,
807 batched_spline_domain_type<BatchedInterpolationDDom>,
808 Layout,
809 memory_space> spline,
810 ddc::ChunkSpan<double const, BatchedInterpolationDDom, Layout, memory_space> vals,
811 std::optional<ddc::ChunkSpan<
812 double const,
813 batched_derivs_domain_type<BatchedInterpolationDDom>,
814 Layout,
815 memory_space>> const derivs_xmin,
816 std::optional<ddc::ChunkSpan<
817 double const,
818 batched_derivs_domain_type<BatchedInterpolationDDom>,
819 Layout,
820 memory_space>> const derivs_xmax) const
821{
822 auto const batched_interpolation_domain = vals.domain();
823
824 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
825
826 assert(vals.template extent<interpolation_discrete_dimension_type>()
827 == ddc::discrete_space<bsplines_type>().nbasis() - s_nbc_xmin - s_nbc_xmax);
828
829 assert((BcLower == ddc::BoundCond::HERMITE)
830 != (!derivs_xmin.has_value() || derivs_xmin->template extent<deriv_type>() == 0));
831 assert((BcUpper == ddc::BoundCond::HERMITE)
832 != (!derivs_xmax.has_value() || derivs_xmax->template extent<deriv_type>() == 0));
833 if constexpr (BcLower == BoundCond::HERMITE) {
834 assert(ddc::DiscreteElement<deriv_type>(derivs_xmin->domain().front()).uid() == 1);
835 }
836 if constexpr (BcUpper == BoundCond::HERMITE) {
837 assert(ddc::DiscreteElement<deriv_type>(derivs_xmax->domain().front()).uid() == 1);
838 }
839
840 // Hermite boundary conditions at xmin, if any
841 // NOTE: For consistency with the linear system, the i-th derivative
842 // provided by the user must be multiplied by dx^i
843 if constexpr (BcLower == BoundCond::HERMITE) {
844 assert(derivs_xmin->template extent<deriv_type>() == s_nbc_xmin);
845 auto derivs_xmin_values = *derivs_xmin;
846 auto const dx_proxy = m_dx;
847 ddc::parallel_for_each(
848 "ddc_splines_hermite_compute_lower_coefficients",
849 exec_space(),
850 batch_domain(batched_interpolation_domain),
851 KOKKOS_LAMBDA(
852 typename batch_domain_type<BatchedInterpolationDDom>::discrete_element_type
853 j) {
854 for (int i = s_nbc_xmin; i > 0; --i) {
855 spline(ddc::DiscreteElement<bsplines_type>(s_nbc_xmin - i), j)
856 = derivs_xmin_values(ddc::DiscreteElement<deriv_type>(i), j)
857 * ddc::detail::ipow(dx_proxy, i + s_odd - 1);
858 }
859 });
860 }
861
862 // Fill spline with vals (to work in spline afterward and preserve vals)
863 ddc::parallel_fill(
864 exec_space(),
865 spline[ddc::DiscreteDomain<bsplines_type>(
866 ddc::DiscreteElement<bsplines_type>(s_nbc_xmin),
867 ddc::DiscreteVector<bsplines_type>(m_offset))],
868 0.);
869 // NOTE: We rely on Kokkos::deep_copy because ddc::parallel_deepcopy do not support
870 // different domain-typed Chunks.
871 Kokkos::deep_copy(
872 exec_space(),
873 spline[ddc::DiscreteDomain<bsplines_type>(
874 ddc::DiscreteElement<bsplines_type>(s_nbc_xmin + m_offset),
875 ddc::DiscreteVector<bsplines_type>(static_cast<std::size_t>(
876 vals.domain()
877 .template extent<
878 interpolation_discrete_dimension_type>())))]
879 .allocation_kokkos_view(),
880 vals.allocation_kokkos_view());
881
882
883
884 // Hermite boundary conditions at xmax, if any
885 // NOTE: For consistency with the linear system, the i-th derivative
886 // provided by the user must be multiplied by dx^i
887 auto const& nbasis_proxy = ddc::discrete_space<bsplines_type>().nbasis();
888 if constexpr (BcUpper == BoundCond::HERMITE) {
889 assert(derivs_xmax->template extent<deriv_type>() == s_nbc_xmax);
890 auto derivs_xmax_values = *derivs_xmax;
891 auto const dx_proxy = m_dx;
892 ddc::parallel_for_each(
893 "ddc_splines_hermite_compute_upper_coefficients",
894 exec_space(),
895 batch_domain(batched_interpolation_domain),
896 KOKKOS_LAMBDA(
897 typename batch_domain_type<BatchedInterpolationDDom>::discrete_element_type
898 j) {
899 for (int i = 0; i < s_nbc_xmax; ++i) {
900 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy - s_nbc_xmax - i),
901 j)
902 = derivs_xmax_values(ddc::DiscreteElement<deriv_type>(i + 1), j)
903 * ddc::detail::ipow(dx_proxy, i + s_odd);
904 }
905 });
906 }
907
908 // 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
909 auto const& offset_proxy = m_offset;
910 ddc::Chunk spline_tr_alloc(
911 batched_spline_tr_domain(batched_interpolation_domain),
912 ddc::KokkosAllocator<double, memory_space>());
913 ddc::ChunkSpan const spline_tr = spline_tr_alloc.span_view();
914 ddc::parallel_for_each(
915 "ddc_splines_transpose_rhs",
916 exec_space(),
917 batch_domain(batched_interpolation_domain),
918 KOKKOS_LAMBDA(
919 typename batch_domain_type<
920 BatchedInterpolationDDom>::discrete_element_type const j) {
921 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
922 spline_tr(ddc::DiscreteElement<bsplines_type>(i), j)
923 = spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j);
924 }
925 });
926 // Create a 2D Kokkos::View to manage spline_tr as a matrix
927 Kokkos::View<double**, Kokkos::LayoutRight, exec_space> const bcoef_section(
928 spline_tr.data_handle(),
929 static_cast<std::size_t>(spline_tr.template extent<bsplines_type>()),
930 batch_domain(batched_interpolation_domain).size());
931 // Compute spline coef
932 m_matrix->solve(bcoef_section, false);
933 // Transpose back spline_tr into spline.
934 ddc::parallel_for_each(
935 "ddc_splines_transpose_back_rhs",
936 exec_space(),
937 batch_domain(batched_interpolation_domain),
938 KOKKOS_LAMBDA(
939 typename batch_domain_type<
940 BatchedInterpolationDDom>::discrete_element_type const j) {
941 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
942 spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j)
943 = spline_tr(ddc::DiscreteElement<bsplines_type>(i), j);
944 }
945 });
946
947 // Duplicate the lower spline coefficients to the upper side in case of periodic boundaries
948 if (bsplines_type::is_periodic()) {
949 ddc::parallel_for_each(
950 "ddc_splines_periodic_rows_duplicate_rhs",
951 exec_space(),
952 batch_domain(batched_interpolation_domain),
953 KOKKOS_LAMBDA(
954 typename batch_domain_type<
955 BatchedInterpolationDDom>::discrete_element_type const j) {
956 if (offset_proxy != 0) {
957 for (int i = 0; i < offset_proxy; ++i) {
958 spline(ddc::DiscreteElement<bsplines_type>(i), j) = spline(
959 ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i),
960 j);
961 }
962 for (std::size_t i = offset_proxy; i < bsplines_type::degree(); ++i) {
963 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i), j)
964 = spline(ddc::DiscreteElement<bsplines_type>(i), j);
965 }
966 }
967 for (std::size_t i(0); i < bsplines_type::degree(); ++i) {
968 ddc::DiscreteElement<bsplines_type> const i_start(i);
969 ddc::DiscreteElement<bsplines_type> const i_end(nbasis_proxy + i);
970
971 spline(i_end, j) = spline(i_start, j);
972 }
973 });
974 }
975}
976
977template <
978 class ExecSpace,
979 class MemorySpace,
980 class BSplines,
981 class InterpolationDDim,
985template <class OutMemorySpace>
986std::tuple<
987 ddc::Chunk<
988 double,
990 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
991 ddc::KokkosAllocator<double, OutMemorySpace>>,
992 ddc::Chunk<
993 double,
994 ddc::DiscreteDomain<InterpolationDDim>,
995 ddc::KokkosAllocator<double, OutMemorySpace>>,
996 ddc::Chunk<
997 double,
999 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
1000 ddc::KokkosAllocator<double, OutMemorySpace>>>
1001SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, BcLower, BcUpper, Solver>::
1003{
1004 // Compute integrals of bsplines
1005 ddc::Chunk integral_bsplines(spline_domain(), ddc::KokkosAllocator<double, MemorySpace>());
1006 ddc::integrals(ExecSpace(), integral_bsplines.span_view());
1007
1008 // Remove additional B-splines in the periodic case (cf. UniformBSplines::full_domain() documentation)
1009 ddc::ChunkSpan const integral_bsplines_without_periodic_additional_bsplines
1010 = integral_bsplines[spline_domain().take_first(
1011 ddc::DiscreteVector<bsplines_type>(m_matrix->size()))];
1012
1013 // Allocate mirror with additional rows (cf. SplinesLinearProblem3x3Blocks documentation)
1014 Kokkos::View<double**, Kokkos::LayoutRight, MemorySpace> const
1015 integral_bsplines_mirror_with_additional_allocation(
1016 "integral_bsplines_mirror_with_additional_allocation",
1017 m_matrix->required_number_of_rhs_rows(),
1018 1);
1019
1020 // Extract relevant subview
1021 Kokkos::View<double*, Kokkos::LayoutRight, MemorySpace> const integral_bsplines_mirror
1022 = Kokkos::
1023 subview(integral_bsplines_mirror_with_additional_allocation,
1024 std::
1025 pair {static_cast<std::size_t>(0),
1026 integral_bsplines_without_periodic_additional_bsplines
1027 .size()},
1028 0);
1029
1030 // Solve matrix equation A^t*X=integral_bsplines
1031 Kokkos::deep_copy(
1032 integral_bsplines_mirror,
1033 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view());
1034 m_matrix->solve(integral_bsplines_mirror_with_additional_allocation, true);
1035 Kokkos::deep_copy(
1036 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view(),
1037 integral_bsplines_mirror);
1038
1039 // Slice into three ChunkSpan corresponding to lower derivatives, function values and upper derivatives
1040 ddc::ChunkSpan const coefficients_derivs_xmin
1041 = integral_bsplines_without_periodic_additional_bsplines[spline_domain().take_first(
1042 ddc::DiscreteVector<bsplines_type>(s_nbc_xmin))];
1043 ddc::ChunkSpan const coefficients = integral_bsplines_without_periodic_additional_bsplines
1045 .remove_first(ddc::DiscreteVector<bsplines_type>(s_nbc_xmin))
1046 .take_first(
1047 ddc::DiscreteVector<bsplines_type>(
1048 ddc::discrete_space<bsplines_type>().nbasis() - s_nbc_xmin
1049 - s_nbc_xmax))];
1050 ddc::ChunkSpan const coefficients_derivs_xmax
1051 = integral_bsplines_without_periodic_additional_bsplines
1053 .remove_first(
1054 ddc::DiscreteVector<bsplines_type>(
1055 s_nbc_xmin + coefficients.size()))
1056 .take_first(ddc::DiscreteVector<bsplines_type>(s_nbc_xmax))];
1057 interpolation_domain_type const interpolation_domain_proxy = interpolation_domain();
1058
1059 // Multiply derivatives coefficients by dx^n
1060 ddc::parallel_for_each(
1061 exec_space(),
1062 coefficients_derivs_xmin.domain(),
1063 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1064 ddc::Coordinate<continuous_dimension_type> const dx
1065 = ddc::distance_at_right(interpolation_domain_proxy.front() + 1);
1066 coefficients_derivs_xmin(i) *= ddc::detail::
1067 ipow(dx,
1068 static_cast<std::size_t>(get<bsplines_type>(
1069 i - coefficients_derivs_xmin.domain().front() + 1)));
1070 });
1071 ddc::parallel_for_each(
1072 exec_space(),
1073 coefficients_derivs_xmax.domain(),
1074 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1075 ddc::Coordinate<continuous_dimension_type> const dx
1076 = ddc::distance_at_left(interpolation_domain_proxy.back() - 1);
1077 coefficients_derivs_xmax(i) *= ddc::detail::
1078 ipow(dx,
1079 static_cast<std::size_t>(get<bsplines_type>(
1080 i - coefficients_derivs_xmax.domain().front() + 1)));
1081 });
1082
1083 // Allocate Chunk on deriv_type and interpolation_discrete_dimension_type and copy quadrature coefficients into it
1084 ddc::Chunk coefficients_derivs_xmin_out(
1085 ddc::DiscreteDomain<deriv_type>(
1086 ddc::DiscreteElement<deriv_type>(1),
1087 ddc::DiscreteVector<deriv_type>(s_nbc_xmin)),
1088 ddc::KokkosAllocator<double, OutMemorySpace>());
1089 ddc::Chunk coefficients_out(
1090 interpolation_domain().take_first(
1091 ddc::DiscreteVector<interpolation_discrete_dimension_type>(
1092 coefficients.size())),
1093 ddc::KokkosAllocator<double, OutMemorySpace>());
1094 ddc::Chunk coefficients_derivs_xmax_out(
1095 ddc::DiscreteDomain<deriv_type>(
1096 ddc::DiscreteElement<deriv_type>(1),
1097 ddc::DiscreteVector<deriv_type>(s_nbc_xmax)),
1098 ddc::KokkosAllocator<double, OutMemorySpace>());
1099 Kokkos::deep_copy(
1100 coefficients_derivs_xmin_out.allocation_kokkos_view(),
1101 coefficients_derivs_xmin.allocation_kokkos_view());
1102 Kokkos::deep_copy(
1103 coefficients_out.allocation_kokkos_view(),
1104 coefficients.allocation_kokkos_view());
1105 Kokkos::deep_copy(
1106 coefficients_derivs_xmax_out.allocation_kokkos_view(),
1107 coefficients_derivs_xmax.allocation_kokkos_view());
1108 return std::make_tuple(
1109 std::move(coefficients_derivs_xmin_out),
1110 std::move(coefficients_out),
1111 std::move(coefficients_derivs_xmax_out));
1112}
1113
1114template <
1115 class ExecSpace,
1116 class MemorySpace,
1117 class BSplines,
1118 class InterpolationDDim,
1122template <class KnotElement>
1123void SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, BcLower, BcUpper, Solver>::
1124 check_n_points_in_cell(int const n_points_in_cell, KnotElement const current_cell_end_idx)
1125{
1126 if (n_points_in_cell > BSplines::degree() + 1) {
1127 KnotElement const rmin_idx = ddc::discrete_space<BSplines>().break_point_domain().front();
1128 int const failed_cell = (current_cell_end_idx - rmin_idx).value();
1129 throw std::runtime_error(
1130 "The spline problem is overconstrained. There are "
1131 + std::to_string(n_points_in_cell) + " points in the " + std::to_string(failed_cell)
1132 + "-th cell.");
1133 }
1134}
1135
1136template <
1137 class ExecSpace,
1138 class MemorySpace,
1139 class BSplines,
1140 class InterpolationDDim,
1141 ddc::BoundCond BcLower,
1142 ddc::BoundCond BcUpper,
1143 SplineSolver Solver>
1144void SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, BcLower, BcUpper, Solver>::
1145 check_valid_grid()
1146{
1147 std::size_t const n_interp_points = interpolation_domain().size();
1148 std::size_t const expected_npoints
1149 = ddc::discrete_space<BSplines>().nbasis() - s_nbc_xmin - s_nbc_xmax;
1150 if (n_interp_points != expected_npoints) {
1151 throw std::runtime_error(
1152 "Incorrect number of points supplied to NonUniformInterpolationPoints. "
1153 "(Received : "
1154 + std::to_string(n_interp_points)
1155 + ", expected : " + std::to_string(expected_npoints));
1156 }
1157 int n_points_in_cell = 0;
1158 auto current_cell_end_idx = ddc::discrete_space<BSplines>().break_point_domain().front() + 1;
1159 ddc::host_for_each(interpolation_domain(), [&](auto idx) {
1160 ddc::Coordinate<continuous_dimension_type> const point = ddc::coordinate(idx);
1161 if (point > ddc::coordinate(current_cell_end_idx)) {
1162 // Check the points found in the previous cell
1163 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1164 // Initialise the number of points in the subsequent cell, including the new point
1165 n_points_in_cell = 1;
1166 // Move to the next cell
1167 current_cell_end_idx += 1;
1168 } else if (point == ddc::coordinate(current_cell_end_idx)) {
1169 // Check the points found in the previous cell including the point on the boundary
1170 check_n_points_in_cell(n_points_in_cell + 1, current_cell_end_idx);
1171 // Initialise the number of points in the subsequent cell, including the point on the boundary
1172 n_points_in_cell = 1;
1173 // Move to the next cell
1174 current_cell_end_idx += 1;
1175 } else {
1176 // Indicate that the point is in the cell
1177 n_points_in_cell += 1;
1178 }
1179 });
1180 // Check the number of points in the final cell
1181 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1182}
1183
1184} // namespace ddc
friend class ChunkSpan
friend class Chunk
Definition chunk.hpp:81
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