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