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