Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_bsplines_uniform.F90
Go to the documentation of this file.
1 
5 
7 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 #include "sll_assert.h"
9 #include "sll_errors.h"
10 
11  use sll_m_working_precision, only: f64
13 
14  implicit none
15 
16  public :: &
18 
19  private
20 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
21 
23  integer, parameter :: wp = f64
24 
27 
28  ! Private components (public ones defined in base class)
29  real(wp), private :: inv_dx
30 
31  contains
32  procedure :: init => s_bsplines_uniform__init
33  procedure :: free => s_bsplines_uniform__free
34  procedure :: find_cell => f_bsplines_uniform__find_cell
35  procedure :: eval_basis => s_bsplines_uniform__eval_basis
36  procedure :: eval_deriv => s_bsplines_uniform__eval_deriv
37  procedure :: eval_basis_and_n_derivs => s_bsplines_uniform__eval_basis_and_n_derivs
38 
39  end type sll_t_bsplines_uniform
40 
41  ! Inverse of integers for later use (max spline degree = 32)
42  integer :: index
43  real(wp), parameter :: inv(1:32) = [(1.0_wp/real(index, wp), index=1, 32)]
44 
45 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
46 contains
47 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
48 
49  ! Module subroutine, private
50  sll_pure subroutine s_bsplines_uniform__get_icell_and_offset(self, x, icell, x_offset)
51  class(sll_t_bsplines_uniform), intent(in) :: self
52  real(wp), intent(in) :: x
53  integer, intent(out) :: icell
54  real(wp), intent(out) :: x_offset
55 
56  sll_assert(x >= self%xmin)
57  sll_assert(x <= self%xmax)
58 
59  if (x == self%xmin) then; icell = 1; x_offset = 0.0_wp
60  else if (x == self%xmax) then; icell = self%ncells; x_offset = 1.0_wp
61  else
62  x_offset = (x - self%xmin)*self%inv_dx ! 0 <= x_offset <= num_cells
63  icell = int(x_offset)
64  x_offset = x_offset - real(icell, wp) ! 0 <= x_offset < 1
65  icell = icell + 1
66  end if
67 
68  ! When x is very close to xmax, round-off may cause the wrong answer
69  ! icell=ncells+1 and x_offset=0, which we convert to the case x=xmax:
70  if (icell == self%ncells + 1 .and. x_offset == 0.0_wp) then
71  icell = self%ncells
72  x_offset = 1.0_wp
73  end if
74 
75  end subroutine s_bsplines_uniform__get_icell_and_offset
76 
77  !-----------------------------------------------------------------------------
85  !-----------------------------------------------------------------------------
86  subroutine s_bsplines_uniform__init(self, degree, periodic, xmin, xmax, ncells)
87  class(sll_t_bsplines_uniform), intent(out) :: self
88  integer, intent(in) :: degree
89  logical, intent(in) :: periodic
90  real(wp), intent(in) :: xmin
91  real(wp), intent(in) :: xmax
92  integer, intent(in) :: ncells
93 
94  ! Public attributes
95  self%degree = degree
96  self%periodic = periodic
97  self%uniform = .true.
98  self%ncells = ncells
99  self%nbasis = merge(ncells, ncells + degree, periodic)
100  self%xmin = xmin
101  self%xmax = xmax
102 
103  ! Private attributes
104  self%inv_dx = real(ncells, wp)/(xmax - xmin)
105 
106  end subroutine s_bsplines_uniform__init
107 
108  !-----------------------------------------------------------------------------
111  !-----------------------------------------------------------------------------
112  subroutine s_bsplines_uniform__free(self)
113  class(sll_t_bsplines_uniform), intent(inout) :: self
114  end subroutine s_bsplines_uniform__free
115 
116  !-----------------------------------------------------------------------------
121  !-----------------------------------------------------------------------------
122  sll_pure function f_bsplines_uniform__find_cell(self, x) result(icell)
123  class(sll_t_bsplines_uniform), intent(in) :: self
124  real(wp), intent(in) :: x
125  integer :: icell
126 
127  icell = -1
128  !TODO: handle error within pure procedure
129  !SLL_ERROR("sll_t_bsplines_uniform % find_cell","procedure not implemented")
130 
131  end function f_bsplines_uniform__find_cell
132 
133  !-----------------------------------------------------------------------------
141  !-----------------------------------------------------------------------------
142  sll_pure subroutine s_bsplines_uniform__eval_basis(self, x, values, jmin)
143  class(sll_t_bsplines_uniform), intent(in) :: self
144  real(wp), intent(in) :: x
145  real(wp), intent(out) :: values(0:)
146  integer, intent(out) :: jmin
147 
148  integer :: icell
149  real(wp) :: x_offset
150 
151  integer :: j, r
152  real(wp) :: j_real
153  real(wp) :: xx
154  real(wp) :: temp
155  real(wp) :: saved
156 
157  ! Check on inputs
158  sll_assert(size(values) == 1 + self%degree)
159 
160  ! 1. Compute cell index 'icell' and x_offset
161  call s_bsplines_uniform__get_icell_and_offset(self, x, icell, x_offset)
162 
163  ! 2. Compute index range of B-splines with support over cell 'icell'
164  jmin = icell
165 
166  ! 3. Compute values of aforementioned B-splines
167  associate(bspl => values, spline_degree => self%degree)
168 
169  bspl(0) = 1.0_wp
170  do j = 1, spline_degree
171  j_real = real(j, wp)
172  xx = -x_offset
173  saved = 0.0_wp
174  do r = 0, j - 1
175  xx = xx + 1.0_wp
176  temp = bspl(r)*inv(j)
177  bspl(r) = saved + xx*temp
178  saved = (j_real - xx)*temp
179  end do
180  bspl(j) = saved
181  end do
182 
183  end associate ! bspl, spline_degree
184 
185  end subroutine s_bsplines_uniform__eval_basis
186 
187  !-----------------------------------------------------------------------------
195  !-----------------------------------------------------------------------------
196  sll_pure subroutine s_bsplines_uniform__eval_deriv(self, x, derivs, jmin)
197  class(sll_t_bsplines_uniform), intent(in) :: self
198  real(wp), intent(in) :: x
199  real(wp), intent(out) :: derivs(0:)
200  integer, intent(out) :: jmin
201 
202  integer :: icell
203  real(wp) :: x_offset
204 
205  integer :: j, r
206  real(wp) :: j_real
207  real(wp) :: xx
208  real(wp) :: temp
209  real(wp) :: saved
210 
211  real(wp) :: bj, bjm1
212 
213  ! Check on inputs
214  sll_assert(size(derivs) == 1 + self%degree)
215 
216  ! 1. Compute cell index 'icell' and x_offset
217  call s_bsplines_uniform__get_icell_and_offset(self, x, icell, x_offset)
218 
219  ! 2. Compute index range of B-splines with support over cell 'icell'
220  jmin = icell
221 
222  ! 3. Compute derivatives of aforementioned B-splines
223  ! Derivatives are normalized, hence they should be divided by dx
224  associate(bspl => derivs, spline_degree => self%degree, start => self%inv_dx)
225 
226  ! only need splines of lower degree to compute derivatives
227  bspl(0) = start
228  do j = 1, spline_degree - 1
229  j_real = real(j, wp)
230  xx = -x_offset
231  saved = 0.0_wp
232  do r = 0, j - 1
233  xx = xx + 1.0_wp
234  temp = bspl(r)*inv(j)
235  bspl(r) = saved + xx*temp
236  saved = (j_real - xx)*temp
237  end do
238  bspl(j) = saved
239  end do
240 
241  ! compute derivatives
242  bjm1 = bspl(0)
243  bj = bjm1
244  bspl(0) = -bjm1
245  do j = 1, spline_degree - 1
246  bj = bspl(j)
247  bspl(j) = bjm1 - bj
248  bjm1 = bj
249  end do
250  bspl(spline_degree) = bj
251 
252  end associate ! bspl, spline_degree
253 
254  end subroutine s_bsplines_uniform__eval_deriv
255 
256  !-----------------------------------------------------------------------------
265  !-----------------------------------------------------------------------------
266  sll_pure subroutine s_bsplines_uniform__eval_basis_and_n_derivs(self, x, n, derivs, jmin)
267  class(sll_t_bsplines_uniform), intent(in) :: self
268  real(wp), intent(in) :: x
269  integer, intent(in) :: n
270  real(wp), intent(out) :: derivs(0:, 0:)
271  integer, intent(out) :: jmin
272 
273  integer :: icell
274  real(wp) :: x_offset
275 
276  integer :: j, r
277  real(wp) :: j_real
278  real(wp) :: xx
279  real(wp) :: temp
280  real(wp) :: saved
281 
282  integer :: k, s1, s2, rk, pk, j1, j2
283  real(wp) :: d
284 
285  ! GFortran: to allocate on stack use -fstack-arrays
286  real(wp) :: ndu(0:self%degree, 0:self%degree)
287  real(wp) :: a(0:1, 0:self%degree)
288 
289  ! Check on inputs
290  sll_assert(n >= 0)
291  sll_assert(size(derivs, 1) == 1 + n)
292  sll_assert(size(derivs, 2) == 1 + self%degree)
293 
294  ! 1. Compute cell index 'icell' and x_offset
295  call s_bsplines_uniform__get_icell_and_offset(self, x, icell, x_offset)
296 
297  ! 2. Compute index range of B-splines with support over cell 'icell'
298  jmin = icell
299 
300  ! 3. Recursively evaluate B-splines (see "sll_s_uniform_bsplines_eval_basis")
301  ! up to self%degree, and store them all in the upper-right triangle of ndu
302  associate(spline_degree => self%degree, bspl => derivs)
303 
304  ndu(0, 0) = 1.0_wp
305  do j = 1, spline_degree
306  j_real = real(j, wp)
307  xx = -x_offset
308  saved = 0.0_wp
309  do r = 0, j - 1
310  xx = xx + 1.0_wp
311  temp = ndu(r, j - 1)*inv(j)
312  ndu(r, j) = saved + xx*temp
313  saved = (j_real - xx)*temp
314  end do
315  ndu(j, j) = saved
316  end do
317  bspl(0, :) = ndu(:, spline_degree)
318 
319  end associate ! spline_degree, bspl
320 
321  ! 4. Use equation 2.10 in "The NURBS Book" to compute n derivatives
322  associate(deg => self%degree, bsdx => derivs)
323 
324  do r = 0, deg
325  s1 = 0
326  s2 = 1
327  a(0, 0) = 1.0_wp
328  do k = 1, n
329  d = 0.0_wp
330  rk = r - k
331  pk = deg - k
332  if (r >= k) then
333  a(s2, 0) = a(s1, 0)*inv(pk + 1)
334  d = a(s2, 0)*ndu(rk, pk)
335  end if
336  if (rk > -1) then
337  j1 = 1
338  else
339  j1 = -rk
340  end if
341  if (r - 1 <= pk) then
342  j2 = k - 1
343  else
344  j2 = deg - r
345  end if
346  do j = j1, j2
347  a(s2, j) = (a(s1, j) - a(s1, j - 1))*inv(pk + 1)
348  d = d + a(s2, j)*ndu(rk + j, pk)
349  end do
350  if (r <= pk) then
351  a(s2, k) = -a(s1, k - 1)*inv(pk + 1)
352  d = d + a(s2, k)*ndu(r, pk)
353  end if
354  bsdx(k, r) = d
355  j = s1
356  s1 = s2
357  s2 = j
358  end do
359  end do
360 
361  ! Multiply result by correct factors:
362  ! deg!/(deg-n)! = deg*(deg-1)*...*(deg-n+1)
363  ! k-th derivatives are normalized, hence they should be divided by dx^k
364  d = real(deg, wp)*self%inv_dx
365  do k = 1, n
366  bsdx(k, :) = bsdx(k, :)*d
367  d = d*real(deg - k, wp)*self%inv_dx
368  end do
369 
370  end associate ! deg, bsdx
371 
372  end subroutine s_bsplines_uniform__eval_basis_and_n_derivs
373 
374 end module sll_m_bsplines_uniform
Abstract class for B-splines of arbitrary degree.
Derived type for uniform B-splines of arbitrary degree.
real(wp), dimension(1:32), parameter inv
subroutine s_bsplines_uniform__free(self)
Free storage.
integer, parameter wp
Working precision.
subroutine s_bsplines_uniform__init(self, degree, periodic, xmin, xmax, ncells)
Initialize uniform B-splines object.
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