DDC 0.1.0
Loading...
Searching...
No Matches
bsplines_non_uniform.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 <memory>
10#include <vector>
11
12#include <ddc/ddc.hpp>
13
14#include "view.hpp"
15
16namespace ddc {
17
18namespace detail {
19
20struct NonUniformBSplinesBase
21{
22};
23
24} // namespace detail
25
26template <class T>
28{
29};
30
31/**
32 * The type of a non-uniform 1D spline basis (B-spline).
33 *
34 * Knots for non-uniform B-splines are non-uniformly distributed (no assumption is made on the uniformity of their distribution,
35 * the associated discrete dimension is a NonUniformPointSampling).
36 *
37 * @tparam CDim The tag identifying the continuous dimension on which the support of the B-spline functions are defined.
38 * @tparam D The degree of the B-splines.
39 */
40template <class CDim, std::size_t D>
41class NonUniformBSplines : detail::NonUniformBSplinesBase
42{
43 static_assert(D > 0, "Parameter `D` must be positive");
44
45public:
46 /// @brief The tag identifying the continuous dimension on which the support of the B-splines are defined.
47 using continuous_dimension_type = CDim;
48
49 /// @brief The discrete dimension identifying B-splines.
50 using discrete_dimension_type = NonUniformBSplines;
51
52 /** @brief The degree of B-splines.
53 *
54 * @return The degree.
55 */
56 static constexpr std::size_t degree() noexcept
57 {
58 return D;
59 }
60
61 /** @brief Indicates if the B-splines are periodic or not.
62 *
63 * @return A boolean indicating if the B-splines are periodic or not.
64 */
65 static constexpr bool is_periodic() noexcept
66 {
67 return CDim::PERIODIC;
68 }
69
70 /** @brief Indicates if the B-splines are uniform or not (this is not the case here).
71 *
72 * @return A boolean indicating if the B-splines are uniform or not.
73 */
74 static constexpr bool is_uniform() noexcept
75 {
76 return false;
77 }
78
79 /** @brief Storage class of the static attributes of the discrete dimension.
80 *
81 * @tparam DDim The name of the discrete dimension.
82 * @tparam MemorySpace The Kokkos memory space where the attributes are being stored.
83 */
84 template <class DDim, class MemorySpace>
85 class Impl
86 {
87 template <class ODDim, class OMemorySpace>
88 friend class Impl;
89
90 public:
91 /// @brief The type of the knots defining the B-splines.
92 using knot_discrete_dimension_type = NonUniformBsplinesKnots<DDim>;
93
94 /// @brief The type of the discrete dimension representing the B-splines.
95 using discrete_dimension_type = NonUniformBSplines;
96
97 /// @brief The type of a DiscreteDomain whose elements identify the B-splines.
98 using discrete_domain_type = DiscreteDomain<DDim>;
99
100 /// @brief The type of a DiscreteElement identifying a B-spline.
101 using discrete_element_type = DiscreteElement<DDim>;
102
103 /// @brief The type of a DiscreteVector representing an "index displacement" between two B-splines.
104 using discrete_vector_type = DiscreteVector<DDim>;
105
106 private:
107 ddc::DiscreteDomain<knot_discrete_dimension_type> m_knot_domain;
108 ddc::DiscreteDomain<knot_discrete_dimension_type> m_break_point_domain;
109
110 public:
111 Impl() = default;
112
113 /** @brief Constructs an Impl using a brace-list, i.e. `Impl bsplines({0., 1.})`
114 *
115 * Constructs an Impl by iterating over a list of break points. Internally this constructor calls the constructor
116 * Impl(RandomIt breaks_begin, RandomIt breaks_end).
117 *
118 * @param breaks The std::initializer_list of the coordinates of break points.
119 */
120 Impl(std::initializer_list<ddc::Coordinate<CDim>> breaks)
121 : Impl(breaks.begin(), breaks.end())
122 {
123 }
124
125 /** @brief Constructs an Impl using a std::vector.
126 *
127 * Constructs an Impl by iterating over a list of break points. Internally this constructor calls the constructor
128 * Impl(RandomIt breaks_begin, RandomIt breaks_end).
129 *
130 * @param breaks The std::vector of the coordinates of break points.
131 */
132 explicit Impl(std::vector<ddc::Coordinate<CDim>> const& breaks)
133 : Impl(breaks.begin(), breaks.end())
134 {
135 }
136
137 /** @brief Constructs an Impl by iterating over a range of break points from begin to end.
138 *
139 * The provided break points describe the separation between the cells on which the polynomials
140 * comprising a spline are defined. They are used to build a set of knots. There are 2*degree more
141 * knots than break points. In the non-periodic case the knots are defined as follows:
142 * \f$ k_i = b_0 \forall 0 \leq i < d \f$
143 * \f$ k_{i+d} = b_i \forall 0 \leq i < n_b \f$
144 * \f$ k_{i+d+n_b} = b_{n_b-1} \forall 0 \leq i < d \f$
145 * where \f$d\f$ is the degree of the polynomials, and \f$n_b\f$ is the number of break points in the input pair of iterators. And in the periodic case:
146 * \f$ k_i = b_{n_b-1-d+i} \forall 0 \leq i < d \f$
147 * \f$ k_{i+d} = b_i \forall 0 \leq i \leq n_b \f$
148 * \f$ k_{i+d+n_b} = b_{i+1} \forall 0 \leq i < d \f$
149 *
150 * This constructor makes the knots accessible via a DiscreteSpace.
151 *
152 * @param breaks_begin The iterator which points at the beginning of the break points.
153 * @param breaks_end The iterator which points at the end of the break points.
154 */
155 template <class RandomIt>
156 Impl(RandomIt breaks_begin, RandomIt breaks_end);
157
158 /** @brief Copy-constructs from another Impl with a different Kokkos memory space.
159 *
160 * @param impl A reference to the other Impl.
161 */
162 template <class OriginMemorySpace>
163 explicit Impl(Impl<DDim, OriginMemorySpace> const& impl)
164 : m_knot_domain(impl.m_knot_domain)
165 , m_break_point_domain(impl.m_break_point_domain)
166 {
167 }
168
169 /** @brief Copy-constructs.
170 *
171 * @param x A reference to another Impl.
172 */
173 Impl(Impl const& x) = default;
174
175 /** @brief Move-constructs.
176 *
177 * @param x An rvalue to another Impl.
178 */
179 Impl(Impl&& x) = default;
180
181 /// @brief Destructs.
182 ~Impl() = default;
183
184 /** @brief Copy-assigns.
185 *
186 * @param x A reference to another Impl.
187 * @return A reference to the copied Impl.
188 */
189 Impl& operator=(Impl const& x) = default;
190
191 /** @brief Move-assigns.
192 *
193 * @param x An rvalue to another Impl.
194 * @return A reference to this object.
195 */
196 Impl& operator=(Impl&& x) = default;
197
198 /** @brief Evaluates non-zero B-splines at a given coordinate.
199 *
200 * The values are computed for every B-spline with support at the given coordinate x. There are only (degree+1)
201 * B-splines which are non-zero at any given point. It is these B-splines which are evaluated.
202 * This can be useful to calculate a spline approximation of a function. A spline approximation at coordinate x
203 * is a linear combination of these B-spline evaluations weighted with the spline coefficients of the spline-transformed
204 * initial discrete function.
205 *
206 * @param[out] values The values of the B-splines evaluated at coordinate x. It has to be a 1D mdspan with (degree+1) elements.
207 * @param[in] x The coordinate where B-splines are evaluated. It has to be in the range of break points coordinates.
208 * @return The index of the first B-spline which is evaluated.
209 */
210 KOKKOS_INLINE_FUNCTION discrete_element_type
211 eval_basis(DSpan1D values, ddc::Coordinate<CDim> const& x) const;
212
213 /** @brief Evaluates non-zero B-spline derivatives at a given coordinate
214 *
215 * The derivatives are computed for every B-spline with support at the given coordinate x. There are only (degree+1)
216 * B-splines which are non-zero at any given point. It is these B-splines which are derivated.
217 * A spline approximation of a derivative at coordinate x is a linear
218 * combination of those B-spline derivatives weighted with the spline coefficients of the spline-transformed
219 * initial discrete function.
220 *
221 * @param[out] derivs The derivatives of the B-splines evaluated at coordinate x. It has to be a 1D mdspan with (degree+1) elements.
222 * @param[in] x The coordinate where B-spline derivatives are evaluated. It has to be in the range of break points coordinates.
223 * @return The index of the first B-spline which is derivated.
224 */
225 KOKKOS_INLINE_FUNCTION discrete_element_type
226 eval_deriv(DSpan1D derivs, ddc::Coordinate<CDim> const& x) const;
227
228 /** @brief Evaluates non-zero B-spline values and \f$n\f$ derivatives at a given coordinate
229 *
230 * The values and derivatives are computed for every B-spline with support at the given coordinate x. There are only (degree+1)
231 * B-splines which are non-zero at any given point. It is these B-splines which are evaluated and derivated.
232 * A spline approximation of a derivative at coordinate x is a linear
233 * combination of those B-spline derivatives weighted with spline coefficients of the spline-transformed
234 * initial discrete function.
235 *
236 * @param[out] derivs The values and \f$n\f$ derivatives of the B-splines evaluated at coordinate x. It has to be a 2D mdspan of sizes (degree+1, n+1).
237 * @param[in] x The coordinate where B-spline derivatives are evaluated. It has to be in the range of break points coordinates.
238 * @param[in] n The number of derivatives to evaluate (in addition to the B-spline values themselves).
239 * @return The index of the first B-spline which is evaluated/derivated.
240 */
242 ddc::DSpan2D derivs,
243 ddc::Coordinate<CDim> const& x,
244 std::size_t n) const;
245
246 /** @brief Compute the integrals of the B-splines.
247 *
248 * The integral of each of the B-splines over their support within the domain on which this basis was defined.
249 *
250 * @deprecated Use @ref integrals instead.
251 *
252 * @param[out] int_vals The values of the integrals. It has to be a 1D Chunkspan of size (nbasis).
253 * @return The values of the integrals.
254 */
255 template <class Layout, class MemorySpace2>
256 [[deprecated("Use `integrals` instead")]] KOKKOS_INLINE_FUNCTION ddc::
257 ChunkSpan<double, ddc::DiscreteDomain<DDim>, Layout, MemorySpace2>
258 integrals(ddc::ChunkSpan<double, discrete_domain_type, Layout, MemorySpace2>
259 int_vals) const;
260
261 /** @brief Returns the coordinate of the first support knot associated to a DiscreteElement identifying a B-spline.
262 *
263 * Each B-spline has a support defined over (degree+2) knots. For a B-spline identified by the
264 * provided DiscreteElement, this function returns the first knot in the support of the B-spline.
265 * In other words it returns the lower bound of the support.
266 *
267 * @param[in] ix DiscreteElement identifying the B-spline.
268 * @return DiscreteElement of the lower bound of the support of the B-spline.
269 */
270 KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<knot_discrete_dimension_type>
271 get_first_support_knot(discrete_element_type const& ix) const
272 {
273 return ddc::DiscreteElement<knot_discrete_dimension_type>(
274 (ix - discrete_element_type(0)).value());
275 }
276
277 /** @brief Returns the coordinate of the last support knot associated to a DiscreteElement identifying a B-spline.
278 *
279 * Each B-spline has a support defined over (degree+2) knots. For a B-spline identified by the
280 * provided DiscreteElement, this function returns the last knot in the support of the B-spline.
281 * In other words it returns the upper bound of the support.
282 *
283 * @param[in] ix DiscreteElement identifying the B-spline.
284 * @return DiscreteElement of the upper bound of the support of the B-spline.
285 */
286 KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<knot_discrete_dimension_type>
287 get_last_support_knot(discrete_element_type const& ix) const
288 {
290 + ddc::DiscreteVector<knot_discrete_dimension_type>(degree() + 1);
291 }
292
293 /** @brief Returns the coordinate of the first break point of the domain on which the B-splines are defined.
294 *
295 * @return Coordinate of the lower bound of the domain.
296 */
297 KOKKOS_INLINE_FUNCTION ddc::Coordinate<CDim> rmin() const noexcept
298 {
299 return ddc::coordinate(m_break_point_domain.front());
300 }
301
302 /** @brief Returns the coordinate of the last break point of the domain on which the B-splines are defined.
303 *
304 * @return Coordinate of the upper bound of the domain.
305 */
306 KOKKOS_INLINE_FUNCTION ddc::Coordinate<CDim> rmax() const noexcept
307 {
308 return ddc::coordinate(m_break_point_domain.back());
309 }
310
311 /** @brief Returns the length of the domain.
312 *
313 * @return The length of the domain.
314 */
315 KOKKOS_INLINE_FUNCTION double length() const noexcept
316 {
317 return rmax() - rmin();
318 }
319
320 /** @brief Returns the number of elements necessary to construct a spline representation of a function.
321 *
322 * For a non-periodic domain the number of elements necessary to construct a spline representation of a function
323 * is equal to the number of basis functions. However in the periodic case it additionally includes degree additional elements
324 * which allow the first B-splines to be evaluated close to rmax (where they also appear due to the periodicity).
325 *
326 * @return The number of elements necessary to construct a spline representation of a function.
327 */
328 KOKKOS_INLINE_FUNCTION std::size_t size() const noexcept
329 {
330 return degree() + ncells();
331 }
332
333 /** @brief Returns the discrete domain including eventual additional B-splines in the periodic case. See size().
334 *
335 * @return The discrete domain including eventual additional B-splines.
336 */
337 KOKKOS_INLINE_FUNCTION discrete_domain_type full_domain() const
338 {
339 return discrete_domain_type(discrete_element_type(0), discrete_vector_type(size()));
340 }
341
342 /** @brief Returns the discrete domain which describes the break points.
343 *
344 * @return The discrete domain describing the break points.
345 */
346 KOKKOS_INLINE_FUNCTION ddc::DiscreteDomain<knot_discrete_dimension_type>
347 break_point_domain() const
348 {
349 return m_break_point_domain;
350 }
351
352 /** @brief The number of break points
353 *
354 * The number of break points or cell boundaries.
355 *
356 * @return The number of break points
357 */
358 KOKKOS_INLINE_FUNCTION std::size_t npoints() const noexcept
359 {
360 return m_knot_domain.size() - 2 * degree();
361 }
362
363 /** @brief Returns the number of basis functions.
364 *
365 * The number of functions in the spline basis.
366 *
367 * @return The number of basis functions.
368 */
369 KOKKOS_INLINE_FUNCTION std::size_t nbasis() const noexcept
370 {
371 return ncells() + !is_periodic() * degree();
372 }
373
374 /** @brief Returns the number of cells over which the B-splines are defined.
375 *
376 * The number of cells over which the B-splines and any spline representation are defined.
377 * In other words the number of polynomials that comprise a spline representation on the domain where the basis is defined.
378 *
379 * @return The number of cells over which the B-splines are defined.
380 */
381 KOKKOS_INLINE_FUNCTION std::size_t ncells() const noexcept
382 {
383 return npoints() - 1;
384 }
385
386 private:
387 KOKKOS_INLINE_FUNCTION discrete_element_type get_first_bspline_in_cell(
388 ddc::DiscreteElement<knot_discrete_dimension_type> const& ic) const
389 {
390 return discrete_element_type((ic - m_break_point_domain.front()).value());
391 }
392
393 /**
394 * @brief Get the DiscreteElement describing the knot at the start of the cell where x is found.
395 * @param x The point whose location must be determined.
396 * @returns The DiscreteElement describing the knot at the lower bound of the cell of interest.
397 */
398 KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<knot_discrete_dimension_type> find_cell_start(
399 ddc::Coordinate<CDim> const& x) const;
400 };
401};
402
403template <class DDim>
404struct is_non_uniform_bsplines : public std::is_base_of<detail::NonUniformBSplinesBase, DDim>
405{
406};
407
408/**
409 * @brief Indicates if a tag corresponds to non-uniform B-splines or not.
410 *
411 * @tparam The presumed non-uniform B-splines.
412 */
413template <class DDim>
415
416template <class CDim, std::size_t D>
417template <class DDim, class MemorySpace>
418template <class RandomIt>
419NonUniformBSplines<CDim, D>::Impl<DDim, MemorySpace>::Impl(
420 RandomIt const breaks_begin,
421 RandomIt const breaks_end)
422 : m_knot_domain(
423 ddc::DiscreteElement<knot_discrete_dimension_type>(0),
424 ddc::DiscreteVector<knot_discrete_dimension_type>(
425 (breaks_end - breaks_begin)
426 + 2 * degree())) // Create a mesh of knots including the eventual periodic point
427 , m_break_point_domain(
428 ddc::DiscreteElement<knot_discrete_dimension_type>(degree()),
429 ddc::DiscreteVector<knot_discrete_dimension_type>(
430 (breaks_end - breaks_begin))) // Create a mesh of break points
431{
432 std::vector<ddc::Coordinate<CDim>> knots((breaks_end - breaks_begin) + 2 * degree());
433 // Fill the provided knots
434 int ii = 0;
435 for (RandomIt it = breaks_begin; it < breaks_end; ++it) {
436 knots[degree() + ii] = *it;
437 ++ii;
438 }
439 ddc::Coordinate<CDim> const rmin = knots[degree()];
440 ddc::Coordinate<CDim> const rmax = knots[(breaks_end - breaks_begin) + degree() - 1];
441 assert(rmin < rmax);
442
443 // Fill out the extra knots
444 if constexpr (is_periodic()) {
445 double const period = rmax - rmin;
446 for (std::size_t i = 1; i < degree() + 1; ++i) {
447 knots[degree() + -i] = knots[degree() + ncells() - i] - period;
448 knots[degree() + ncells() + i] = knots[degree() + i] + period;
449 }
450 } else // open
451 {
452 for (std::size_t i = 1; i < degree() + 1; ++i) {
453 knots[degree() + -i] = rmin;
454 knots[degree() + npoints() - 1 + i] = rmax;
455 }
456 }
457 ddc::init_discrete_space<knot_discrete_dimension_type>(knots);
458}
459
460template <class CDim, std::size_t D>
461template <class DDim, class MemorySpace>
462KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<DDim> NonUniformBSplines<CDim, D>::
463 Impl<DDim, MemorySpace>::eval_basis(DSpan1D values, ddc::Coordinate<CDim> const& x) const
464{
465 assert(values.size() == D + 1);
466
467 std::array<double, degree()> left;
468 std::array<double, degree()> right;
469
470 assert(x - rmin() >= -length() * 1e-14);
471 assert(rmax() - x >= -length() * 1e-14);
472 assert(values.size() == degree() + 1);
473
474 // 1. Compute cell index 'icell'
475 ddc::DiscreteElement<knot_discrete_dimension_type> const icell = find_cell_start(x);
476
477 assert(icell >= m_break_point_domain.front());
478 assert(icell <= m_break_point_domain.back());
479 assert(ddc::coordinate(icell) - x <= length() * 1e-14);
480 assert(x - ddc::coordinate(icell + 1) <= length() * 1e-14);
481
482 // 2. Compute values of B-splines with support over cell 'icell'
483 double temp;
484 values[0] = 1.0;
485 for (std::size_t j = 0; j < degree(); ++j) {
486 left[j] = x - ddc::coordinate(icell - j);
487 right[j] = ddc::coordinate(icell + j + 1) - x;
488 double saved = 0.0;
489 for (std::size_t r = 0; r < j + 1; ++r) {
490 temp = values[r] / (right[r] + left[j - r]);
491 values[r] = saved + right[r] * temp;
492 saved = left[j - r] * temp;
493 }
494 values[j + 1] = saved;
495 }
496
497 return get_first_bspline_in_cell(icell);
498}
499
500template <class CDim, std::size_t D>
501template <class DDim, class MemorySpace>
502KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<DDim> NonUniformBSplines<CDim, D>::
503 Impl<DDim, MemorySpace>::eval_deriv(DSpan1D derivs, ddc::Coordinate<CDim> const& x) const
504{
505 std::array<double, degree()> left;
506 std::array<double, degree()> right;
507
508 assert(x - rmin() >= -length() * 1e-14);
509 assert(rmax() - x >= -length() * 1e-14);
510 assert(derivs.size() == degree() + 1);
511
512 // 1. Compute cell index 'icell'
513 ddc::DiscreteElement<knot_discrete_dimension_type> const icell = find_cell_start(x);
514
515 assert(icell >= m_break_point_domain.front());
516 assert(icell <= m_break_point_domain.back());
517 assert(ddc::coordinate(icell) <= x);
518 assert(ddc::coordinate(icell + 1) >= x);
519
520 // 2. Compute values of derivatives of B-splines with support over cell 'icell'
521
522 /*
523 * Compute nonzero basis functions and knot differences
524 * for splines up to degree degree-1 which are needed to compute derivative
525 * First part of Algorithm A3.2 of NURBS book
526 */
527 double saved;
528 double temp;
529 derivs[0] = 1.0;
530 for (std::size_t j = 0; j < degree() - 1; ++j) {
531 left[j] = x - ddc::coordinate(icell - j);
532 right[j] = ddc::coordinate(icell + j + 1) - x;
533 saved = 0.0;
534 for (std::size_t r = 0; r < j + 1; ++r) {
535 temp = derivs[r] / (right[r] + left[j - r]);
536 derivs[r] = saved + right[r] * temp;
537 saved = left[j - r] * temp;
538 }
539 derivs[j + 1] = saved;
540 }
541
542 /*
543 * Compute derivatives at x using values stored in bsdx and formula
544 * for spline derivative based on difference of splines of degree degree-1
545 */
546 saved = degree() * derivs[0]
547 / (ddc::coordinate(icell + 1) - ddc::coordinate(icell + 1 - degree()));
548 derivs[0] = -saved;
549 for (std::size_t j = 1; j < degree(); ++j) {
550 temp = saved;
551 saved = degree() * derivs[j]
552 / (ddc::coordinate(icell + j + 1) - ddc::coordinate(icell + j + 1 - degree()));
553 derivs[j] = temp - saved;
554 }
555 derivs[degree()] = saved;
556
557 return get_first_bspline_in_cell(icell);
558}
559
560template <class CDim, std::size_t D>
561template <class DDim, class MemorySpace>
562KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<DDim> NonUniformBSplines<CDim, D>::
563 Impl<DDim, MemorySpace>::eval_basis_and_n_derivs(
564 ddc::DSpan2D const derivs,
565 ddc::Coordinate<CDim> const& x,
566 std::size_t const n) const
567{
568 std::array<double, degree()> left;
569 std::array<double, degree()> right;
570
571 std::array<double, 2 * (degree() + 1)> a_ptr;
572 Kokkos::mdspan<double, Kokkos::extents<std::size_t, degree() + 1, 2>> const a(a_ptr.data());
573
574 std::array<double, (degree() + 1) * (degree() + 1)> ndu_ptr;
575 Kokkos::mdspan<double, Kokkos::extents<std::size_t, degree() + 1, degree() + 1>> const ndu(
576 ndu_ptr.data());
577
578 assert(x - rmin() >= -length() * 1e-14);
579 assert(rmax() - x >= -length() * 1e-14);
580 // assert(n >= 0); as long as n is unsigned
581 assert(n <= degree());
582 assert(derivs.extent(0) == 1 + degree());
583 assert(derivs.extent(1) == 1 + n);
584
585 // 1. Compute cell index 'icell' and x_offset
586 ddc::DiscreteElement<knot_discrete_dimension_type> const icell = find_cell_start(x);
587
588 assert(icell >= m_break_point_domain.front());
589 assert(icell <= m_break_point_domain.back());
590 assert(ddc::coordinate(icell) <= x);
591 assert(ddc::coordinate(icell + 1) >= x);
592
593 // 2. Compute nonzero basis functions and knot differences for splines
594 // up to degree (degree-1) which are needed to compute derivative
595 // Algorithm A2.3 of NURBS book
596 //
597 // 21.08.2017: save inverse of knot differences to avoid unnecessary
598 // divisions
599 // [Yaman Güçlü, Edoardo Zoni]
600
601 double saved;
602 double temp;
603 DDC_MDSPAN_ACCESS_OP(ndu, 0, 0) = 1.0;
604 for (std::size_t j = 0; j < degree(); ++j) {
605 left[j] = x - ddc::coordinate(icell - j);
606 right[j] = ddc::coordinate(icell + j + 1) - x;
607 saved = 0.0;
608 for (std::size_t r = 0; r < j + 1; ++r) {
609 // compute inverse of knot differences and save them into lower
610 // triangular part of ndu
611 DDC_MDSPAN_ACCESS_OP(ndu, r, j + 1) = 1.0 / (right[r] + left[j - r]);
612 // compute basis functions and save them into upper triangular part
613 // of ndu
614 temp = DDC_MDSPAN_ACCESS_OP(ndu, j, r) * DDC_MDSPAN_ACCESS_OP(ndu, r, j + 1);
615 DDC_MDSPAN_ACCESS_OP(ndu, j + 1, r) = saved + right[r] * temp;
616 saved = left[j - r] * temp;
617 }
618 DDC_MDSPAN_ACCESS_OP(ndu, j + 1, j + 1) = saved;
619 }
620 // Save 0-th derivative
621 for (std::size_t j = 0; j < degree() + 1; ++j) {
622 DDC_MDSPAN_ACCESS_OP(derivs, j, 0) = DDC_MDSPAN_ACCESS_OP(ndu, degree(), j);
623 }
624
625 for (int r = 0; r < int(degree() + 1); ++r) {
626 int s1 = 0;
627 int s2 = 1;
628 DDC_MDSPAN_ACCESS_OP(a, 0, 0) = 1.0;
629 for (int k = 1; k < int(n + 1); ++k) {
630 double d = 0.0;
631 int const rk = r - k;
632 int const pk = degree() - k;
633 if (r >= k) {
634 DDC_MDSPAN_ACCESS_OP(a, 0, s2)
635 = DDC_MDSPAN_ACCESS_OP(a, 0, s1) * DDC_MDSPAN_ACCESS_OP(ndu, rk, pk + 1);
636 d = DDC_MDSPAN_ACCESS_OP(a, 0, s2) * DDC_MDSPAN_ACCESS_OP(ndu, pk, rk);
637 }
638 int const j1 = rk > -1 ? 1 : (-rk);
639 int const j2 = (r - 1) <= pk ? k : (degree() - r + 1);
640 for (int j = j1; j < j2; ++j) {
641 DDC_MDSPAN_ACCESS_OP(a, j, s2)
642 = (DDC_MDSPAN_ACCESS_OP(a, j, s1) - DDC_MDSPAN_ACCESS_OP(a, j - 1, s1))
643 * DDC_MDSPAN_ACCESS_OP(ndu, rk + j, pk + 1);
644 d += DDC_MDSPAN_ACCESS_OP(a, j, s2) * DDC_MDSPAN_ACCESS_OP(ndu, pk, rk + j);
645 }
646 if (r <= pk) {
647 DDC_MDSPAN_ACCESS_OP(a, k, s2) = -DDC_MDSPAN_ACCESS_OP(a, k - 1, s1)
648 * DDC_MDSPAN_ACCESS_OP(ndu, r, pk + 1);
649 d += DDC_MDSPAN_ACCESS_OP(a, k, s2) * DDC_MDSPAN_ACCESS_OP(ndu, pk, r);
650 }
651 DDC_MDSPAN_ACCESS_OP(derivs, r, k) = d;
652 Kokkos::kokkos_swap(s1, s2);
653 }
654 }
655
656 int r = degree();
657 for (int k = 1; k < int(n + 1); ++k) {
658 for (std::size_t i = 0; i < derivs.extent(0); ++i) {
659 DDC_MDSPAN_ACCESS_OP(derivs, i, k) *= r;
660 }
661 r *= degree() - k;
662 }
663
664 return get_first_bspline_in_cell(icell);
665}
666
667template <class CDim, std::size_t D>
668template <class DDim, class MemorySpace>
669KOKKOS_INLINE_FUNCTION ddc::DiscreteElement<NonUniformBsplinesKnots<DDim>> NonUniformBSplines<
670 CDim,
671 D>::Impl<DDim, MemorySpace>::find_cell_start(ddc::Coordinate<CDim> const& x) const
672{
673 assert(x - rmin() >= -length() * 1e-14);
674 assert(rmax() - x >= -length() * 1e-14);
675
676 if (x <= rmin()) {
677 return m_break_point_domain.front();
678 }
679 if (x >= rmax()) {
680 return m_break_point_domain.back() - 1;
681 }
682
683 // Binary search
684 ddc::DiscreteElement<knot_discrete_dimension_type> low = m_break_point_domain.front();
685 ddc::DiscreteElement<knot_discrete_dimension_type> high = m_break_point_domain.back();
686 ddc::DiscreteElement<knot_discrete_dimension_type> icell = low + (high - low) / 2;
687 while (x < ddc::coordinate(icell) || x >= ddc::coordinate(icell + 1)) {
688 if (x < ddc::coordinate(icell)) {
689 high = icell;
690 } else {
691 low = icell;
692 }
693 icell = low + (high - low) / 2;
694 }
695 return icell;
696}
697
698template <class CDim, std::size_t D>
699template <class DDim, class MemorySpace>
700template <class Layout, class MemorySpace2>
701KOKKOS_INLINE_FUNCTION ddc::ChunkSpan<double, ddc::DiscreteDomain<DDim>, Layout, MemorySpace2>
702NonUniformBSplines<CDim, D>::Impl<DDim, MemorySpace>::integrals(
703 ddc::ChunkSpan<double, discrete_domain_type, Layout, MemorySpace2> int_vals) const
704{
705 assert([&]() -> bool {
706 if constexpr (is_periodic()) {
707 return int_vals.size() == nbasis() || int_vals.size() == size();
708 } else {
709 return int_vals.size() == nbasis();
710 }
711 }());
712
713 double const inv_deg = 1.0 / (degree() + 1);
714
715 discrete_domain_type const dom_bsplines(
716 full_domain().take_first(discrete_vector_type {nbasis()}));
717 for (auto ix : dom_bsplines) {
718 int_vals(ix) = double(ddc::coordinate(get_last_support_knot(ix))
719 - ddc::coordinate(get_first_support_knot(ix)))
720 * inv_deg;
721 }
722
723 if constexpr (is_periodic()) {
724 if (int_vals.size() == size()) {
725 discrete_domain_type const dom_bsplines_wrap(
726 full_domain().take_last(discrete_vector_type {degree()}));
727 for (auto ix : dom_bsplines_wrap) {
728 int_vals(ix) = 0;
729 }
730 }
731 }
732 return int_vals;
733}
734
735} // namespace ddc
friend class DiscreteDomain
KOKKOS_FUNCTION constexpr bool operator!=(DiscreteVector< OTags... > const &rhs) const noexcept
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 ddc::ChunkSpan< double, ddc::DiscreteDomain< DDim >, Layout, MemorySpace2 > integrals(ddc::ChunkSpan< double, discrete_domain_type, Layout, MemorySpace2 > int_vals) const
Compute the integrals of the B-splines.
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 .
The top-level namespace of DDC.
constexpr bool is_non_uniform_bsplines_v
Indicates if a tag corresponds to non-uniform B-splines or not.