DDC 0.0.0

a discrete domain computation library

bsplines_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
11#include <ddc/ddc.hpp>
12
13#include "math_tools.hpp"
14#include "view.hpp"
15
16namespace ddc {
17
18namespace detail {
19
20template <class T>
21struct UniformBsplinesKnots : UniformPointSampling<typename T::tag_type>
22{
23};
24
25struct UniformBSplinesBase
26{
27};
28
29} // namespace detail
30
40template <class Tag, std::size_t D>
41class UniformBSplines : detail::UniformBSplinesBase
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 true;
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::UniformBsplinesKnots<DDim>;
92
93 // In the periodic case, it contains twice the periodic point!!!
95
96 public:
99
102
105
108
109 Impl() = default;
110
118 : m_domain(
119 ddc::DiscreteElement<mesh_type>(0),
120 ddc::DiscreteVector<mesh_type>(
121 ncells + 1)) // Create a mesh including the eventual periodic point
122 {
123 assert(ncells > 0);
125 mesh_type::template init<
127 }
128
133 template <class OriginMemorySpace>
134 explicit Impl(Impl<DDim, OriginMemorySpace> const& impl) : m_domain(impl.m_domain)
135 {
136 }
137
142 Impl(Impl const& x) = default;
143
148 Impl(Impl&& x) = default;
149
151 ~Impl() = default;
152
158 Impl& operator=(Impl const& x) = default;
159
165 Impl& operator=(Impl&& x) = default;
166
181 {
182 assert(values.size() == degree() + 1);
183 return eval_basis(values, x, degree());
184 }
185
200
216 ddc::Coordinate<Tag> const& x,
217 std::size_t n) const;
218
226 template <class Layout, class MemorySpace2>
230 int_vals) const;
231
246
257 {
258 return get_knot(ix.uid() - degree());
259 }
260
271 {
272 return get_knot(ix.uid() + 1);
273 }
274
285 const
286 {
287 return get_knot(ix.uid() + n - degree());
288 }
289
298
307
313 {
314 return rmax() - rmin();
315 }
316
326 {
327 return degree() + ncells();
328 }
329
338
346 {
347 return ncells() + !is_periodic() * degree();
348 }
349
358 {
359 return m_domain.size() - 1;
360 }
361
362 private:
363 KOKKOS_INLINE_FUNCTION double inv_step() const noexcept
364 {
365 return 1.0 / ddc::step<mesh_type>();
366 }
367
369 eval_basis(DSpan1D values, ddc::Coordinate<Tag> const& x, std::size_t degree) const;
370
371 KOKKOS_INLINE_FUNCTION void get_icell_and_offset(
372 int& icell,
373 double& offset,
374 ddc::Coordinate<Tag> const& x) const;
375 };
376};
377
378template <class DDim>
379struct is_uniform_bsplines : public std::is_base_of<detail::UniformBSplinesBase, DDim>
380{
381};
382
388template <class DDim>
390
391template <class Tag, std::size_t D>
392template <class DDim, class MemorySpace>
396 ddc::Coordinate<Tag> const& x,
397 [[maybe_unused]] std::size_t const deg) const
398{
399 assert(values.size() == deg + 1);
400
401 double offset;
402 int jmin;
403 // 1. Compute cell index 'icell' and x_offset
404 // 2. Compute index range of B-splines with support over cell 'icell'
405 get_icell_and_offset(jmin, offset, x);
406
407 // 3. Compute values of aforementioned B-splines
408 double xx, temp, saved;
409 values(0) = 1.0;
410 for (std::size_t j = 1; j < values.size(); ++j) {
411 xx = -offset;
412 saved = 0.0;
413 for (std::size_t r = 0; r < j; ++r) {
414 xx += 1;
415 temp = values(r) / j;
416 values(r) = saved + xx * temp;
417 saved = (j - xx) * temp;
418 }
419 values(j) = saved;
420 }
421
422 return discrete_element_type(jmin);
423}
424
425template <class Tag, std::size_t D>
426template <class DDim, class MemorySpace>
429{
430 assert(derivs.size() == degree() + 1);
431
432 double offset;
433 int jmin;
434 // 1. Compute cell index 'icell' and x_offset
435 // 2. Compute index range of B-splines with support over cell 'icell'
436 get_icell_and_offset(jmin, offset, x);
437
438 // 3. Compute derivatives of aforementioned B-splines
439 // Derivatives are normalized, hence they should be divided by dx
440 double xx, temp, saved;
441 derivs(0) = 1.0 / ddc::step<mesh_type>();
442 for (std::size_t j = 1; j < degree(); ++j) {
443 xx = -offset;
444 saved = 0.0;
445 for (std::size_t r = 0; r < j; ++r) {
446 xx += 1.0;
447 temp = derivs(r) / j;
448 derivs(r) = saved + xx * temp;
449 saved = (j - xx) * temp;
450 }
451 derivs(j) = saved;
452 }
453
454 // Compute derivatives
455 double bjm1 = derivs[0];
456 double bj = bjm1;
457 derivs(0) = -bjm1;
458 for (std::size_t j = 1; j < degree(); ++j) {
459 bj = derivs(j);
460 derivs(j) = bjm1 - bj;
461 bjm1 = bj;
462 }
463 derivs(degree()) = bj;
464
466}
467
468template <class Tag, std::size_t D>
469template <class DDim, class MemorySpace>
472 ddc::DSpan2D const derivs,
473 ddc::Coordinate<Tag> const& x,
474 std::size_t const n) const
475{
476 std::array<double, (degree() + 1) * (degree() + 1)> ndu_ptr;
477 std::experimental::mdspan<
478 double,
479 std::experimental::extents<std::size_t, degree() + 1, degree() + 1>> const
480 ndu(ndu_ptr.data());
481 std::array<double, 2 * (degree() + 1)> a_ptr;
482 std::experimental::
483 mdspan<double, std::experimental::extents<std::size_t, degree() + 1, 2>> const a(
484 a_ptr.data());
485 double offset;
486 int jmin;
487
488 assert(x >= rmin());
489 assert(x <= rmax());
490 // assert(n >= 0); as long as n is unsigned
491 assert(n <= degree());
492 assert(derivs.extent(0) == 1 + degree());
493 assert(derivs.extent(1) == 1 + n);
494
495 // 1. Compute cell index 'icell' and x_offset
496 // 2. Compute index range of B-splines with support over cell 'icell'
497 get_icell_and_offset(jmin, offset, x);
498
499 // 3. Recursively evaluate B-splines (eval_basis)
500 // up to self%degree, and store them all in the upper-right triangle of
501 // ndu
502 double xx, temp, saved;
503 ndu(0, 0) = 1.0;
504 for (std::size_t j = 1; j < degree() + 1; ++j) {
505 xx = -offset;
506 saved = 0.0;
507 for (std::size_t r = 0; r < j; ++r) {
508 xx += 1.0;
509 temp = ndu(j - 1, r) / j;
510 ndu(j, r) = saved + xx * temp;
511 saved = (j - xx) * temp;
512 }
513 ndu(j, j) = saved;
514 }
515 for (std::size_t i = 0; i < ndu.extent(1); ++i) {
516 derivs(i, 0) = ndu(degree(), i);
517 }
518
519 for (int r = 0; r < int(degree() + 1); ++r) {
520 int s1 = 0;
521 int s2 = 1;
522 a(0, 0) = 1.0;
523 for (int k = 1; k < int(n + 1); ++k) {
524 double d = 0.0;
525 int const rk = r - k;
526 int const pk = degree() - k;
527 if (r >= k) {
528 a(0, s2) = a(0, s1) / (pk + 1);
529 d = a(0, s2) * ndu(pk, rk);
530 }
531 int const j1 = rk > -1 ? 1 : (-rk);
532 int const j2 = (r - 1) <= pk ? k : (degree() - r + 1);
533 for (int j = j1; j < j2; ++j) {
534 a(j, s2) = (a(j, s1) - a(j - 1, s1)) / (pk + 1);
535 d += a(j, s2) * ndu(pk, rk + j);
536 }
537 if (r <= pk) {
538 a(k, s2) = -a(k - 1, s1) / (pk + 1);
539 d += a(k, s2) * ndu(pk, r);
540 }
541 derivs(r, k) = d;
542 Kokkos::kokkos_swap(s1, s2);
543 }
544 }
545
546 // Multiply result by correct factors:
547 // degree!/(degree-n)! = degree*(degree-1)*...*(degree-n+1)
548 // k-th derivatives are normalized, hence they should be divided by dx^k
549 double const inv_dx = inv_step();
550 double d = degree() * inv_dx;
551 for (int k = 1; k < int(n + 1); ++k) {
552 for (std::size_t i = 0; i < derivs.extent(0); ++i) {
553 derivs(i, k) *= d;
554 }
555 d *= (degree() - k) * inv_dx;
556 }
557
559}
560
561template <class Tag, std::size_t D>
562template <class DDim, class MemorySpace>
564 int& icell,
565 double& offset,
566 ddc::Coordinate<Tag> const& x) const
567{
568 assert(x >= rmin());
569 assert(x <= rmax());
570
571 double const inv_dx = inv_step();
572 if (x == rmin()) {
573 icell = 0;
574 offset = 0.0;
575 } else if (x == rmax()) {
576 icell = ncells() - 1;
577 offset = 1.0;
578 } else {
579 offset = (x - rmin()) * inv_dx;
580 icell = static_cast<int>(offset);
581 offset = offset - icell;
582
583 // When x is very close to xmax, round-off may cause the wrong answer
584 // icell=ncells and x_offset=0, which we convert to the case x=xmax:
585 if (icell == int(ncells()) && offset == 0.0) {
586 icell = ncells() - 1;
587 offset = 1.0;
588 }
589 }
590}
591
592template <class Tag, std::size_t D>
593template <class DDim, class MemorySpace>
594template <class Layout, class MemorySpace2>
598{
599 if constexpr (is_periodic()) {
600 assert(int_vals.size() == nbasis() || int_vals.size() == size());
601 } else {
602 assert(int_vals.size() == nbasis());
603 }
604 discrete_domain_type const full_dom_splines(full_domain());
605
606 if constexpr (is_periodic()) {
608 full_dom_splines.take_first(discrete_vector_type {nbasis()}));
609 for (auto ix : dom_bsplines) {
611 }
612 if (int_vals.size() == size()) {
614 full_dom_splines.take_last(discrete_vector_type {degree()}));
615 for (auto ix : dom_bsplines_repeated) {
616 int_vals(ix) = 0;
617 }
618 }
619 } else {
622 .remove(discrete_vector_type(degree()), discrete_vector_type(degree()));
625 }
626
627 std::array<double, degree() + 2> edge_vals_ptr;
628 std::experimental::
629 mdspan<double, std::experimental::extents<std::size_t, degree() + 2>> const
630 edge_vals(edge_vals_ptr.data());
631
632 eval_basis(edge_vals, rmin(), degree() + 1);
633
634 double const d_eval = ddc::detail::sum(edge_vals);
635
636 for (std::size_t i = 0; i < degree(); ++i) {
637 double const c_eval = ddc::detail::sum(edge_vals, 0, degree() - i);
638
639 double const edge_value = ddc::step<mesh_type>() * (d_eval - c_eval);
640
642 int_vals(discrete_element_type(nbasis() - 1 - i)) = edge_value;
643 }
644 }
645 return int_vals;
646}
647} // namespace ddc
Definition discrete_domain.hpp:51
KOKKOS_FUNCTION constexpr std::size_t size() const
Definition discrete_domain.hpp:137
KOKKOS_FUNCTION constexpr discrete_element_type front() const noexcept
Definition discrete_domain.hpp:154
KOKKOS_FUNCTION constexpr DiscreteDomain remove(mlength_type n1, mlength_type n2) const
Definition discrete_domain.hpp:184
KOKKOS_FUNCTION constexpr discrete_element_type back() const noexcept
Definition discrete_domain.hpp:159
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_uniform.hpp:86
Impl & operator=(Impl const &x)=default
Copy-assigns.
Impl(ddc::Coordinate< Tag > rmin, ddc::Coordinate< Tag > rmax, std::size_t ncells)
Constructs a spline basis (B-splines) with n equidistant knots over .
Definition bsplines_uniform.hpp:117
KOKKOS_INLINE_FUNCTION discrete_domain_type full_domain() const
Returns the discrete domain including eventual additional B-splines in the periodic case.
Definition bsplines_uniform.hpp:334
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_uniform.hpp:242
KOKKOS_INLINE_FUNCTION std::size_t nbasis() const noexcept
Returns the number of basis functions.
Definition bsplines_uniform.hpp:345
DiscreteDomain< DDim > discrete_domain_type
The type of a DiscreteDomain whose elements identify the B-splines.
Definition bsplines_uniform.hpp:101
DiscreteElement< DDim > discrete_element_type
The type of a DiscreteElement identifying a B-spline.
Definition bsplines_uniform.hpp:104
KOKKOS_INLINE_FUNCTION double 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_uniform.hpp:284
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_uniform.hpp:428
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_uniform.hpp:596
Impl(Impl &&x)=default
Move-constructs.
KOKKOS_INLINE_FUNCTION double 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_uniform.hpp:256
KOKKOS_INLINE_FUNCTION std::size_t ncells() const noexcept
Returns the number of cells over which the B-splines are defined.
Definition bsplines_uniform.hpp:357
DiscreteVector< DDim > discrete_vector_type
The type of a DiscreteVector representing an "index displacement" between two B-splines.
Definition bsplines_uniform.hpp:107
KOKKOS_INLINE_FUNCTION double 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_uniform.hpp:270
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > rmax() const noexcept
Returns the coordinate of the upper bound of the domain on which the B-splines are defined.
Definition bsplines_uniform.hpp:303
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_uniform.hpp:180
Impl & operator=(Impl &&x)=default
Move-assigns.
Impl(Impl const &x)=default
Copy-constructs.
Impl(Impl< DDim, OriginMemorySpace > const &impl)
Copy-constructs from another Impl with a different Kokkos memory space.
Definition bsplines_uniform.hpp:134
KOKKOS_INLINE_FUNCTION ddc::Coordinate< Tag > rmin() const noexcept
Returns the coordinate of the lower bound of the domain on which the B-splines are defined.
Definition bsplines_uniform.hpp:294
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_uniform.hpp:471
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_uniform.hpp:325
KOKKOS_INLINE_FUNCTION double length() const noexcept
Returns the length of the domain.
Definition bsplines_uniform.hpp:312
~Impl()=default
Destructs.
The type of a uniform 1D spline basis (B-spline).
Definition bsplines_uniform.hpp:42
static constexpr std::size_t degree() noexcept
The degree of B-splines.
Definition bsplines_uniform.hpp:56
static constexpr bool is_periodic() noexcept
Indicates if the B-splines are periodic or not.
Definition bsplines_uniform.hpp:65
static constexpr bool is_uniform() noexcept
Indicates if the B-splines are uniform or not (this is the case here).
Definition bsplines_uniform.hpp:74
Tag tag_type
The tag identifying the continuous dimension on which the support of the B-splines are defined.
Definition bsplines_uniform.hpp:47
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
constexpr bool is_uniform_bsplines_v
Indicates if a tag corresponds to uniform B-splines or not.
Definition bsplines_uniform.hpp:389
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
Definition chunk_span.hpp:30
Definition bsplines_uniform.hpp:380