Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_bsplines_non_uniform.F90
Go to the documentation of this file.
1 
5 
7 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 #include "sll_assert.h"
9 
10  use sll_m_working_precision, only: f64
12 
13  implicit none
14 
15  public :: &
17 
18  private
19 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20 
22  integer, parameter :: wp = f64
23 
26 
27  contains
28  procedure :: init => s_bsplines_non_uniform__init
29  procedure :: free => s_bsplines_non_uniform__free
30  procedure :: find_cell => f_bsplines_non_uniform__find_cell
31  procedure :: eval_basis => s_bsplines_non_uniform__eval_basis
32  procedure :: eval_deriv => s_bsplines_non_uniform__eval_deriv
33  procedure :: eval_basis_and_n_derivs => s_bsplines_non_uniform__eval_basis_and_n_derivs
34 
36 
37 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
38 contains
39 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
40 
41  !-----------------------------------------------------------------------------
47  !-----------------------------------------------------------------------------
48  subroutine s_bsplines_non_uniform__init(self, degree, periodic, breaks)
49  class(sll_t_bsplines_non_uniform), intent(out) :: self
50  integer, intent(in) :: degree
51  logical, intent(in) :: periodic
52  real(wp), intent(in) :: breaks(:)
53 
54  integer :: i
55  real(wp) :: period ! length of period for periodic B-splines
56 
57  ! Public attributes
58  self%degree = degree
59  self%periodic = periodic
60  self%uniform = .false.
61  self%ncells = size(breaks) - 1
62  self%nbasis = merge(self%ncells, self%ncells + degree, periodic)
63  self%xmin = breaks(1)
64  self%xmax = breaks(self%ncells + 1)
65 
66  ! Create the knots array from the grid points. Here take the grid points
67  ! as knots and simply add to the left and the right the
68  ! amount of knots that depends on the degree of the requested
69  ! spline. We aim at setting up the indexing in such a way that the
70  ! original indexing of 'grid' is preserved, i.e.: grid(i) = knot(i), at
71  ! least whenever the scope of the indices defined here is active.
72  associate(npoints => self%ncells + 1)
73 
74  allocate (self%knots(1 - degree:npoints + degree))
75 
76  do i = 1, npoints
77  self%knots(i) = breaks(i)
78  end do
79 
80  ! Fill out the extra nodes
81  if (periodic) then
82  period = breaks(npoints) - breaks(1)
83  do i = 1, degree
84  self%knots(1 - i) = breaks(npoints - i) - period
85  self%knots(npoints + i) = breaks(i + 1) + period
86  end do
87  else ! open
88  do i = 1, degree
89  self%knots(1 - i) = breaks(1)
90  self%knots(npoints + i) = breaks(npoints)
91  end do
92  end if
93 
94  end associate
95 
96  end subroutine s_bsplines_non_uniform__init
97 
98  !-----------------------------------------------------------------------------
101  !-----------------------------------------------------------------------------
103  class(sll_t_bsplines_non_uniform), intent(inout) :: self
104 
105  deallocate (self%knots)
106 
107  end subroutine s_bsplines_non_uniform__free
108 
109  !----------------------------------------------------------------------------
114  !----------------------------------------------------------------------------
115  sll_pure function f_bsplines_non_uniform__find_cell(self, x) result(icell)
116  class(sll_t_bsplines_non_uniform), intent(in) :: self
117  real(wp), intent(in) :: x
118  integer :: icell
119 
120  integer :: low, high
121 
122  associate(npoints => self%ncells + 1)
123 
124  ! check if point is outside of grid
125  if (x > self%knots(npoints)) then; icell = -1; return; end if
126  if (x < self%knots(1)) then; icell = -1; return; end if
127 
128  ! check if point is exactly on left/right boundary
129  if (x == self%knots(1)) then; icell = 1; return; end if
130  if (x == self%knots(npoints)) then; icell = self%ncells; return; end if
131 
132  low = 1
133  high = npoints
134  icell = (low + high)/2
135  do while (x < self%knots(icell) .or. x >= self%knots(icell + 1))
136  if (x < self%knots(icell)) then
137  high = icell
138  else
139  low = icell
140  end if
141  icell = (low + high)/2
142  end do
143 
144  end associate
145 
146  end function
147 
148  !-----------------------------------------------------------------------------
156  !-----------------------------------------------------------------------------
157  sll_pure subroutine s_bsplines_non_uniform__eval_basis(self, x, values, jmin)
158  class(sll_t_bsplines_non_uniform), intent(in) :: self
159  real(wp), intent(in) :: x
160  real(wp), intent(out) :: values(0:)
161  integer, intent(out) :: jmin
162 
163  integer :: icell
164 
165  integer :: j, r
166  real(wp) :: temp
167  real(wp) :: saved
168 
169  ! GFortran: to allocate on stack use -fstack-arrays
170  real(wp) :: left(1:self%degree)
171  real(wp) :: right(1:self%degree)
172 
173  ! Check on inputs
174  sll_assert(x >= self%xmin)
175  sll_assert(x <= self%xmax)
176  sll_assert(size(values) == 1 + self%degree)
177 
178  ! 1. Compute cell index 'icell'
179 ! icell = f_bsplines_non_uniform__find_cell( self, x )
180  icell = self%find_cell(x)
181  sll_assert(icell >= 1)
182  sll_assert(icell <= self%ncells)
183  sll_assert(self%knots(icell) <= x)
184  sll_assert(x <= self%knots(icell + 1))
185 
186  ! 2. Compute index range of B-splines with support over cell 'icell'
187  jmin = icell
188 
189  ! 3. Compute values of aforementioned B-splines
190  values(0) = 1.0_wp
191  do j = 1, self%degree
192  left(j) = x - self%knots(icell + 1 - j)
193  right(j) = self%knots(icell + j) - x
194  saved = 0.0_wp
195  do r = 0, j - 1
196  temp = values(r)/(right(r + 1) + left(j - r))
197  values(r) = saved + right(r + 1)*temp
198  saved = left(j - r)*temp
199  end do
200  values(j) = saved
201  end do
202 
203  end subroutine s_bsplines_non_uniform__eval_basis
204 
205  !-----------------------------------------------------------------------------
213  !-----------------------------------------------------------------------------
214  sll_pure subroutine s_bsplines_non_uniform__eval_deriv(self, x, derivs, jmin)
215  class(sll_t_bsplines_non_uniform), intent(in) :: self
216  real(wp), intent(in) :: x
217  real(wp), intent(out) :: derivs(0:)
218  integer, intent(out) :: jmin
219 
220  integer :: icell
221 
222  integer :: j, r
223  real(wp) :: temp
224  real(wp) :: saved
225 
226  ! GFortran: to allocate on stack use -fstack-arrays
227  real(wp) :: left(1:self%degree)
228  real(wp) :: right(1:self%degree)
229 
230  ! Check on inputs
231  sll_assert(x >= self%xmin)
232  sll_assert(x <= self%xmax)
233  sll_assert(size(derivs) == 1 + self%degree)
234 
235  ! 1. Compute cell index 'icell' and x_offset
236 ! icell = f_bsplines_non_uniform__find_cell( self, x )
237  icell = self%find_cell(x)
238  sll_assert(icell >= 1)
239  sll_assert(icell <= self%ncells)
240  sll_assert(self%knots(icell) <= x)
241  sll_assert(x <= self%knots(icell + 1))
242 
243  ! 2. Compute index range of B-splines with support over cell 'icell'
244  jmin = icell
245 
246  ! 3. Compute derivatives of aforementioned B-splines
247  associate(degree => self%degree, degree_real => real(self%degree, wp))
248 
249  ! Compute nonzero basis functions and knot differences
250  ! for splines up to degree deg-1 which are needed to compute derivative
251  ! First part of Algorithm A3.2 of NURBS book
252  derivs(0) = 1.0_wp
253  do j = 1, degree - 1
254  left(j) = x - self%knots(icell + 1 - j)
255  right(j) = self%knots(icell + j) - x
256  saved = 0.0_wp
257  do r = 0, j - 1
258  ! compute and save bspline values
259  temp = derivs(r)/(right(r + 1) + left(j - r))
260  derivs(r) = saved + right(r + 1)*temp
261  saved = left(j - r)*temp
262  end do
263  derivs(j) = saved
264  end do
265 
266  ! Compute derivatives at x using values stored in bsdx and formula
267  ! formula for spline derivative based on difference of splines of
268  ! degree deg-1
269  ! -------
270  ! j = 0
271  saved = degree_real*derivs(0)/(self%knots(icell + 1) - self%knots(icell + 1 - degree))
272  derivs(0) = -saved
273  do j = 1, degree - 1
274  temp = saved
275  saved = degree_real*derivs(j)/(self%knots(icell + j + 1) - self%knots(icell + j + 1 - degree))
276  derivs(j) = temp - saved
277  end do
278  ! j = degree
279  derivs(degree) = saved
280 
281  end associate
282 
283  end subroutine s_bsplines_non_uniform__eval_deriv
284 
285  !-----------------------------------------------------------------------------
294  !-----------------------------------------------------------------------------
295  sll_pure subroutine s_bsplines_non_uniform__eval_basis_and_n_derivs(self, x, n, derivs, jmin)
296  class(sll_t_bsplines_non_uniform), intent(in) :: self
297  real(wp), intent(in) :: x
298  integer, intent(in) :: n
299  real(wp), intent(out) :: derivs(0:, 0:)
300  integer, intent(out) :: jmin
301 
302  integer :: icell
303 
304  integer :: j, r
305  real(wp) :: temp
306  real(wp) :: saved
307 
308  integer :: k, s1, s2, rk, pk, j1, j2
309  real(wp) :: d
310 
311  ! GFortran: to allocate on stack use -fstack-arrays
312  real(wp) :: left(1:self%degree)
313  real(wp) :: right(1:self%degree)
314  real(wp) :: ndu(0:self%degree, 0:self%degree)
315  real(wp) :: a(0:1, 0:self%degree)
316 
317  ! Check on inputs
318  sll_assert(x >= self%xmin)
319  sll_assert(x <= self%xmax)
320  sll_assert(n >= 0)
321  sll_assert(n <= self%degree)
322  sll_assert(size(derivs, 1) == 1 + n)
323  sll_assert(size(derivs, 2) == 1 + self%degree)
324 
325  ! 1. Compute cell index 'icell' and x_offset
326 ! icell = f_bsplines_non_uniform__find_cell( self, x )
327  icell = self%find_cell(x)
328  sll_assert(icell >= 1)
329  sll_assert(icell <= self%ncells)
330  sll_assert(self%knots(icell) <= x)
331  sll_assert(x <= self%knots(icell + 1))
332 
333  ! 2. Compute index range of B-splines with support over cell 'icell'
334  jmin = icell
335 
336  ! 3. Compute nonzero basis functions and knot differences for splines
337  ! up to degree (degree-1) which are needed to compute derivative
338  ! Algorithm A2.3 of NURBS book
339  !
340  ! 21.08.2017: save inverse of knot differences to avoid unnecessary divisions
341  ! [Yaman Güçlü, Edoardo Zoni]
342  associate(degree => self%degree)
343 
344  ndu(0, 0) = 1.0_wp
345  do j = 1, degree
346  left(j) = x - self%knots(icell + 1 - j)
347  right(j) = self%knots(icell + j) - x
348  saved = 0.0_wp
349  do r = 0, j - 1
350  ! compute inverse of knot differences and save them into lower triangular part of ndu
351  ndu(j, r) = 1.0_wp/(right(r + 1) + left(j - r))
352  ! compute basis functions and save them into upper triangular part of ndu
353  temp = ndu(r, j - 1)*ndu(j, r)
354  ndu(r, j) = saved + right(r + 1)*temp
355  saved = left(j - r)*temp
356  end do
357  ndu(j, j) = saved
358  end do
359  derivs(0, :) = ndu(:, degree)
360 
361  do r = 0, degree
362  s1 = 0
363  s2 = 1
364  a(0, 0) = 1.0_wp
365  do k = 1, n
366  d = 0.0_wp
367  rk = r - k
368  pk = degree - k
369  if (r >= k) then
370  a(s2, 0) = a(s1, 0)*ndu(pk + 1, rk)
371  d = a(s2, 0)*ndu(rk, pk)
372  end if
373  if (rk > -1) then
374  j1 = 1
375  else
376  j1 = -rk
377  end if
378  if (r - 1 <= pk) then
379  j2 = k - 1
380  else
381  j2 = degree - r
382  end if
383  do j = j1, j2
384  a(s2, j) = (a(s1, j) - a(s1, j - 1))*ndu(pk + 1, rk + j)
385  d = d + a(s2, j)*ndu(rk + j, pk)
386  end do
387  if (r <= pk) then
388  a(s2, k) = -a(s1, k - 1)*ndu(pk + 1, r)
389  d = d + a(s2, k)*ndu(r, pk)
390  end if
391  derivs(k, r) = d
392  j = s1
393  s1 = s2
394  s2 = j
395  end do
396  end do
397  r = degree
398  do k = 1, n
399  derivs(k, :) = derivs(k, :)*real(r, kind=wp)
400  r = r*(degree - k)
401  end do
402 
403  end associate
404 
405  end subroutine s_bsplines_non_uniform__eval_basis_and_n_derivs
406 
Abstract class for B-splines of arbitrary degree.
Derived type for non-uniform B-splines of arbitrary degree.
integer, parameter wp
Working precision.
subroutine s_bsplines_non_uniform__init(self, degree, periodic, breaks)
Initialize non-uniform B-splines object.
subroutine s_bsplines_non_uniform__free(self)
Free storage.
Module to select the kind parameter.
integer, parameter, public f64
f64 is the kind type for 64-bit reals (double precision)
    Report Typos and Errors