DDC 0.0.0

a discrete domain computation library

bsplines_non_uniform.hpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <array>
8#include <cassert>
9#include <memory>
10#include <vector>
11
12#include <ddc/ddc.hpp>
13
14#include "view.hpp"
15
16namespace ddc {
17
18namespace detail {
19
20template <class T>
21struct NonUniformBsplinesKnots : NonUniformPointSampling<typename T::tag_type>
22{
23};
24
25struct NonUniformBSplinesBase
26{
27};
28
29} // namespace detail
30
40template <class Tag, std::size_t D>
41class NonUniformBSplines : detail::NonUniformBSplinesBase
42{
43 static_assert(D > 0, "Parameter `D` must be positive");
44
45public:
47 using tag_type = Tag;
48
51
56 static constexpr std::size_t degree() noexcept
57 {
58 return D;
59 }
60
65 static constexpr bool is_periodic() noexcept
66 {
67 return Tag::PERIODIC;
68 }
69
74 static constexpr bool is_uniform() noexcept
75 {
76 return false;
77 }
78
84 template <class DDim, class MemorySpace>
85 class Impl
86 {
87 template <class ODDim, class OMemorySpace>
88 friend class Impl;
89
90 private:
91 using mesh_type = detail::NonUniformBsplinesKnots<DDim>;
92
94
95 int m_nknots;
96
97 public:
100
103
106
109
110 Impl() = default;
111
119 explicit Impl(std::initializer_list<ddc::Coordinate<Tag>> breaks)
120 : Impl(breaks.begin(), breaks.end())
121 {
122 }
123
131 explicit Impl(std::vector<ddc::Coordinate<Tag>> const& breaks)
132 : Impl(breaks.begin(), breaks.end())
133 {
134 }
135
154 template <class RandomIt>
156
161 template <class OriginMemorySpace>
163 : m_domain(impl.m_domain)
164 , m_nknots(impl.m_nknots)
165 {
166 }
167
172 Impl(Impl const& x) = default;
173
178 Impl(Impl&& x) = default;
179
181 ~Impl() = default;
182
188 Impl& operator=(Impl const& x) = default;
189
195 Impl& operator=(Impl&& x) = default;
196
211
226
242 ddc::Coordinate<Tag> const& x,
243 std::size_t n) const;
244
252 template <class Layout, class MemorySpace2>
256 int_vals) const;
257
272
287
302
318
327
336
342 {
343 return rmax() - rmin();
344 }
345
355 {
356 return degree() + ncells();
357 }
358
367
375 {
376 return m_nknots - 2 * degree();
377 }
378
386 {
387 return ncells() + !is_periodic() * degree();
388 }
389
398 {
399 return npoints() - 1;
400 }
401
402 private:
403 KOKKOS_INLINE_FUNCTION int find_cell(ddc::Coordinate<Tag> const& x) const;
404 };
405};
406
407template <class DDim>
408struct is_non_uniform_bsplines : public std::is_base_of<detail::NonUniformBSplinesBase, DDim>
409{
410};
411
417template <class DDim>
419
420template <class Tag, std::size_t D>
421template <class DDim, class MemorySpace>
422template <class RandomIt>
424 RandomIt const break_begin,
425 RandomIt const break_end)
426 : m_domain(
427 ddc::DiscreteElement<mesh_type>(0),
428 ddc::DiscreteVector<mesh_type>(
430 + 2 * degree())) // Create a mesh including the eventual periodic point
431 , m_nknots((break_end - break_begin) + 2 * degree())
432{
433 std::vector<ddc::Coordinate<Tag>> knots((break_end - break_begin) + 2 * degree());
434 // Fill the provided knots
435 int ii = 0;
436 for (RandomIt it = break_begin; it < break_end; ++it) {
437 knots[degree() + ii] = *it;
438 ++ii;
439 }
442 assert(rmin < rmax);
443
444 // Fill out the extra knots
445 if constexpr (is_periodic()) {
446 double const period = rmax - rmin;
447 for (std::size_t i = 1; i < degree() + 1; ++i) {
448 knots[degree() + -i] = knots[degree() + ncells() - i] - period;
449 knots[degree() + ncells() + i] = knots[degree() + i] + period;
450 }
451 } else // open
452 {
453 for (std::size_t i = 1; i < degree() + 1; ++i) {
454 knots[degree() + -i] = rmin;
455 knots[degree() + npoints() - 1 + i] = rmax;
456 }
457 }
459}
460
461template <class Tag, std::size_t D>
462template <class DDim, class MemorySpace>
465{
466 assert(values.size() == D + 1);
467
468 std::array<double, degree()> left;
469 std::array<double, degree()> right;
470
471 assert(x >= rmin());
472 assert(x <= rmax());
473 assert(values.size() == degree() + 1);
474
475 // 1. Compute cell index 'icell'
476 int const icell = find_cell(x);
477
478 assert(icell >= 0);
479 assert(icell <= int(ncells() - 1));
480 assert(get_knot(icell) <= x);
481 assert(get_knot(icell + 1) >= x);
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 - get_knot(icell - j);
488 right[j] = get_knot(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
499}
500
501template <class Tag, std::size_t D>
502template <class DDim, class MemorySpace>
505{
506 std::array<double, degree()> left;
507 std::array<double, degree()> right;
508
509 assert(x >= rmin());
510 assert(x <= rmax());
511 assert(derivs.size() == degree() + 1);
512
513 // 1. Compute cell index 'icell'
514 int const icell = find_cell(x);
515
516 assert(icell >= 0);
517 assert(icell <= int(ncells() - 1));
518 assert(get_knot(icell) <= x);
519 assert(get_knot(icell + 1) >= x);
520
521 // 2. Compute values of derivatives of B-splines with support over cell 'icell'
522
523 /*
524 * Compute nonzero basis functions and knot differences
525 * for splines up to degree degree-1 which are needed to compute derivative
526 * First part of Algorithm A3.2 of NURBS book
527 */
528 double saved, temp;
529 derivs[0] = 1.0;
530 for (std::size_t j = 0; j < degree() - 1; ++j) {
531 left[j] = x - get_knot(icell - j);
532 right[j] = get_knot(icell + j + 1) - x;
533 saved = 0.0;
534 for (std::size_t r = 0; r < j + 1; ++r) {
535 temp = derivs[r] / (right[r] + left[j - r]);
536 derivs[r] = saved + right[r] * temp;
537 saved = left[j - r] * temp;
538 }
539 derivs[j + 1] = saved;
540 }
541
542 /*
543 * Compute derivatives at x using values stored in bsdx and formula
544 * for spline derivative based on difference of splines of degree degree-1
545 */
546 saved = degree() * derivs[0] / (get_knot(icell + 1) - get_knot(icell + 1 - degree()));
547 derivs[0] = -saved;
548 for (std::size_t j = 1; j < degree(); ++j) {
549 temp = saved;
550 saved = degree() * derivs[j]
551 / (get_knot(icell + j + 1) - get_knot(icell + j + 1 - degree()));
552 derivs[j] = temp - saved;
553 }
554 derivs[degree()] = saved;
555
557}
558
559template <class Tag, std::size_t D>
560template <class DDim, class MemorySpace>
563 ddc::DSpan2D const derivs,
564 ddc::Coordinate<Tag> const& x,
565 std::size_t const n) const
566{
567 std::array<double, degree()> left;
568 std::array<double, degree()> right;
569
570 std::array<double, 2 * (degree() + 1)> a_ptr;
571 std::experimental::
572 mdspan<double, std::experimental::extents<std::size_t, degree() + 1, 2>> const a(
573 a_ptr.data());
574
575 std::array<double, (degree() + 1) * (degree() + 1)> ndu_ptr;
576 std::experimental::mdspan<
577 double,
578 std::experimental::extents<std::size_t, degree() + 1, degree() + 1>> const
579 ndu(ndu_ptr.data());
580
581 assert(x >= rmin());
582 assert(x <= rmax());
583 // assert(n >= 0); as long as n is unsigned
584 assert(n <= degree());
585 assert(derivs.extent(0) == 1 + degree());
586 assert(derivs.extent(1) == 1 + n);
587
588 // 1. Compute cell index 'icell' and x_offset
589 int const icell = find_cell(x);
590
591 assert(icell >= 0);
592 assert(icell <= int(ncells() - 1));
593 assert(get_knot(icell) <= x);
594 assert(get_knot(icell + 1) >= x);
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, temp;
605 ndu(0, 0) = 1.0;
606 for (std::size_t j = 0; j < degree(); ++j) {
607 left[j] = x - get_knot(icell - j);
608 right[j] = get_knot(icell + j + 1) - x;
609 saved = 0.0;
610 for (std::size_t r = 0; r < j + 1; ++r) {
611 // compute inverse of knot differences and save them into lower
612 // triangular part of ndu
613 ndu(r, j + 1) = 1.0 / (right[r] + left[j - r]);
614 // compute basis functions and save them into upper triangular part
615 // of ndu
616 temp = ndu(j, r) * ndu(r, j + 1);
617 ndu(j + 1, r) = saved + right[r] * temp;
618 saved = left[j - r] * temp;
619 }
620 ndu(j + 1, j + 1) = saved;
621 }
622 // Save 0-th derivative
623 for (std::size_t j = 0; j < degree() + 1; ++j) {
624 derivs(j, 0) = ndu(degree(), j);
625 }
626
627 for (int r = 0; r < int(degree() + 1); ++r) {
628 int s1 = 0;
629 int s2 = 1;
630 a(0, 0) = 1.0;
631 for (int k = 1; k < int(n + 1); ++k) {
632 double d = 0.0;
633 int const rk = r - k;
634 int const pk = degree() - k;
635 if (r >= k) {
636 a(0, s2) = a(0, s1) * ndu(rk, pk + 1);
637 d = a(0, s2) * ndu(pk, rk);
638 }
639 int const j1 = rk > -1 ? 1 : (-rk);
640 int const j2 = (r - 1) <= pk ? k : (degree() - r + 1);
641 for (int j = j1; j < j2; ++j) {
642 a(j, s2) = (a(j, s1) - a(j - 1, s1)) * ndu(rk + j, pk + 1);
643 d += a(j, s2) * ndu(pk, rk + j);
644 }
645 if (r <= pk) {
646 a(k, s2) = -a(k - 1, s1) * ndu(r, pk + 1);
647 d += a(k, s2) * ndu(pk, r);
648 }
649 derivs(r, k) = d;
650 Kokkos::kokkos_swap(s1, s2);
651 }
652 }
653
654 int r = degree();
655 for (int k = 1; k < int(n + 1); ++k) {
656 for (std::size_t i = 0; i < derivs.extent(0); i++) {
657 derivs(i, k) *= r;
658 }
659 r *= degree() - k;
660 }
661
663}
664
665template <class Tag, std::size_t D>
666template <class DDim, class MemorySpace>
668 ddc::Coordinate<Tag> const& x) const
669{
670 if (x > rmax())
671 return -1;
672 if (x < rmin())
673 return -1;
674
675 if (x == rmin())
676 return 0;
677 if (x == rmax())
678 return ncells() - 1;
679
680 // Binary search
681 int low = 0, high = ncells();
682 int icell = (low + high) / 2;
683 while (x < get_knot(icell) || x >= get_knot(icell + 1)) {
684 if (x < get_knot(icell)) {
685 high = icell;
686 } else {
687 low = icell;
688 }
689 icell = (low + high) / 2;
690 }
691 return icell;
692}
693
694template <class Tag, std::size_t D>
695template <class DDim, class MemorySpace>
696template <class Layout, class MemorySpace2>
700{
701 if constexpr (is_periodic()) {
702 assert(int_vals.size() == nbasis() || int_vals.size() == size());
703 } else {
704 assert(int_vals.size() == nbasis());
705 }
706
707 double const inv_deg = 1.0 / (degree() + 1);
708
710 full_domain().take_first(discrete_vector_type {nbasis()}));
711 for (auto ix : dom_bsplines) {
712 int_vals(ix) = (get_last_support_knot(ix) - get_first_support_knot(ix)) * inv_deg;
713 }
714
715 if constexpr (is_periodic()) {
716 if (int_vals.size() == size()) {
718 full_domain().take_last(discrete_vector_type {degree()}));
719 for (auto ix : dom_bsplines_wrap) {
720 int_vals(ix) = 0;
721 }
722 }
723 }
724 return int_vals;
725}
726} // namespace ddc
Definition discrete_domain.hpp:51
A DiscreteVector is a vector in the discrete dimension.
Definition discrete_vector.hpp:254
Storage class of the static attributes of the discrete dimension.
Definition bsplines_non_uniform.hpp:86
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > 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...
Definition bsplines_non_uniform.hpp:282
Impl(std::initializer_list< ddc::Coordinate< Tag > > breaks)
Constructs an Impl using a brace-list, i.e.
Definition bsplines_non_uniform.hpp:119
Impl(std::vector< ddc::Coordinate< Tag > > const &breaks)
Constructs an Impl using a std::vector.
Definition bsplines_non_uniform.hpp:131
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis_and_n_derivs(ddc::DSpan2D derivs, ddc::Coordinate< Tag > const &x, std::size_t n) const
Evaluates non-zero B-spline values and derivatives at a given coordinate.
Definition bsplines_non_uniform.hpp:562
Impl(Impl &&x)=default
Move-constructs.
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > 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...
Definition bsplines_non_uniform.hpp:297
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > rmin() const noexcept
Returns the coordinate of the first break point of the domain on which the B-splines are defined.
Definition bsplines_non_uniform.hpp:323
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > rmax() const noexcept
Returns the coordinate of the last break point of the domain on which the B-splines are defined.
Definition bsplines_non_uniform.hpp:332
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > get_knot(int knot_idx) const noexcept
Returns the coordinate of the knot corresponding to the given index.
Definition bsplines_non_uniform.hpp:268
~Impl()=default
Destructs.
KOKKOS_INLINE_FUNCTION ddc::ChunkSpan< double, ddc::DiscreteDomain< DDim >, Layout, MemorySpace2 > integrals(ddc::ChunkSpan< double, discrete_domain_type, Layout, MemorySpace2 > int_vals) const
Compute the integrals of the B-splines.
Definition bsplines_non_uniform.hpp:698
KOKKOS_INLINE_FUNCTION std::size_t size() const noexcept
Returns the number of elements necessary to construct a spline representation of a function.
Definition bsplines_non_uniform.hpp:354
Impl & operator=(Impl &&x)=default
Move-assigns.
KOKKOS_INLINE_FUNCTION double length() const noexcept
Returns the length of the domain.
Definition bsplines_non_uniform.hpp:341
Impl(Impl< DDim, OriginMemorySpace > const &impl)
Copy-constructs from another Impl with a different Kokkos memory space.
Definition bsplines_non_uniform.hpp:162
DiscreteVector< DDim > discrete_vector_type
The type of a DiscreteVector representing an "index displacement" between two B-splines.
Definition bsplines_non_uniform.hpp:108
KOKKOS_INLINE_FUNCTION std::size_t ncells() const noexcept
Returns the number of cells over which the B-splines are defined.
Definition bsplines_non_uniform.hpp:397
KOKKOS_INLINE_FUNCTION discrete_element_type eval_deriv(DSpan1D derivs, ddc::Coordinate< Tag > const &x) const
Evaluates non-zero B-spline derivatives at a given coordinate.
Definition bsplines_non_uniform.hpp:504
KOKKOS_INLINE_FUNCTION discrete_domain_type full_domain() const
Returns the discrete domain including eventual additional B-splines in the periodic case.
Definition bsplines_non_uniform.hpp:363
Impl(Impl const &x)=default
Copy-constructs.
KOKKOS_INLINE_FUNCTION std::size_t nbasis() const noexcept
Returns the number of basis functions.
Definition bsplines_non_uniform.hpp:385
KOKKOS_INLINE_FUNCTION std::size_t npoints() const noexcept
The number of break points.
Definition bsplines_non_uniform.hpp:374
KOKKOS_INLINE_FUNCTION discrete_element_type eval_basis(DSpan1D values, ddc::Coordinate< Tag > const &x) const
Evaluates non-zero B-splines at a given coordinate.
Definition bsplines_non_uniform.hpp:464
Impl & operator=(Impl const &x)=default
Copy-assigns.
DiscreteDomain< DDim > discrete_domain_type
The type of a DiscreteDomain whose elements identify the B-splines.
Definition bsplines_non_uniform.hpp:102
friend class Impl
Definition bsplines_non_uniform.hpp:88
DiscreteElement< DDim > discrete_element_type
The type of a DiscreteElement identifying a B-spline.
Definition bsplines_non_uniform.hpp:105
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > get_support_knot_n(discrete_element_type const &ix, int n) const
Returns the coordinate of the (n+1)-th knot in the support of the identified B-spline.
Definition bsplines_non_uniform.hpp:312
The type of a non-uniform 1D spline basis (B-spline).
Definition bsplines_non_uniform.hpp:42
static constexpr bool is_uniform() noexcept
Indicates if the B-splines are uniform or not (this is not the case here).
Definition bsplines_non_uniform.hpp:74
Tag tag_type
The tag identifying the continuous dimension on which the support of the B-splines are defined.
Definition bsplines_non_uniform.hpp:47
static constexpr std::size_t degree() noexcept
The degree of B-splines.
Definition bsplines_non_uniform.hpp:56
static constexpr bool is_periodic() noexcept
Indicates if the B-splines are periodic or not.
Definition bsplines_non_uniform.hpp:65
The top-level namespace of DDC.
Definition aligned_allocator.hpp:11
constexpr bool enable_chunk
Definition chunk_traits.hpp:16
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type > rmax(DiscreteDomain< DDim > const &d)
Definition non_uniform_point_sampling.hpp:182
ddc::Span2D< double > DSpan2D
Definition view.hpp:128
ddc::Span1D< double > DSpan1D
Definition view.hpp:126
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type... > coordinate(DiscreteElement< DDim... > const &c)
Definition coordinate.hpp:29
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type > rmin(DiscreteDomain< DDim > const &d)
Definition non_uniform_point_sampling.hpp:175
detail::TaggedVector< CoordinateElement, CDims... > Coordinate
A Coordinate represents a coordinate in the continuous space.
Definition coordinate.hpp:26
constexpr bool is_non_uniform_bsplines_v
Indicates if a tag corresponds to non-uniform B-splines or not.
Definition bsplines_non_uniform.hpp:418
Definition chunk_span.hpp:30
Definition bsplines_non_uniform.hpp:409