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