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