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