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