DDC 0.15.0
Loading...
Searching...
No Matches
spline_builder.hpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <array>
8#include <cassert>
9#include <cstddef>
10#include <memory>
11#include <optional>
12#include <stdexcept>
13#include <string>
14#include <tuple>
15#include <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
185 /// @brief The boundary condition implemented at the lower bound.
186 /// @deprecated Use s_sbc_xmin instead
187 [[deprecated("Use s_sbc_xmin instead")]] static constexpr ddc::SplineBuilderClosure s_bc_xmin
188 = SBCLower;
189
190 /// @brief The boundary condition implemented at the upper bound.
191 /// @deprecated Use s_sbc_xmax instead
192 [[deprecated("Use s_sbc_xmax instead")]] static constexpr ddc::SplineBuilderClosure s_bc_xmax
193 = SBCUpper;
194#endif
195
196 /// @brief The closure relation implemented at the lower bound.
197 static constexpr ddc::SplineBuilderClosure s_sbc_xmin = SBCLower;
198
199 /// @brief The closure relation implemented at the upper bound.
200 static constexpr ddc::SplineBuilderClosure s_sbc_xmax = SBCUpper;
201
202 /// @brief The SplineSolver giving the backend used to perform the spline approximation.
203 static constexpr SplineSolver s_spline_solver = Solver;
204
205private:
206 interpolation_domain_type m_interpolation_domain;
207
208 int m_offset = 0;
209
210 double m_dx; // average cell size for normalization of derivatives
211
212 // interpolator specific
213 std::unique_ptr<ddc::detail::SplinesLinearProblem<exec_space>> m_matrix;
214
215 std::string m_label;
216
217 /// Calculate offset so that the matrix is diagonally dominant
218 void compute_offset(interpolation_domain_type const& interpolation_domain, int& offset);
219
220public:
221 /**
222 * @brief Build a SplineBuilder acting on interpolation_domain.
223 *
224 * @param label A label used to tag parallel regions and memory allocations for profiling.
225 *
226 * @param interpolation_domain The domain on which the interpolation points are defined.
227 *
228 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
229 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
230 * by the linear solver one-after-the-other).
231 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
232 *
233 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
234 * define the size of a block used by the Block-Jacobi preconditioner.
235 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
236 *
237 * @see MatrixSparse
238 */
239 explicit SplineBuilder(
240 std::string label,
241 interpolation_domain_type const& interpolation_domain,
242 std::optional<std::size_t> cols_per_chunk = std::nullopt,
243 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
244 : m_interpolation_domain(interpolation_domain)
245 , m_dx((ddc::discrete_space<BSplines>().rmax() - ddc::discrete_space<BSplines>().rmin())
246 / ddc::discrete_space<BSplines>().ncells())
247 , m_label(std::move(label))
248 {
249 static_assert(
250 ((SBCLower == SplineBuilderClosure::PERIODIC)
251 == (SBCUpper == SplineBuilderClosure::PERIODIC)),
252 "Incompatible closure relations");
253 check_valid_grid();
254
255 compute_offset(this->interpolation_domain(), m_offset);
256
257 // Calculate block sizes
258 int lower_block_size;
259 int upper_block_size;
260 if constexpr (bsplines_type::is_uniform()) {
261 upper_block_size = compute_block_sizes_uniform(SBCLower, s_nbe_xmin);
262 lower_block_size = compute_block_sizes_uniform(SBCUpper, s_nbe_xmax);
263 } else {
264 upper_block_size = compute_block_sizes_non_uniform(SBCLower, s_nbe_xmin);
265 lower_block_size = compute_block_sizes_non_uniform(SBCUpper, s_nbe_xmax);
266 }
267 allocate_matrix(
268 lower_block_size,
269 upper_block_size,
270 cols_per_chunk,
271 preconditioner_max_block_size);
272 }
273
274 /**
275 * @brief Build a SplineBuilder acting on interpolation_domain.
276 *
277 * @param interpolation_domain The domain on which the interpolation points are defined.
278 *
279 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
280 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
281 * by the linear solver one-after-the-other).
282 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
283 *
284 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
285 * define the size of a block used by the Block-Jacobi preconditioner.
286 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
287 *
288 * @see MatrixSparse
289 */
290 explicit SplineBuilder(
291 interpolation_domain_type const& interpolation_domain,
292 std::optional<std::size_t> cols_per_chunk = std::nullopt,
293 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
295 "no-label",
296 interpolation_domain,
297 cols_per_chunk,
298 preconditioner_max_block_size)
299 {
300 }
301
302 /**
303 * @brief Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
304 *
305 * @param label A label used to tag parallel regions and memory allocations for profiling.
306 *
307 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
308 *
309 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
310 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
311 * by the linear solver one-after-the-other).
312 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
313 *
314 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
315 * define the size of a block used by the Block-Jacobi preconditioner.
316 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
317 *
318 * @see MatrixSparse
319 */
320 template <concepts::discrete_domain BatchedInterpolationDDom>
321 explicit SplineBuilder(
322 std::string label,
323 BatchedInterpolationDDom const& batched_interpolation_domain,
324 std::optional<std::size_t> cols_per_chunk = std::nullopt,
325 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
327 std::move(label),
328 interpolation_domain_type(batched_interpolation_domain),
329 cols_per_chunk,
330 preconditioner_max_block_size)
331 {
332 }
333
334 /**
335 * @brief Build a SplineBuilder acting on the interpolation domain contained by batched_interpolation_domain.
336 *
337 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
338 *
339 * @param cols_per_chunk A parameter used by the slicer (internal to the solver) to define the size
340 * of a chunk of right-hand sides of the linear problem to be computed in parallel (chunks are treated
341 * by the linear solver one-after-the-other).
342 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
343 *
344 * @param preconditioner_max_block_size A parameter used by the slicer (internal to the solver) to
345 * define the size of a block used by the Block-Jacobi preconditioner.
346 * This value is optional. If no value is provided then the default value is chosen by the requested solver.
347 *
348 * @see MatrixSparse
349 */
350 template <concepts::discrete_domain BatchedInterpolationDDom>
351 explicit SplineBuilder(
352 BatchedInterpolationDDom const& batched_interpolation_domain,
353 std::optional<std::size_t> cols_per_chunk = std::nullopt,
354 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt)
356 "no-label",
357 interpolation_domain_type(batched_interpolation_domain),
358 cols_per_chunk,
359 preconditioner_max_block_size)
360 {
361 }
362
363
364 /// @brief Copy-constructor is deleted.
365 SplineBuilder(SplineBuilder const& x) = delete;
366
367 /** @brief Move-constructs.
368 *
369 * @param x An rvalue to another SplineBuilder.
370 */
371 SplineBuilder(SplineBuilder&& x) = default;
372
373 /// @brief Destructs.
374 ~SplineBuilder() = default;
375
376 /// @brief Copy-assignment is deleted.
377 SplineBuilder& operator=(SplineBuilder const& x) = delete;
378
379 /** @brief Move-assigns.
380 *
381 * @param x An rvalue to another SplineBuilder.
382 * @return A reference to this object.
383 */
384 SplineBuilder& operator=(SplineBuilder&& x) = default;
385
386 /**
387 * @brief Get the domain for the 1D interpolation mesh used by this class.
388 *
389 * This is 1D because it is defined along the dimension of interest.
390 *
391 * @return The 1D domain for the interpolation mesh.
392 */
393 interpolation_domain_type interpolation_domain() const noexcept
394 {
395 return m_interpolation_domain;
396 }
397
398 /**
399 * @brief Get the whole domain representing interpolation points.
400 *
401 * Values of the function must be provided on this domain in order
402 * to build a spline representation of the function (cartesian product of 1D interpolation_domain and batch_domain).
403 *
404 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
405 *
406 * @return The domain for the interpolation mesh.
407 */
408 template <concepts::discrete_domain BatchedInterpolationDDom>
409 BatchedInterpolationDDom batched_interpolation_domain(
410 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
411 {
412 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
413 return batched_interpolation_domain;
414 }
415
416 /**
417 * @brief Get the batch domain.
418 *
419 * Obtained by removing the dimension of interest from the whole interpolation domain.
420 *
421 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
422 *
423 * @return The batch domain.
424 */
425 template <class BatchedInterpolationDDom>
426 batch_domain_type<BatchedInterpolationDDom> batch_domain(
427 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
428 {
429 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
430 return ddc::remove_dims_of(batched_interpolation_domain, interpolation_domain());
431 }
432
433 /**
434 * @brief Get the 1D domain on which spline coefficients are defined.
435 *
436 * The 1D spline domain corresponding to the dimension of interest.
437 *
438 * @return The 1D domain for the spline coefficients.
439 */
440 ddc::DiscreteDomain<bsplines_type> spline_domain() const noexcept
441 {
442 return ddc::discrete_space<bsplines_type>().full_domain();
443 }
444
445 /**
446 * @brief Get the whole domain on which spline coefficients are defined.
447 *
448 * Spline approximations (spline-transformed functions) are computed on this domain.
449 *
450 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
451 *
452 * @return The domain for the spline coefficients.
453 */
454 template <class BatchedInterpolationDDom>
455 batched_spline_domain_type<BatchedInterpolationDDom> batched_spline_domain(
456 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
457 {
458 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
459 return ddc::replace_dim_of<
460 interpolation_discrete_dimension_type,
461 bsplines_type>(batched_interpolation_domain, spline_domain());
462 }
463
464private:
465 /**
466 * @brief Get the whole domain on which spline coefficients are defined, with the dimension of interest being the leading dimension.
467 *
468 * 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.
469 *
470 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
471 *
472 * @return The (transposed) domain for the spline coefficients.
473 */
474 template <class BatchedInterpolationDDom>
475 batched_spline_tr_domain_type<BatchedInterpolationDDom> batched_spline_tr_domain(
476 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
477 {
478 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
479 return batched_spline_tr_domain_type<BatchedInterpolationDDom>(
480 ddc::replace_dim_of<bsplines_type, bsplines_type>(
481 batched_spline_domain(batched_interpolation_domain),
482 ddc::DiscreteDomain<bsplines_type>(
483 ddc::DiscreteElement<bsplines_type>(0),
484 ddc::DiscreteVector<bsplines_type>(
485 m_matrix->required_number_of_rhs_rows()))));
486 }
487
488public:
489 /**
490 * @brief Get the whole domain on which derivatives on lower boundary are defined.
491 *
492 * This is only used with SplineBuilderClosure::HERMITE closure relations.
493 *
494 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
495 *
496 * @return The domain for the Derivs values.
497 */
498 template <class BatchedInterpolationDDom>
499 batched_derivs_domain_type<BatchedInterpolationDDom> batched_derivs_xmin_domain(
500 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
501 {
502 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
503 return ddc::replace_dim_of<interpolation_discrete_dimension_type, deriv_type>(
504 batched_interpolation_domain,
505 ddc::DiscreteDomain<deriv_type>(
506 ddc::DiscreteElement<deriv_type>(1),
507 ddc::DiscreteVector<deriv_type>(s_nbv_xmin)));
508 }
509
510 /**
511 * @brief Get the whole domain on which derivatives on upper boundary are defined.
512 *
513 * This is only used with SplineBuilderClosure::HERMITE closure relations.
514 *
515 * @param batched_interpolation_domain The whole domain on which the interpolation points are defined.
516 *
517 * @return The domain for the Derivs values.
518 */
519 template <class BatchedInterpolationDDom>
520 batched_derivs_domain_type<BatchedInterpolationDDom> batched_derivs_xmax_domain(
521 BatchedInterpolationDDom const& batched_interpolation_domain) const noexcept
522 {
523 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
524 return ddc::replace_dim_of<interpolation_discrete_dimension_type, deriv_type>(
525 batched_interpolation_domain,
526 ddc::DiscreteDomain<deriv_type>(
527 ddc::DiscreteElement<deriv_type>(1),
528 ddc::DiscreteVector<deriv_type>(s_nbv_xmax)));
529 }
530
531 /**
532 * @brief Compute a spline approximation of a function.
533 *
534 * Use the values of a function (defined on
535 * SplineBuilder::batched_interpolation_domain) and the derivatives of the
536 * function at the boundaries (in the case of SplineBuilderClosure::HERMITE only, defined
537 * on SplineBuilder::batched_derivs_xmin_domain and SplineBuilder::batched_derivs_xmax_domain)
538 * to calculate a spline approximation of this function.
539 *
540 * The spline approximation is stored as a ChunkSpan of coefficients
541 * associated with B-splines.
542 *
543 * @param[out] spline The coefficients of the spline computed by this SplineBuilder.
544 * @param[in] vals The values of the function on the interpolation mesh.
545 * @param[in] derivs_xmin The values of the derivatives at the lower boundary
546 * (used only with SplineBuilderClosure::HERMITE lower closure relation).
547 * @param[in] derivs_xmax The values of the derivatives at the upper boundary
548 * (used only with SplineBuilderClosure::HERMITE upper closure relation).
549 */
550 template <class Layout, class BatchedInterpolationDDom>
551 void operator()(
552 ddc::ChunkSpan<
553 double,
554 batched_spline_domain_type<BatchedInterpolationDDom>,
555 Layout,
556 memory_space> spline,
557 ddc::ChunkSpan<double const, BatchedInterpolationDDom, Layout, memory_space> vals,
558 std::optional<ddc::ChunkSpan<
559 double const,
560 batched_derivs_domain_type<BatchedInterpolationDDom>,
561 Layout,
562 memory_space>> derivs_xmin
563 = std::nullopt,
564 std::optional<ddc::ChunkSpan<
565 double const,
566 batched_derivs_domain_type<BatchedInterpolationDDom>,
567 Layout,
568 memory_space>> derivs_xmax
569 = std::nullopt) const;
570
571 /**
572 * @brief Compute the quadrature coefficients associated to the b-splines used by this SplineBuilder.
573 *
574 * Those coefficients can be used to perform integration way faster than SplineEvaluator::integrate().
575 *
576 * This function solves matrix equation A^t*Q=integral_bsplines. In case of HERMITE closure relations,
577 * integral_bsplines contains the integral coefficients at the boundaries, and Q thus has to
578 * be split in three parts (quadrature coefficients for the derivatives at lower boundary,
579 * for the values inside the domain and for the derivatives at upper boundary).
580 *
581 * A discrete function f can then be integrated using sum_j Q_j*f_j for j in interpolation_domain.
582 * If closure relation is HERMITE, sum_j Qderiv_j*(d^j f/dx^j) for j in derivs_domain
583 * must be added at the boundary.
584 *
585 * Please refer to section 2.8.1 of Emily's Bourne phd (https://theses.fr/2022AIXM0412) for more information and to
586 * the (Non)PeriodicSplineBuilderTest for example usage to compute integrals.
587 *
588 * @tparam OutMemorySpace The Kokkos::MemorySpace on which the quadrature coefficients are be returned
589 * (but they are computed on ExecSpace then copied).
590 *
591 * @return A tuple containing the three Chunks containing the quadrature coefficients (if HERMITE
592 * is not used, first and third are empty).
593 */
594 template <class OutMemorySpace = MemorySpace>
595 std::tuple<
596 ddc::Chunk<
597 double,
599 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
600 ddc::KokkosAllocator<double, OutMemorySpace>>,
601 ddc::Chunk<
602 double,
603 ddc::DiscreteDomain<InterpolationDDim>,
604 ddc::KokkosAllocator<double, OutMemorySpace>>,
605 ddc::Chunk<
606 double,
608 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
609 ddc::KokkosAllocator<double, OutMemorySpace>>>
611
612private:
613 static int compute_block_sizes_uniform(ddc::SplineBuilderClosure bound_cond, int nbc);
614
615 static int compute_block_sizes_non_uniform(ddc::SplineBuilderClosure bound_cond, int nbc);
616
617 void allocate_matrix(
618 int lower_block_size,
619 int upper_block_size,
620 std::optional<std::size_t> cols_per_chunk = std::nullopt,
621 std::optional<unsigned int> preconditioner_max_block_size = std::nullopt);
622
623 void build_matrix_system();
624
625 void check_valid_grid();
626
627 template <class KnotElement>
628 static void check_n_points_in_cell(int n_points_in_cell, KnotElement current_cell_end_idx);
629};
630
631template <
632 class ExecSpace,
633 class MemorySpace,
634 class BSplines,
635 class InterpolationDDim,
636 ddc::SplineBuilderClosure SBCLower,
637 ddc::SplineBuilderClosure SBCUpper,
638 SplineSolver Solver>
639void SplineBuilder<
640 ExecSpace,
641 MemorySpace,
642 BSplines,
643 InterpolationDDim,
644 SBCLower,
645 SBCUpper,
646 Solver>::compute_offset(interpolation_domain_type const& interpolation_domain, int& offset)
647{
648 if constexpr (bsplines_type::is_periodic()) {
649 // Calculate offset so that the matrix is diagonally dominant
650 std::array<double, bsplines_type::degree() + 1> values_ptr;
651 Kokkos::mdspan<double, Kokkos::extents<std::size_t, bsplines_type::degree() + 1>> const
652 values(values_ptr.data());
653 ddc::DiscreteElement<interpolation_discrete_dimension_type> start(
654 interpolation_domain.front());
655 auto jmin = ddc::discrete_space<BSplines>()
656 .eval_basis(values, ddc::coordinate(start + BSplines::degree()));
657 if constexpr (bsplines_type::degree() % 2 == 0) {
658 offset = jmin.uid() - start.uid() + bsplines_type::degree() / 2 - BSplines::degree();
659 } else {
660 int const mid = bsplines_type::degree() / 2;
661 offset = jmin.uid() - start.uid()
662 + (DDC_MDSPAN_ACCESS_OP(values, mid) > DDC_MDSPAN_ACCESS_OP(values, mid + 1)
663 ? mid
664 : mid + 1)
665 - BSplines::degree();
666 }
667 } else {
668 offset = 0;
669 }
670}
671
672template <
673 class ExecSpace,
674 class MemorySpace,
675 class BSplines,
676 class InterpolationDDim,
677 ddc::SplineBuilderClosure SBCLower,
678 ddc::SplineBuilderClosure SBCUpper,
679 SplineSolver Solver>
680int SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
681 compute_block_sizes_uniform(ddc::SplineBuilderClosure const bound_cond, int const nbc)
682{
683 if (bound_cond == ddc::SplineBuilderClosure::PERIODIC) {
684 return static_cast<int>(bsplines_type::degree()) / 2;
685 }
686
687 if (bound_cond == ddc::SplineBuilderClosure::HERMITE
689 return nbc;
690 }
691
692 if (bound_cond == ddc::SplineBuilderClosure::GREVILLE) {
693 return static_cast<int>(bsplines_type::degree()) - 1;
694 }
695
696 throw std::runtime_error("ddc::SplineBuilderClosure not handled");
697}
698
699template <
700 class ExecSpace,
701 class MemorySpace,
702 class BSplines,
703 class InterpolationDDim,
704 ddc::SplineBuilderClosure SBCLower,
705 ddc::SplineBuilderClosure SBCUpper,
706 SplineSolver Solver>
707int SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
708 compute_block_sizes_non_uniform(ddc::SplineBuilderClosure const bound_cond, int const nbc)
709{
711 || bound_cond == ddc::SplineBuilderClosure::GREVILLE) {
712 return static_cast<int>(bsplines_type::degree()) - 1;
713 }
714
715 if (bound_cond == ddc::SplineBuilderClosure::HERMITE
717 return nbc + 1;
718 }
719
720 throw std::runtime_error("ddc::SplineBuilderClosure not handled");
721}
722
723template <
724 class ExecSpace,
725 class MemorySpace,
726 class BSplines,
727 class InterpolationDDim,
728 ddc::SplineBuilderClosure SBCLower,
729 ddc::SplineBuilderClosure SBCUpper,
730 SplineSolver Solver>
731void SplineBuilder<
732 ExecSpace,
733 MemorySpace,
734 BSplines,
735 InterpolationDDim,
736 SBCLower,
737 SBCUpper,
738 Solver>::
739 allocate_matrix(
740 [[maybe_unused]] int lower_block_size,
741 [[maybe_unused]] int upper_block_size,
742 std::optional<std::size_t> cols_per_chunk,
743 std::optional<unsigned int> preconditioner_max_block_size)
744{
745 // Special case: linear spline
746 // No need for matrix assembly
747 // (disabled)
748 // if constexpr (bsplines_type::degree() == 1) {
749 // return;
750 // }
751
752 if constexpr (Solver == ddc::SplineSolver::LAPACK) {
753 int upper_band_width;
754 if (bsplines_type::is_uniform()) {
755 upper_band_width = bsplines_type::degree() / 2;
756 } else {
757 upper_band_width = bsplines_type::degree() - 1;
758 }
759 if constexpr (bsplines_type::is_periodic()) {
760 m_matrix = ddc::detail::SplinesLinearProblemMaker::make_new_periodic_band_matrix<
761 ExecSpace>(
762 ddc::discrete_space<BSplines>().nbasis(),
763 upper_band_width,
764 upper_band_width,
765 bsplines_type::is_uniform());
766 } else {
767 m_matrix = ddc::detail::SplinesLinearProblemMaker::
768 make_new_block_matrix_with_band_main_block<ExecSpace>(
769 ddc::discrete_space<BSplines>().nbasis(),
770 upper_band_width,
771 upper_band_width,
772 bsplines_type::is_uniform(),
773 lower_block_size,
774 upper_block_size);
775 }
776 } else if constexpr (Solver == ddc::SplineSolver::GINKGO) {
777 m_matrix = ddc::detail::SplinesLinearProblemMaker::make_new_sparse<ExecSpace>(
778 ddc::discrete_space<BSplines>().nbasis(),
779 cols_per_chunk,
780 preconditioner_max_block_size);
781 }
782
783 build_matrix_system();
784
785 m_matrix->setup_solver();
786}
787
788template <
789 class ExecSpace,
790 class MemorySpace,
791 class BSplines,
792 class InterpolationDDim,
793 ddc::SplineBuilderClosure SBCLower,
794 ddc::SplineBuilderClosure SBCUpper,
795 SplineSolver Solver>
796void SplineBuilder<
797 ExecSpace,
798 MemorySpace,
799 BSplines,
800 InterpolationDDim,
801 SBCLower,
802 SBCUpper,
803 Solver>::build_matrix_system()
804{
805 // Hermite closure relations at xmin, if any
806 if constexpr (
809 std::array<double, (bsplines_type::degree() / 2 + 1) * (bsplines_type::degree() + 1)>
810 derivs_ptr;
811 ddc::DSpan2D const
812 derivs(derivs_ptr.data(),
813 bsplines_type::degree() + 1,
814 bsplines_type::degree() / 2 + 1);
815 ddc::discrete_space<BSplines>().eval_basis_and_n_derivs(
816 derivs,
817 ddc::discrete_space<BSplines>().rmin(),
818 s_nbe_xmin);
819
820 // In order to improve the condition number of the matrix, we normalize
821 // all derivatives by multiplying the i-th derivative by dx^i
822 for (std::size_t i = 0; i < bsplines_type::degree() + 1; ++i) {
823 for (std::size_t j = 1; j < bsplines_type::degree() / 2 + 1; ++j) {
824 DDC_MDSPAN_ACCESS_OP(derivs, i, j) *= ddc::detail::ipow(m_dx, j);
825 }
826 }
827
828 if constexpr (s_nbe_xmin > 0) {
829 // iterate only to deg as last bspline is 0
830 for (std::size_t i = 0; i < s_nbe_xmin; ++i) {
831 for (std::size_t j = 0; j < bsplines_type::degree(); ++j) {
832 m_matrix->set_element(i, j, DDC_MDSPAN_ACCESS_OP(derivs, j, i + s_odd));
833 }
834 }
835 }
836 }
837
838 // Interpolation points
839 std::array<double, bsplines_type::degree() + 1> values_ptr;
840 Kokkos::mdspan<double, Kokkos::extents<std::size_t, bsplines_type::degree() + 1>> const values(
841 values_ptr.data());
842
843 int start = interpolation_domain().front().uid();
844 ddc::host_for_each(interpolation_domain(), [&](auto ix) {
845 auto jmin = ddc::discrete_space<BSplines>().eval_basis(
846 values,
847 ddc::coordinate(ddc::DiscreteElement<interpolation_discrete_dimension_type>(ix)));
848 for (std::size_t s = 0; s < bsplines_type::degree() + 1; ++s) {
849 int const j = ddc::detail::
850 modulo(int(jmin.uid() - m_offset + s),
851 static_cast<int>(ddc::discrete_space<BSplines>().nbasis()));
852 m_matrix->set_element(
853 ix.uid() - start + s_nbe_xmin,
854 j,
855 DDC_MDSPAN_ACCESS_OP(values, s));
856 }
857 });
858
859 // Hermite closure relations at xmax, if any
860 if constexpr (
863 std::array<double, (bsplines_type::degree() / 2 + 1) * (bsplines_type::degree() + 1)>
864 derivs_ptr;
865 Kokkos::mdspan<
866 double,
867 Kokkos::extents<
868 std::size_t,
869 bsplines_type::degree() + 1,
870 bsplines_type::degree() / 2 + 1>> const derivs(derivs_ptr.data());
871
872 ddc::discrete_space<BSplines>().eval_basis_and_n_derivs(
873 derivs,
874 ddc::discrete_space<BSplines>().rmax(),
875 s_nbe_xmax);
876
877 // In order to improve the condition number of the matrix, we normalize
878 // all derivatives by multiplying the i-th derivative by dx^i
879 for (std::size_t i = 0; i < bsplines_type::degree() + 1; ++i) {
880 for (std::size_t j = 1; j < bsplines_type::degree() / 2 + 1; ++j) {
881 DDC_MDSPAN_ACCESS_OP(derivs, i, j) *= ddc::detail::ipow(m_dx, j);
882 }
883 }
884
885 if constexpr (s_nbe_xmax > 0) {
886 int const i0 = ddc::discrete_space<BSplines>().nbasis() - s_nbe_xmax;
887 int const j0 = ddc::discrete_space<BSplines>().nbasis() - bsplines_type::degree();
888 for (std::size_t j = 0; j < bsplines_type::degree(); ++j) {
889 for (std::size_t i = 0; i < s_nbe_xmax; ++i) {
890 m_matrix->set_element(
891 i0 + i,
892 j0 + j,
893 DDC_MDSPAN_ACCESS_OP(derivs, j + 1, i + s_odd));
894 }
895 }
896 }
897 }
898}
899
900template <
901 class ExecSpace,
902 class MemorySpace,
903 class BSplines,
904 class InterpolationDDim,
908template <class Layout, class BatchedInterpolationDDom>
909void SplineBuilder<
910 ExecSpace,
911 MemorySpace,
912 BSplines,
913 InterpolationDDim,
914 SBCLower,
915 SBCUpper,
916 Solver>::
917operator()(
918 ddc::ChunkSpan<
919 double,
920 batched_spline_domain_type<BatchedInterpolationDDom>,
921 Layout,
922 memory_space> spline,
923 ddc::ChunkSpan<double const, BatchedInterpolationDDom, Layout, memory_space> vals,
924 std::optional<ddc::ChunkSpan<
925 double const,
926 batched_derivs_domain_type<BatchedInterpolationDDom>,
927 Layout,
928 memory_space>> const derivs_xmin,
929 std::optional<ddc::ChunkSpan<
930 double const,
931 batched_derivs_domain_type<BatchedInterpolationDDom>,
932 Layout,
933 memory_space>> const derivs_xmax) const
934{
935 auto const batched_interpolation_domain = vals.domain();
936
937 assert(interpolation_domain() == interpolation_domain_type(batched_interpolation_domain));
938 assert(batch_domain_type<BatchedInterpolationDDom>(batched_interpolation_domain)
939 == batch_domain_type<BatchedInterpolationDDom>(spline.domain()));
940
941 if (batch_domain(batched_interpolation_domain).empty()) {
942 return;
943 }
944
945 assert(vals.template extent<interpolation_discrete_dimension_type>()
946 == ddc::discrete_space<bsplines_type>().nbasis() - s_nbe_xmin - s_nbe_xmax);
947
948 if constexpr (SBCLower == SplineBuilderClosure::HERMITE) {
949 assert(ddc::DiscreteElement<deriv_type>(derivs_xmin->domain().front()).uid() == s_odd);
950 assert(derivs_xmin.has_value() || s_nbe_xmin == 0);
951 } else {
952 assert(!derivs_xmin.has_value() || derivs_xmin->template extent<deriv_type>() == 0);
953 }
954 if constexpr (SBCUpper == SplineBuilderClosure::HERMITE) {
955 assert(ddc::DiscreteElement<deriv_type>(derivs_xmax->domain().front()).uid() == s_odd);
956 assert(derivs_xmax.has_value() || s_nbe_xmax == 0);
957 } else {
958 assert(!derivs_xmax.has_value() || derivs_xmax->template extent<deriv_type>() == 0);
959 }
960
961 // Hermite closure relations at xmin, if any
962 // NOTE: For consistency with the linear system, the i-th derivative
963 // provided by the user must be multiplied by dx^i
964 if constexpr (SBCLower == SplineBuilderClosure::HERMITE) {
965 assert(derivs_xmin->template extent<deriv_type>() == s_nbe_xmin);
966 auto derivs_xmin_values = *derivs_xmin;
967 auto const dx_proxy = m_dx;
968 auto const odd_proxy = s_odd;
969 ddc::parallel_for_each(
970 "ddc_splines_hermite_compute_lower_coefficients",
971 exec_space(),
972 batch_domain(batched_interpolation_domain),
973 KOKKOS_LAMBDA(
974 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type j) {
975 for (int i = 0; i < s_nbe_xmin; ++i) {
976 spline(ddc::DiscreteElement<bsplines_type>(i), j)
977 = derivs_xmin_values(
978 ddc::DiscreteElement<deriv_type>(i + odd_proxy),
979 j)
980 * ddc::detail::ipow(dx_proxy, i + odd_proxy);
981 }
982 });
983 } else if constexpr (SBCLower == SplineBuilderClosure::HOMOGENEOUS_HERMITE) {
984 ddc::DiscreteDomain<bsplines_type> const dx_splines(
985 ddc::DiscreteElement<bsplines_type>(0),
986 ddc::DiscreteVector<bsplines_type>(s_nbe_xmin));
987 batched_spline_domain_type<BatchedInterpolationDDom> const
988 dx_spline_domain(dx_splines, batch_domain(batched_interpolation_domain));
989 ddc::parallel_fill(exec_space(), spline[dx_spline_domain], 0.0);
990 }
991
992 // Fill spline with vals (to work in spline afterward and preserve vals)
993 ddc::parallel_fill(
994 exec_space(),
995 spline[ddc::DiscreteDomain<bsplines_type>(
996 ddc::DiscreteElement<bsplines_type>(s_nbe_xmin),
997 ddc::DiscreteVector<bsplines_type>(m_offset))],
998 0.);
999 // NOTE: We rely on Kokkos::deep_copy because ddc::parallel_deepcopy do not support
1000 // different domain-typed Chunks.
1001 Kokkos::deep_copy(
1002 exec_space(),
1003 spline[ddc::DiscreteDomain<bsplines_type>(
1004 ddc::DiscreteElement<bsplines_type>(s_nbe_xmin + m_offset),
1005 ddc::DiscreteVector<bsplines_type>(static_cast<std::size_t>(
1006 vals.domain()
1007 .template extent<
1008 interpolation_discrete_dimension_type>())))]
1009 .allocation_kokkos_view(),
1010 vals.allocation_kokkos_view());
1011
1012
1013
1014 // Hermite closure relations at xmax, if any
1015 // NOTE: For consistency with the linear system, the i-th derivative
1016 // provided by the user must be multiplied by dx^i
1017 auto const& nbasis_proxy = ddc::discrete_space<bsplines_type>().nbasis();
1018 if constexpr (SBCUpper == SplineBuilderClosure::HERMITE) {
1019 assert(derivs_xmax->template extent<deriv_type>() == s_nbe_xmax);
1020 auto derivs_xmax_values = *derivs_xmax;
1021 auto const dx_proxy = m_dx;
1022 auto const odd_proxy = s_odd;
1023 ddc::parallel_for_each(
1024 "ddc_splines_hermite_compute_upper_coefficients",
1025 exec_space(),
1026 batch_domain(batched_interpolation_domain),
1027 KOKKOS_LAMBDA(
1028 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type j) {
1029 for (int i = 0; i < s_nbe_xmax; ++i) {
1030 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy - s_nbe_xmax + i),
1031 j)
1032 = derivs_xmax_values(
1033 ddc::DiscreteElement<deriv_type>(i + odd_proxy),
1034 j)
1035 * ddc::detail::ipow(dx_proxy, i + odd_proxy);
1036 }
1037 });
1038 } else if constexpr (SBCUpper == SplineBuilderClosure::HOMOGENEOUS_HERMITE) {
1039 ddc::DiscreteDomain<bsplines_type> const dx_splines(
1040 ddc::DiscreteElement<bsplines_type>(nbasis_proxy - s_nbe_xmax),
1041 ddc::DiscreteVector<bsplines_type>(s_nbe_xmax));
1042 batched_spline_domain_type<BatchedInterpolationDDom> const
1043 dx_spline_domain(dx_splines, batch_domain(batched_interpolation_domain));
1044 ddc::parallel_fill(exec_space(), spline[dx_spline_domain], 0.0);
1045 }
1046
1047 // 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
1048 auto const& offset_proxy = m_offset;
1049 ddc::Chunk spline_tr_alloc(
1050 m_label + " > spline_tr (ddc::SplineBuilder::operator())",
1051 batched_spline_tr_domain(batched_interpolation_domain),
1052 ddc::KokkosAllocator<double, memory_space>());
1053 ddc::ChunkSpan const spline_tr = spline_tr_alloc.span_view();
1054 ddc::parallel_for_each(
1055 m_label + " > ddc_splines_transpose_rhs",
1056 exec_space(),
1057 batch_domain(batched_interpolation_domain),
1058 KOKKOS_LAMBDA(
1059 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const j) {
1060 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
1061 spline_tr(ddc::DiscreteElement<bsplines_type>(i), j)
1062 = spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j);
1063 }
1064 });
1065 // Create a 2D Kokkos::View to manage spline_tr as a matrix
1066 Kokkos::View<double**, Kokkos::LayoutRight, exec_space> const bcoef_section(
1067 spline_tr.data_handle(),
1068 static_cast<std::size_t>(spline_tr.template extent<bsplines_type>()),
1069 batch_domain(batched_interpolation_domain).size());
1070 // Compute spline coef
1071 m_matrix->solve(bcoef_section, false);
1072 // Transpose back spline_tr into spline.
1073 ddc::parallel_for_each(
1074 m_label + " > ddc_splines_transpose_back_rhs",
1075 exec_space(),
1076 batch_domain(batched_interpolation_domain),
1077 KOKKOS_LAMBDA(
1078 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const j) {
1079 for (std::size_t i = 0; i < nbasis_proxy; ++i) {
1080 spline(ddc::DiscreteElement<bsplines_type>(i + offset_proxy), j)
1081 = spline_tr(ddc::DiscreteElement<bsplines_type>(i), j);
1082 }
1083 });
1084
1085 // Duplicate the lower spline coefficients to the upper side in case of periodic boundaries
1086 if (bsplines_type::is_periodic()) {
1087 ddc::parallel_for_each(
1088 m_label + " > ddc_splines_periodic_rows_duplicate_rhs",
1089 exec_space(),
1090 batch_domain(batched_interpolation_domain),
1091 KOKKOS_LAMBDA(
1092 batch_domain_type<BatchedInterpolationDDom>::discrete_element_type const
1093 j) {
1094 if (offset_proxy != 0) {
1095 for (int i = 0; i < offset_proxy; ++i) {
1096 spline(ddc::DiscreteElement<bsplines_type>(i), j) = spline(
1097 ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i),
1098 j);
1099 }
1100 for (std::size_t i = offset_proxy; i < bsplines_type::degree(); ++i) {
1101 spline(ddc::DiscreteElement<bsplines_type>(nbasis_proxy + i), j)
1102 = spline(ddc::DiscreteElement<bsplines_type>(i), j);
1103 }
1104 }
1105 for (std::size_t i(0); i < bsplines_type::degree(); ++i) {
1106 ddc::DiscreteElement<bsplines_type> const i_start(i);
1107 ddc::DiscreteElement<bsplines_type> const i_end(nbasis_proxy + i);
1108
1109 spline(i_end, j) = spline(i_start, j);
1110 }
1111 });
1112 }
1113}
1114
1115template <
1116 class ExecSpace,
1117 class MemorySpace,
1118 class BSplines,
1119 class InterpolationDDim,
1123template <class OutMemorySpace>
1124std::tuple<
1125 ddc::Chunk<
1126 double,
1128 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
1129 ddc::KokkosAllocator<double, OutMemorySpace>>,
1130 ddc::Chunk<
1131 double,
1132 ddc::DiscreteDomain<InterpolationDDim>,
1133 ddc::KokkosAllocator<double, OutMemorySpace>>,
1134 ddc::Chunk<
1135 double,
1137 ddc::Deriv<typename InterpolationDDim::continuous_dimension_type>>,
1138 ddc::KokkosAllocator<double, OutMemorySpace>>>
1139SplineBuilder<ExecSpace, MemorySpace, BSplines, InterpolationDDim, SBCLower, SBCUpper, Solver>::
1141{
1142 // Compute integrals of bsplines
1143 ddc::Chunk integral_bsplines(spline_domain(), ddc::KokkosAllocator<double, MemorySpace>());
1144 ddc::integrals(ExecSpace(), integral_bsplines.span_view());
1145
1146 // Remove additional B-splines in the periodic case (cf. UniformBSplines::full_domain() documentation)
1147 ddc::ChunkSpan const integral_bsplines_without_periodic_additional_bsplines
1148 = integral_bsplines[spline_domain().take_first(
1149 ddc::DiscreteVector<bsplines_type>(m_matrix->size()))];
1150
1151 // Allocate mirror with additional rows (cf. SplinesLinearProblem3x3Blocks documentation)
1152 Kokkos::View<double**, Kokkos::LayoutRight, MemorySpace> const
1153 integral_bsplines_mirror_with_additional_allocation(
1154 m_label + " > integral_bsplines_mirror_with_additional_allocation",
1155 m_matrix->required_number_of_rhs_rows(),
1156 1);
1157
1158 // Extract relevant subview
1159 Kokkos::View<double*, Kokkos::LayoutRight, MemorySpace> const integral_bsplines_mirror
1160 = Kokkos::
1161 subview(integral_bsplines_mirror_with_additional_allocation,
1162 std::
1163 pair {static_cast<std::size_t>(0),
1164 integral_bsplines_without_periodic_additional_bsplines
1165 .size()},
1166 0);
1167
1168 // Solve matrix equation A^t*X=integral_bsplines
1169 Kokkos::deep_copy(
1170 integral_bsplines_mirror,
1171 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view());
1172 m_matrix->solve(integral_bsplines_mirror_with_additional_allocation, true);
1173 Kokkos::deep_copy(
1174 integral_bsplines_without_periodic_additional_bsplines.allocation_kokkos_view(),
1175 integral_bsplines_mirror);
1176
1177 // Slice into three ChunkSpan corresponding to lower derivatives, function values and upper derivatives
1178 ddc::ChunkSpan const coefficients_derivs_xmin
1179 = integral_bsplines_without_periodic_additional_bsplines[spline_domain().take_first(
1180 ddc::DiscreteVector<bsplines_type>(s_nbv_xmin))];
1181 ddc::ChunkSpan const coefficients = integral_bsplines_without_periodic_additional_bsplines
1183 .remove_first(ddc::DiscreteVector<bsplines_type>(s_nbv_xmin))
1184 .take_first(
1185 ddc::DiscreteVector<bsplines_type>(
1186 ddc::discrete_space<bsplines_type>().nbasis() - s_nbv_xmin
1187 - s_nbv_xmax))];
1188 ddc::ChunkSpan const coefficients_derivs_xmax
1189 = integral_bsplines_without_periodic_additional_bsplines
1191 .remove_first(
1192 ddc::DiscreteVector<bsplines_type>(
1193 s_nbv_xmin + coefficients.size()))
1194 .take_first(ddc::DiscreteVector<bsplines_type>(s_nbv_xmax))];
1195
1196 // Multiply derivatives coefficients by dx^n
1197 auto const dx_proxy = m_dx;
1198 auto const odd_proxy = s_odd;
1199 ddc::parallel_for_each(
1200 exec_space(),
1201 coefficients_derivs_xmin.domain(),
1202 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1203 coefficients_derivs_xmin(i) *= ddc::detail::
1204 ipow(dx_proxy,
1205 static_cast<std::size_t>(get<bsplines_type>(
1206 (i - coefficients_derivs_xmin.domain().front()) + odd_proxy)));
1207 });
1208 ddc::parallel_for_each(
1209 exec_space(),
1210 coefficients_derivs_xmax.domain(),
1211 KOKKOS_LAMBDA(ddc::DiscreteElement<bsplines_type> i) {
1212 coefficients_derivs_xmax(i) *= ddc::detail::
1213 ipow(dx_proxy,
1214 static_cast<std::size_t>(get<bsplines_type>(
1215 (i - coefficients_derivs_xmax.domain().front()) + odd_proxy)));
1216 });
1217
1218 ddc::DiscreteElement<deriv_type> const first_deriv(s_odd);
1219 // Allocate Chunk on deriv_type and interpolation_discrete_dimension_type and copy quadrature coefficients into it
1220 ddc::Chunk coefficients_derivs_xmin_out(
1222 deriv_type>(first_deriv, ddc::DiscreteVector<deriv_type>(s_nbv_xmin)),
1223 ddc::KokkosAllocator<double, OutMemorySpace>());
1224 ddc::Chunk coefficients_out(
1225 interpolation_domain().take_first(
1226 ddc::DiscreteVector<interpolation_discrete_dimension_type>(
1227 coefficients.size())),
1228 ddc::KokkosAllocator<double, OutMemorySpace>());
1229 ddc::Chunk coefficients_derivs_xmax_out(
1231 deriv_type>(first_deriv, ddc::DiscreteVector<deriv_type>(s_nbv_xmax)),
1232 ddc::KokkosAllocator<double, OutMemorySpace>());
1233 Kokkos::deep_copy(
1234 coefficients_derivs_xmin_out.allocation_kokkos_view(),
1235 coefficients_derivs_xmin.allocation_kokkos_view());
1236 Kokkos::deep_copy(
1237 coefficients_out.allocation_kokkos_view(),
1238 coefficients.allocation_kokkos_view());
1239 Kokkos::deep_copy(
1240 coefficients_derivs_xmax_out.allocation_kokkos_view(),
1241 coefficients_derivs_xmax.allocation_kokkos_view());
1242 return std::make_tuple(
1243 std::move(coefficients_derivs_xmin_out),
1244 std::move(coefficients_out),
1245 std::move(coefficients_derivs_xmax_out));
1246}
1247
1248template <
1249 class ExecSpace,
1250 class MemorySpace,
1251 class BSplines,
1252 class InterpolationDDim,
1256template <class KnotElement>
1257void SplineBuilder<
1258 ExecSpace,
1259 MemorySpace,
1260 BSplines,
1261 InterpolationDDim,
1262 SBCLower,
1263 SBCUpper,
1264 Solver>::
1265 check_n_points_in_cell(int const n_points_in_cell, KnotElement const current_cell_end_idx)
1266{
1267 if (n_points_in_cell > BSplines::degree() + 1) {
1268 KnotElement const rmin_idx = ddc::discrete_space<BSplines>().break_point_domain().front();
1269 int const failed_cell = (current_cell_end_idx - rmin_idx).value();
1270 throw std::runtime_error(
1271 "The spline problem is overconstrained. There are "
1272 + std::to_string(n_points_in_cell) + " points in the " + std::to_string(failed_cell)
1273 + "-th cell.");
1274 }
1275}
1276
1277template <
1278 class ExecSpace,
1279 class MemorySpace,
1280 class BSplines,
1281 class InterpolationDDim,
1282 ddc::SplineBuilderClosure SBCLower,
1283 ddc::SplineBuilderClosure SBCUpper,
1284 SplineSolver Solver>
1285void SplineBuilder<
1286 ExecSpace,
1287 MemorySpace,
1288 BSplines,
1289 InterpolationDDim,
1290 SBCLower,
1291 SBCUpper,
1292 Solver>::check_valid_grid()
1293{
1294 std::size_t const n_interp_points = interpolation_domain().size();
1295 std::size_t const expected_npoints
1296 = ddc::discrete_space<BSplines>().nbasis() - s_nbe_xmin - s_nbe_xmax;
1297 if (n_interp_points != expected_npoints) {
1298 throw std::runtime_error(
1299 "Incorrect number of points supplied to NonUniformInterpolationPoints. "
1300 "(Received : "
1301 + std::to_string(n_interp_points)
1302 + ", expected : " + std::to_string(expected_npoints));
1303 }
1304 int n_points_in_cell = 0;
1305 auto current_cell_end_idx = ddc::discrete_space<BSplines>().break_point_domain().front() + 1;
1306 ddc::host_for_each(interpolation_domain(), [&](auto idx) {
1307 ddc::Coordinate<continuous_dimension_type> const point = ddc::coordinate(idx);
1308 if (point > ddc::coordinate(current_cell_end_idx)) {
1309 // Check the points found in the previous cell
1310 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1311 // Initialise the number of points in the subsequent cell, including the new point
1312 n_points_in_cell = 1;
1313 // Move to the next cell
1314 current_cell_end_idx += 1;
1315 } else if (point == ddc::coordinate(current_cell_end_idx)) {
1316 // Check the points found in the previous cell including the point on the boundary
1317 check_n_points_in_cell(n_points_in_cell + 1, current_cell_end_idx);
1318 // Initialise the number of points in the subsequent cell, including the point on the boundary
1319 n_points_in_cell = 1;
1320 // Move to the next cell
1321 current_cell_end_idx += 1;
1322 } else {
1323 // Indicate that the point is in the cell
1324 n_points_in_cell += 1;
1325 }
1326 });
1327 // Check the number of points in the final cell
1328 check_n_points_in_cell(n_points_in_cell, current_cell_end_idx);
1329}
1330
1331} // 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.
#define DDC_BUILD_DEPRECATED_CODE
Definition config.hpp:7
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