Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_low_level_bsplines.F90
Go to the documentation of this file.
1 !**************************************************************
2 ! Copyright INRIA
3 ! Authors :
4 ! CALVI project team
5 !
6 ! This code SeLaLib (for Semi-Lagrangian-Library)
7 ! is a parallel library for simulating the plasma turbulence
8 ! in a tokamak.
9 !
10 ! This software is governed by the CeCILL-B license
11 ! under French law and abiding by the rules of distribution
12 ! of free software. You can use, modify and redistribute
13 ! the software under the terms of the CeCILL-B license as
14 ! circulated by CEA, CNRS and INRIA at the following URL
15 ! "http://www.cecill.info".
16 !**************************************************************
17 
26 
28 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
29 #include "sll_assert.h"
30 #include "sll_errors.h"
31 
32  use sll_m_working_precision, only: &
33  f64
34 
36  sll_p_periodic, &
37  sll_p_open, &
38  sll_p_mirror
39 
40  implicit none
41 
42  public :: &
47  sll_s_bsplines_eval_basis, &
48  sll_s_bsplines_eval_deriv, &
49  sll_s_bsplines_eval_basis_and_deriv, &
50  sll_s_bsplines_eval_basis_and_n_derivs, &
51  ! Binary search algorithm (should be in 'meshes' or 'low_level_utilities'):
53  ! Michel Mehrenberger's algorithms (slightly faster but not fully robust):
54  sll_s_bsplines_eval_basis_mm, &
55  sll_s_bsplines_eval_basis_and_deriv_mm, &
56  ! Evaluation of uniform spline basis (faster, should go to separate module):
57  sll_s_uniform_bsplines_eval_basis, &
58  sll_s_uniform_bsplines_eval_deriv, &
59  sll_s_uniform_bsplines_eval_basis_and_deriv, &
60  sll_s_uniform_bsplines_eval_basis_and_n_derivs, &
61  ! Evaluation of uniform periodic spline (use 'sll_m_spline_1d' instead):
62  sll_s_eval_uniform_periodic_spline_curve, &
63  sll_s_eval_uniform_periodic_spline_curve_with_zero
64 
65  private
66 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
67 
69  integer, parameter :: wp = f64
70 
72  integer, parameter :: allowed_bcs(1:3) = [sll_p_periodic, sll_p_open, sll_p_mirror]
73 
76  integer :: num_pts
77  integer :: deg
78  real(wp) :: xmin
79  real(wp) :: xmax
80  integer :: n ! dimension of spline space
81  real(wp), allocatable :: knots(:) ! knots array
82  end type sll_t_bsplines
83 
84 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
85 contains
86 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
87 
88  !-----------------------------------------------------------------------------
144  !-----------------------------------------------------------------------------
146  basis, &
147  degree, &
148  grid, &
149  bc_xmin, &
150  bc_xmax)
151 
152  type(sll_t_bsplines), intent(out) :: basis
153  integer, intent(in) :: degree
154  real(wp), intent(in) :: grid(:)
155  integer, intent(in) :: bc_xmin
156  integer, intent(in) :: bc_xmax
157 
158  character(len=*), parameter :: this_sub_name = "sll_s_bsplines_init_from_grid"
159  character(len=256) :: err_msg
160 
161  integer :: i
162  integer :: num_pts
163  real(wp) :: period ! length of period
164  real(wp) :: min_cell_width
165 
166  ! Check that polynomial degree is at least 1
167  if (degree < 1) then
168  write (err_msg, "('Minimum degree = 1, given ',i0,' instead.')") degree
169  sll_error(this_sub_name, trim(err_msg))
170  end if
171 
172  ! Check that grid contains at least two points
173  num_pts = size(grid)
174  if (num_pts < 2) then
175  write (err_msg, "('Minimum size(grid) = 2, given ',i0,' instead.')") num_pts
176  sll_error(this_sub_name, trim(err_msg))
177  end if
178 
179  ! Check whether grid points are in strictly increasing order
180  min_cell_width = grid(2) - grid(1)
181  do i = 3, num_pts
182  min_cell_width = min(min_cell_width, grid(i) - grid(i - 1))
183  end do
184  if (min_cell_width <= 0.0_wp) then
185  err_msg = "Grid points must be in strictly increasing order."
186  sll_error(this_sub_name, trim(err_msg))
187  else if (min_cell_width < 1.e-10_wp) then
188  err_msg = "Length of grid cells must be >= 1e-10."
189  sll_error(this_sub_name, trim(err_msg))
190  end if
191 
192  ! Check that boundary conditions are OK
193  if (.not. any(bc_xmin == allowed_bcs)) then
194  err_msg = "Unrecognized boundary condition at xmin: "// &
195  "possible values are [sll_p_periodic, sll_p_open, sll_p_mirror]."
196  sll_error(this_sub_name, trim(err_msg))
197  end if
198  if (.not. any(bc_xmax == allowed_bcs)) then
199  err_msg = "Unrecognized boundary condition at xmax: "// &
200  "possible values are [sll_p_periodic, sll_p_open, sll_p_mirror]."
201  sll_error(this_sub_name, trim(err_msg))
202  end if
203  if (any([bc_xmin, bc_xmax] == sll_p_periodic) .and. bc_xmin /= bc_xmax) then
204  err_msg = "Periodic boundary conditions mismatch: bc_xmin /= bc_xmax."
205  sll_error(this_sub_name, trim(err_msg))
206  end if
207 
208  ! Periodic case: check that there are enough grid points for a given degree
209  if (any([bc_xmin, bc_xmax] == sll_p_periodic) .and. num_pts < 2 + degree) then
210  err_msg = "Insufficient number of grid points for periodic spline: "// &
211  "condition num_pts >= 2+degree not satisfied."
212  sll_error(this_sub_name, trim(err_msg))
213  end if
214 
215  basis%num_pts = num_pts
216  basis%deg = degree
217  basis%xmin = grid(1)
218  basis%xmax = grid(num_pts)
219 
220  ! 'period' is useful in the case of periodic boundary conditions.
221  period = grid(num_pts) - grid(1)
222 
223  ! Create the knots array from the grid points. Here take the grid points
224  ! as knots and simply add to the left and the right the
225  ! amount of knots that depends on the degree of the requested
226  ! spline. We aim at setting up the indexing in such a way that the
227  ! original indexing of 'grid' is preserved, i.e.: grid(i) = knot(i), at
228  ! least whenever the scope of the indices defined here is active.
229  allocate (basis%knots(1 - degree:num_pts + degree))
230  do i = 1, num_pts
231  basis%knots(i) = grid(i)
232  end do
233 
234  ! Allocate array for spline coefficients
235  if (bc_xmin == sll_p_periodic) then
236  basis%n = num_pts - 1 ! dimension of periodic spline space
237  else
238  basis%n = num_pts + degree - 1 ! dimension of non periodic spline space
239  end if
240 
241  ! Fill out the extra points at both ends of the local knot array with
242  ! values proper to the boundary condition requested.
243  !
244  ! Fill out the extra nodes on the left
245  select case (bc_xmin)
246  case (sll_p_periodic)
247  do i = 1, degree
248  basis%knots(1 - i) = grid(num_pts - i) - period
249  end do
250  case (sll_p_open)
251  do i = 1, degree
252  basis%knots(1 - i) = grid(1)
253  end do
254  case (sll_p_mirror)
255  do i = 1, degree
256  basis%knots(1 - i) = 2*grid(1) - grid(1 + i)
257  end do
258  end select
259  !
260  ! Fill out the extra nodes on the right
261  select case (bc_xmax)
262  case (sll_p_periodic)
263  do i = 1, degree
264  basis%knots(num_pts + i) = grid(i + 1) + period
265  end do
266  case (sll_p_open)
267  do i = 1, degree
268  basis%knots(num_pts + i) = grid(num_pts)
269  end do
270  case (sll_p_mirror)
271  do i = 1, degree
272  basis%knots(num_pts + i) = 2*grid(num_pts) - grid(num_pts - i)
273  end do
274  end select
275 
276  end subroutine sll_s_bsplines_init_from_grid
277 
278  !-----------------------------------------------------------------------------
288  !-----------------------------------------------------------------------------
289  subroutine sll_s_bsplines_init_from_knots(basis, degree, n, knots)
290 
291  type(sll_t_bsplines), intent(out) :: basis
292  integer, intent(in) :: degree
293  integer, intent(in) :: n
294  real(wp), intent(in) :: knots(:)
295 
296  integer :: i
297  real(wp) :: knotmin
298 
299  ! Error checking
300  sll_assert(size(knots) == n + 2*degree)
301  if (degree < 1) then
302  print *, 'ERROR. sll_s_bsplines_init_from_knots: ', &
303  'only strictly positive integer values for degree are allowed, ', &
304  'given: ', degree
305  end if
306  ! Check whether grid points are in strictly increasing order
307  knotmin = knots(2) - knots(1)
308  do i = 2, n + 2*degree
309  knotmin = min(knotmin, knots(i) - knots(i - 1))
310  end do
311  if (knotmin < 0.0_wp) then
312  print *, 'ERROR. sll_s_bsplines_init_from_knots: ', &
313  ' we require that knots are in non decreasing.'
314  end if
315 
316  ! Initialise variables
317  basis%n = n
318  basis%deg = degree
319  basis%num_pts = n - degree + 1
320  basis%xmin = knots(degree + 1)
321  basis%xmax = knots(n + 1)
322 
323  ! We aim at setting up the indexing in such a way that the
324  ! the computation grid is [knot(1),knots(num_pts)], at
325  ! least whenever the scope of the indices defined here is active.
326  allocate (basis%knots(1 - degree:n + 1))
327  basis%knots(1 - degree:n + 1) = knots
328 
329  end subroutine sll_s_bsplines_init_from_knots
330 
331  !-----------------------------------------------------------------------------
338  !-----------------------------------------------------------------------------
339  pure function sll_f_find_cell(basis, x) result(icell)
340 
341  type(sll_t_bsplines), intent(in) :: basis
342  real(wp), intent(in) :: x
343  integer :: icell
344 
345  integer :: low
346  integer :: high
347  integer :: n
348 
349  n = basis%num_pts
350 
351  ! check if point is outside of grid
352  if (x > basis%knots(n)) then
353  icell = -1
354  return
355  end if
356  if (x < basis%knots(1)) then
357  icell = -1
358  return
359  end if
360 
361  ! check if point is exactly on left boundary
362  if (x == basis%knots(1)) then
363  icell = 1
364  return
365  end if
366 
367  ! check if point is exactly on right boundary
368  if (x == basis%knots(n)) then
369  icell = n - 1
370  return
371  end if
372 
373  low = 1
374  high = n
375  icell = (low + high)/2
376  do while (x < basis%knots(icell) &
377  .or. x >= basis%knots(icell + 1))
378  if (x < basis%knots(icell)) then
379  high = icell
380  else
381  low = icell
382  end if
383  icell = (low + high)/2
384  end do
385 
386  end function sll_f_find_cell
387 
388  !-----------------------------------------------------------------------------
389  ! sll_f_splines_at_x returns the values of all the B-splines of a given
390  ! degree that have support in cell 'cell' and evaluated at the point 'x'.
391  ! In other words, if B[j,i](x) is the spline of degree 'j' whose leftmost
392  ! support is at cell 'i' and evaluated at 'x', then sll_f_splines_at_x returns
393  ! the sequence (in the form of an array):
394  !
395  ! B[j,i-degree](x), B[j,i-degree+1](x), B[j,i-degree+2](x), ..., B[j,i](x)
396  !
397  ! Implementation notes:
398  !
399  ! The algorithm is based on Algorithm A2.2 from
400  ! (L. Piegl, W. Tiller, The NURBS book p. 70)
401  ! A variant of the same Algorithm is implemented in the de Boor splines.
402  ! It is known as the Cox - de Boor algorithm
403 
416  !-----------------------------------------------------------------------------
417  sll_pure subroutine sll_s_bsplines_eval_basis(basis, icell, x, splines_at_x)
418 
419  type(sll_t_bsplines), intent(in) :: basis
420  integer, intent(in) :: icell
421  real(wp), intent(in) :: x
422  real(wp), intent(out) :: splines_at_x(0:basis%deg)
423 
424  real(wp) :: saved
425  real(wp) :: temp
426  integer :: j
427  integer :: r
428 
429  ! GFortran: to allocate on stack use -fstack-arrays
430  real(wp) :: left(1:basis%deg)
431  real(wp) :: right(1:basis%deg)
432 
433  ! Run some checks on the arguments.
434  sll_assert(x > basis%xmin - 1.0d-14)
435  sll_assert(x < basis%xmax + 1.0d-14)
436  sll_assert(icell >= 1)
437  sll_assert(icell <= basis%num_pts - 1)
438  sll_assert(basis%knots(icell) <= x .and. x <= basis%knots(icell + 1))
439 
440  splines_at_x(0) = 1.0_wp
441  do j = 1, basis%deg
442  left(j) = x - basis%knots(icell + 1 - j)
443  right(j) = basis%knots(icell + j) - x
444  saved = 0.0_wp
445  do r = 0, j - 1
446  temp = splines_at_x(r)/(right(r + 1) + left(j - r))
447  splines_at_x(r) = saved + right(r + 1)*temp
448  saved = left(j - r)*temp
449  end do
450  splines_at_x(j) = saved
451  end do
452 
453  end subroutine sll_s_bsplines_eval_basis
454 
455  !-----------------------------------------------------------------------------
469  !-----------------------------------------------------------------------------
470  sll_pure subroutine sll_s_bsplines_eval_deriv(basis, icell, x, bsdx)
471 
472  type(sll_t_bsplines), intent(in) :: basis
473  integer, intent(in) :: icell
474  real(wp), intent(in) :: x
475  real(wp), intent(out) :: bsdx(0:basis%deg)
476 
477  integer :: deg
478  integer :: num_pts
479  integer :: r
480  integer :: j
481  real(wp) :: saved
482  real(wp) :: temp
483  real(wp) :: rdeg
484 
485  ! GFortran: to allocate on stack use -fstack-arrays
486  real(wp) :: left(1:basis%deg)
487  real(wp) :: right(1:basis%deg)
488 
489  ! Run some checks on the arguments.
490  sll_assert(allocated(basis%knots))
491  sll_assert(x > basis%xmin - 1.0d-14)
492  sll_assert(x < basis%xmax + 1.0d-14)
493  sll_assert(icell >= 1)
494  sll_assert(icell <= basis%num_pts - 1)
495  sll_assert(basis%knots(icell) <= x .and. x <= basis%knots(icell + 1))
496 
497  deg = basis%deg
498  rdeg = real(deg, wp)
499  num_pts = basis%num_pts
500 
501  ! compute nonzero basis functions and knot differences
502  ! for splines up to degree deg-1 which are needed to compute derivative
503  ! First part of Algorithm A3.2 of NURBS book
504  bsdx(0) = 1.0_wp
505  do j = 1, deg - 1
506  left(j) = x - basis%knots(icell + 1 - j)
507  right(j) = basis%knots(icell + j) - x
508  saved = 0.0_wp
509  do r = 0, j - 1
510  ! compute and save bspline values
511  temp = bsdx(r)/(right(r + 1) + left(j - r))
512  bsdx(r) = saved + right(r + 1)*temp
513  saved = left(j - r)*temp
514  end do
515  bsdx(j) = saved
516  end do
517 
518  ! Compute derivatives at x using values stored in bsdx and formula
519  ! formula for spline derivative based on difference of splines of
520  ! degree deg-1
521  ! -------
522  ! j = 0
523  saved = rdeg*bsdx(0)/(basis%knots(icell + 1) - basis%knots(icell + 1 - deg))
524  bsdx(0) = -saved
525  do j = 1, deg - 1
526  temp = saved
527  saved = rdeg*bsdx(j)/(basis%knots(icell + j + 1) - basis%knots(icell + j + 1 - deg))
528  bsdx(j) = temp - saved
529  end do
530  ! j = deg
531  bsdx(deg) = saved
532 
533  end subroutine sll_s_bsplines_eval_deriv
534 
535  !-----------------------------------------------------------------------------
541  !-----------------------------------------------------------------------------
542  sll_pure subroutine sll_s_bsplines_eval_basis_and_deriv(basis, icell, x, bsdx)
543 
544  type(sll_t_bsplines), intent(in) :: basis
545  integer, intent(in) :: icell
546  real(wp), intent(in) :: x
547  real(wp), intent(out) :: bsdx(2, 0:basis%deg)
548 
549  integer :: deg
550  integer :: num_pts
551  integer :: r
552  integer :: j
553  real(wp) :: saved
554  real(wp) :: temp
555  real(wp) :: rdeg
556 
557  ! GFortran: to allocate on stack use -fstack-arrays
558  real(wp) :: left(1:basis%deg)
559  real(wp) :: right(1:basis%deg)
560 
561  ! Run some checks on the arguments.
562  sll_assert(allocated(basis%knots))
563  sll_assert(x > basis%xmin - 1.0d-14)
564  sll_assert(x < basis%xmax + 1.0d-14)
565  sll_assert(icell >= 1)
566  sll_assert(icell <= basis%num_pts - 1)
567  sll_assert(basis%knots(icell) <= x .and. x <= basis%knots(icell + 1))
568 
569  deg = basis%deg
570  num_pts = basis%num_pts
571  rdeg = real(deg, wp)
572 
573  ! compute nonzero basis functions and knot differences
574  ! for splines up to degree deg-1 which are needed to compute derivative
575  ! First part of Algorithm A2.3 of NURBS book
576  bsdx(1, 0) = 1.0_wp
577  do j = 1, deg - 1
578  left(j) = x - basis%knots(icell + 1 - j)
579  right(j) = basis%knots(icell + j) - x
580  saved = 0.0_wp
581  do r = 0, j - 1
582  ! compute and save knot differences
583  temp = bsdx(1, r)/(right(r + 1) + left(j - r))
584  bsdx(1, r) = saved + right(r + 1)*temp
585  saved = left(j - r)*temp
586  end do
587  bsdx(1, j) = saved
588  end do
589 
590  ! Compute derivatives at x using values stored in bsdx and formula
591  ! formula for spline derivative based on difference of splines of
592  ! degree deg-1
593  ! -------
594  ! j = 0
595  saved = rdeg*bsdx(1, 0)/ &
596  (basis%knots(icell + 1) - basis%knots(icell + 1 - deg))
597  bsdx(2, 0) = -saved
598  do j = 1, deg - 1
599  temp = saved
600  saved = rdeg*bsdx(1, j)/ &
601  (basis%knots(icell + j + 1) - basis%knots(icell + j + 1 - deg))
602  bsdx(2, j) = temp - saved
603  end do
604  ! j = deg
605  bsdx(2, deg) = saved
606 
607  ! Compute values of splines of degree deg
608  !----------------------------------------
609  j = deg
610  left(j) = x - basis%knots(icell + 1 - j)
611  right(j) = basis%knots(icell + j) - x
612  saved = 0.0_wp
613  do r = 0, j - 1
614  ! compute and save knot differences
615  temp = bsdx(1, r)/(right(r + 1) + left(j - r))
616  bsdx(1, r) = saved + right(r + 1)*temp
617  saved = left(j - r)*temp
618  end do
619  bsdx(1, j) = saved
620 
621  end subroutine sll_s_bsplines_eval_basis_and_deriv
622 
623  !-----------------------------------------------------------------------------
633  !
634  ! TODO: transpose output array 'bsdx'
635  !-----------------------------------------------------------------------------
636  sll_pure subroutine sll_s_bsplines_eval_basis_and_n_derivs(basis, icell, x, n, bsdx)
637 
638  type(sll_t_bsplines), intent(in) :: basis
639  integer, intent(in) :: icell
640  real(wp), intent(in) :: x
641  integer, intent(in) :: n
642  real(wp), intent(out) :: bsdx(0:n, 0:basis%deg)
643 
644  integer :: deg
645  integer :: num_pts
646  integer :: r
647  integer :: k
648  integer :: j
649  integer :: j1
650  integer :: j2
651  integer :: s1
652  integer :: s2
653  integer :: rk
654  integer :: pk
655  real(wp) :: saved
656  real(wp) :: temp
657  real(wp) :: rdeg
658  real(wp) :: d
659 
660  ! GFortran: to allocate on stack use -fstack-arrays
661  real(wp) :: left(1:basis%deg)
662  real(wp) :: right(1:basis%deg)
663  real(wp) :: ndu(0:basis%deg, 0:basis%deg)
664  real(wp) :: a(0:1, 0:basis%deg)
665 
666  ! Run some checks on the arguments.
667  sll_assert(allocated(basis%knots))
668  sll_assert(x > basis%xmin - 1.0d-14)
669  sll_assert(x < basis%xmax + 1.0d-14)
670  sll_assert(icell >= 1)
671  sll_assert(icell <= basis%num_pts - 1)
672  sll_assert(n >= 0)
673  sll_assert(n <= basis%deg)
674  sll_assert(basis%knots(icell) <= x .and. x <= basis%knots(icell + 1))
675 
676  deg = basis%deg
677  num_pts = basis%num_pts
678  rdeg = real(deg, wp)
679 
680  ! compute nonzero basis functions and knot differences
681  ! for splines up to degree deg-1 which are needed to compute derivative
682  ! Algorithm A2.3 of NURBS book
683  !
684  ! 21.08.2017: save inverse of knot differences to avoid unnecessary divisions
685  ! [Yaman Güçlü, Edoardo Zoni]
686 
687  ndu(0, 0) = 1.0_wp
688  do j = 1, deg
689  left(j) = x - basis%knots(icell + 1 - j)
690  right(j) = basis%knots(icell + j) - x
691  saved = 0.0_wp
692  do r = 0, j - 1
693  ! compute inverse of knot differences and save them into lower triangular part of ndu
694  ndu(j, r) = 1.0_wp/(right(r + 1) + left(j - r))
695  ! compute basis functions and save them into upper triangular part of ndu
696  temp = ndu(r, j - 1)*ndu(j, r)
697  ndu(r, j) = saved + right(r + 1)*temp
698  saved = left(j - r)*temp
699  end do
700  ndu(j, j) = saved
701  end do
702  bsdx(0, :) = ndu(:, deg)
703 
704  do r = 0, deg
705  s1 = 0
706  s2 = 1
707  a(0, 0) = 1.0_wp
708  do k = 1, n
709  d = 0.0_wp
710  rk = r - k
711  pk = deg - k
712  if (r >= k) then
713  a(s2, 0) = a(s1, 0)*ndu(pk + 1, rk)
714  d = a(s2, 0)*ndu(rk, pk)
715  end if
716  if (rk > -1) then
717  j1 = 1
718  else
719  j1 = -rk
720  end if
721  if (r - 1 <= pk) then
722  j2 = k - 1
723  else
724  j2 = deg - r
725  end if
726  do j = j1, j2
727  a(s2, j) = (a(s1, j) - a(s1, j - 1))*ndu(pk + 1, rk + j)
728  d = d + a(s2, j)*ndu(rk + j, pk)
729  end do
730  if (r <= pk) then
731  a(s2, k) = -a(s1, k - 1)*ndu(pk + 1, r)
732  d = d + a(s2, k)*ndu(r, pk)
733  end if
734  bsdx(k, r) = d
735  j = s1
736  s1 = s2
737  s2 = j
738  end do
739  end do
740  r = deg
741  do k = 1, n
742  bsdx(k, :) = bsdx(k, :)*real(r, f64)
743  r = r*(deg - k)
744  end do
745 
746  end subroutine sll_s_bsplines_eval_basis_and_n_derivs
747 
748  !-----------------------------------------------------------------------------
756  !-----------------------------------------------------------------------------
757  sll_pure subroutine sll_s_bsplines_eval_basis_mm( &
758  knots, &
759  cell, &
760  x, &
761  degree, &
762  out)
763 
764  integer, intent(in) :: degree
765  real(wp), intent(in) :: knots(1 - degree:)
766  integer, intent(in) :: cell
767  real(wp), intent(in) :: x
768  real(wp), intent(out) :: out(:)
769 
770  real(wp) :: tmp1
771  real(wp) :: tmp2
772  integer :: ell
773  integer :: k
774 
775  out(1) = 1._wp
776  do ell = 1, degree
777  tmp1 = (x - knots(cell + 1 - ell))/(knots(cell + 1) - knots(cell + 1 - ell))*out(1)
778  out(1) = out(1) - tmp1
779  do k = 2, ell
780  tmp2 = (x - knots(cell + k - ell))/(knots(cell + k) - knots(cell + k - ell))*out(k)
781  out(k) = out(k) + tmp1 - tmp2
782  tmp1 = tmp2
783  end do
784  out(ell + 1) = tmp1
785  end do
786 
787  end subroutine sll_s_bsplines_eval_basis_mm
788 
789  !-----------------------------------------------------------------------------
797  !-----------------------------------------------------------------------------
798  sll_pure subroutine sll_s_bsplines_eval_basis_and_deriv_mm( &
799  knots, &
800  cell, &
801  x, &
802  degree, &
803  out)
804 
805  integer, intent(in) :: degree
806  real(wp), intent(in) :: knots(1 - degree:)
807  integer, intent(in) :: cell
808  real(wp), intent(in) :: x
809  real(wp), intent(out) :: out(:, :)
810 
811  real(wp) :: tmp1
812  real(wp) :: tmp2
813  integer :: ell
814  integer :: k
815 
816  out(1, 1) = 1._wp
817  do ell = 1, degree
818  tmp1 = (x - knots(cell + 1 - ell))/(knots(cell + 1) - knots(cell + 1 - ell))*out(1, 1)
819  out(1, 1) = out(1, 1) - tmp1
820  do k = 2, ell
821  tmp2 = (x - knots(cell + k - ell))/(knots(cell + k) - knots(cell + k - ell))*out(1, k)
822  out(1, k) = out(1, k) + tmp1 - tmp2
823  tmp1 = tmp2
824  end do
825  out(2, ell + 1) = tmp1
826  if (ell == degree - 1) then
827  !compute the derivatives
828  tmp1 = real(degree, wp)/(knots(cell + 1) - knots(cell + 1 - degree))*out(1, 1)
829  out(2, 1) = -tmp1
830  do k = 2, degree
831  out(2, k) = tmp1
832  tmp1 = real(degree, wp)/(knots(cell + k) - knots(cell + k - degree))*out(2, k)
833  out(2, k) = out(2, k) - tmp1
834  end do
835  out(2, degree + 1) = tmp1
836  end if
837  end do
838 
839  end subroutine sll_s_bsplines_eval_basis_and_deriv_mm
840 
841  !-----------------------------------------------------------------------------
842  subroutine sll_s_bsplines_free(spline)
843 
844  type(sll_t_bsplines), intent(inout) :: spline
845 
846  character(len=*), parameter :: this_sub_name = "sll_s_bsplines_free()"
847 
848  if (.not. allocated(spline%knots)) then
849  sll_error(this_sub_name, 'knots array is not allocated.')
850  end if
851  deallocate (spline%knots)
852 
853  end subroutine sll_s_bsplines_free
854 
855  ! *************************************************************************
856  !
857  ! UNIFORM B-SPLINE FUNCTIONS
858  !
859  ! *************************************************************************
860 
861  !-----------------------------------------------------------------------------
879  !-----------------------------------------------------------------------------
880  sll_pure subroutine sll_s_uniform_bsplines_eval_basis( &
881  spline_degree, &
882  normalized_offset, &
883  bspl)
884 
885  integer, intent(in) :: spline_degree
886  real(wp), intent(in) :: normalized_offset
887  real(wp), intent(out) :: bspl(0:spline_degree)
888 
889  integer :: j, r
890  real(wp) :: inv_j
891  real(wp) :: j_real
892  real(wp) :: xx
893  real(wp) :: temp
894  real(wp) :: saved
895 
896  sll_assert(spline_degree >= 0)
897  sll_assert(normalized_offset >= 0.0_wp)
898  sll_assert(normalized_offset <= 1.0_wp)
899 
900  bspl(0) = 1.0_wp
901  do j = 1, spline_degree
902  xx = -normalized_offset
903  j_real = real(j, wp)
904  inv_j = 1.0_wp/j_real
905  saved = 0.0_wp
906  do r = 0, j - 1
907  xx = xx + 1.0_wp
908  temp = bspl(r)*inv_j
909  bspl(r) = saved + xx*temp
910  saved = (j_real - xx)*temp
911  end do
912  bspl(j) = saved
913  end do
914 
915  end subroutine sll_s_uniform_bsplines_eval_basis
916 
917  !-----------------------------------------------------------------------------
926  !-----------------------------------------------------------------------------
927  sll_pure subroutine sll_s_uniform_bsplines_eval_deriv( &
928  spline_degree, &
929  normalized_offset, &
930  bspl)
931 
932  integer, intent(in) :: spline_degree
933  real(wp), intent(in) :: normalized_offset
934  real(wp), intent(out) :: bspl(0:spline_degree)
935 
936  real(wp) :: inv_j
937  real(wp) :: x, xx
938  real(wp) :: j_real
939  integer :: j, r
940  real(wp) :: temp
941  real(wp) :: saved
942  real(wp) :: bj, bjm1
943 
944  sll_assert(spline_degree >= 0)
945  sll_assert(normalized_offset >= 0.0_wp)
946  sll_assert(normalized_offset <= 1.0_wp)
947 
948  x = normalized_offset
949  bspl(0) = 1.0_wp
950 
951  ! only need splines of lower degree to compute derivatives
952  do j = 1, spline_degree - 1
953  saved = 0.0_wp
954  j_real = real(j, wp)
955  inv_j = 1.0_wp/j_real
956  xx = -x
957  do r = 0, j - 1
958  xx = xx + 1.0_wp
959  temp = bspl(r)*inv_j
960  bspl(r) = saved + xx*temp
961  saved = (j_real - xx)*temp
962  end do
963  bspl(j) = saved
964  end do
965 
966  ! compute derivatives
967  bjm1 = bspl(0)
968  bj = bjm1
969  bspl(0) = -bjm1
970  do j = 1, spline_degree - 1
971  bj = bspl(j)
972  bspl(j) = bjm1 - bj
973  bjm1 = bj
974  end do
975  bspl(spline_degree) = bj
976 
977  end subroutine sll_s_uniform_bsplines_eval_deriv
978 
979  !-----------------------------------------------------------------------------
988  !-----------------------------------------------------------------------------
989  sll_pure subroutine sll_s_uniform_bsplines_eval_basis_and_deriv( &
990  degree, &
991  normalized_offset, &
992  bspl)
993 
994  integer, intent(in) :: degree
995  real(wp), intent(in) :: normalized_offset
996  real(wp), intent(out) :: bspl(2, 0:degree)
997 
998  real(wp) :: inv_j
999  real(wp) :: xx
1000  real(wp) :: j_real
1001  integer :: j, r
1002  real(wp) :: temp
1003  real(wp) :: saved
1004 
1005  sll_assert(degree >= 0)
1006  sll_assert(normalized_offset >= 0.0_wp)
1007  sll_assert(normalized_offset <= 1.0_wp)
1008 
1009  ! compute splines up to degree spline_degree -1
1010  bspl(1, 0) = 1.0_wp
1011  do j = 1, degree - 1
1012  saved = 0.0_wp
1013  j_real = real(j, wp)
1014  inv_j = 1.0_wp/j_real
1015  xx = -normalized_offset
1016  do r = 0, j - 1
1017  xx = xx + 1.0_wp
1018  temp = bspl(1, r)*inv_j
1019  bspl(1, r) = saved + xx*temp
1020  saved = (j_real - xx)*temp
1021  end do
1022  bspl(1, j) = saved
1023  end do
1024 
1025  ! compute derivatives
1026  bspl(2, 0) = -bspl(1, 0)
1027  do j = 1, degree - 1
1028  bspl(2, j) = bspl(1, j - 1) - bspl(1, j)
1029  end do
1030  bspl(2, degree) = bspl(1, degree - 1)
1031 
1032  ! continue the Cox-De Boor algorithm to evaluate splines
1033  j = degree
1034  saved = 0.0_wp
1035  j_real = real(j, wp)
1036  inv_j = 1.0_wp/j_real
1037  xx = -normalized_offset
1038  do r = 0, j - 1
1039  xx = xx + 1.0_wp
1040  temp = bspl(1, r)*inv_j
1041  bspl(1, r) = saved + xx*temp
1042  saved = (j_real - xx)*temp
1043  end do
1044  bspl(1, j) = saved
1045 
1046  end subroutine sll_s_uniform_bsplines_eval_basis_and_deriv
1047 
1048  !-----------------------------------------------------------------------------
1049  ! TODO: transpose output array 'bspl'
1050  sll_pure subroutine sll_s_uniform_bsplines_eval_basis_and_n_derivs( &
1051  spline_degree, &
1052  normalized_offset, &
1053  n, &
1054  bspl)
1055 
1056  integer, intent(in) :: spline_degree
1057  real(wp), intent(in) :: normalized_offset
1058  integer, intent(in) :: n
1059  real(wp), intent(out) :: bspl(0:n, 0:spline_degree)
1060 
1061  integer :: j, r
1062  real(wp) :: j_real
1063  real(wp) :: xx
1064  real(wp) :: temp
1065  real(wp) :: saved
1066 
1067  integer :: k, s1, s2, rk, pk, j1, j2
1068  real(wp) :: d
1069 
1070  ! GFortran: to allocate on stack use -fstack-arrays
1071  real(wp) :: ndu(0:spline_degree, 0:spline_degree)
1072  real(wp) :: a(0:1, 0:spline_degree)
1073 
1074  ! Inverse of integers for later use (max spline degree = 32)
1075  real(wp), parameter :: inv_idx(1:32) = [(1.0_wp/real(j, wp), j=1, 32)]
1076 
1077  sll_assert(spline_degree >= 0)
1078  sll_assert(n >= 0)
1079  sll_assert(normalized_offset >= 0.0_wp)
1080  sll_assert(normalized_offset <= 1.0_wp)
1081 
1082  ! Evaluate all basis splines (see "sll_s_uniform_bsplines_eval_basis")
1083  ndu(0, 0) = 1.0_wp
1084  do j = 1, spline_degree
1085  xx = -normalized_offset
1086  j_real = real(j, wp)
1087  saved = 0.0_wp
1088  do r = 0, j - 1
1089  xx = xx + 1.0_wp
1090  temp = ndu(r, j - 1)*inv_idx(j)
1091  ndu(r, j) = saved + xx*temp
1092  saved = (j_real - xx)*temp
1093  end do
1094  ndu(j, j) = saved
1095  end do
1096  bspl(0, :) = ndu(:, spline_degree)
1097 
1098  ! Use equation 2.10 in "The NURBS Book" to compute n derivatives
1099  associate(deg => spline_degree, bsdx => bspl)
1100 
1101  do r = 0, deg
1102  s1 = 0
1103  s2 = 1
1104  a(0, 0) = 1.0_wp
1105  do k = 1, n
1106  d = 0.0_wp
1107  rk = r - k
1108  pk = deg - k
1109  if (r >= k) then
1110  a(s2, 0) = a(s1, 0)*inv_idx(pk + 1)
1111  d = a(s2, 0)*ndu(rk, pk)
1112  end if
1113  if (rk > -1) then
1114  j1 = 1
1115  else
1116  j1 = -rk
1117  end if
1118  if (r - 1 <= pk) then
1119  j2 = k - 1
1120  else
1121  j2 = deg - r
1122  end if
1123  do j = j1, j2
1124  a(s2, j) = (a(s1, j) - a(s1, j - 1))*inv_idx(pk + 1)
1125  d = d + a(s2, j)*ndu(rk + j, pk)
1126  end do
1127  if (r <= pk) then
1128  a(s2, k) = -a(s1, k - 1)*inv_idx(pk + 1)
1129  d = d + a(s2, k)*ndu(r, pk)
1130  end if
1131  bsdx(k, r) = d
1132  j = s1
1133  s1 = s2
1134  s2 = j
1135  end do
1136  end do
1137 
1138  ! Multiply result by correct factors:
1139  ! deg!/(deg-n)! = deg*(deg-1)*...*(deg-n+1)
1140  r = deg
1141  do k = 1, n
1142  bsdx(k, :) = bsdx(k, :)*real(r, f64)
1143  r = r*(deg - k)
1144  end do
1145 
1146  end associate
1147 
1148  end subroutine sll_s_uniform_bsplines_eval_basis_and_n_derivs
1149 
1150  !-----------------------------------------------------------------------------
1154  !-----------------------------------------------------------------------------
1155  sll_pure subroutine sll_s_eval_uniform_periodic_spline_curve(degree, scoef, sval)
1156 
1157  integer, intent(in) :: degree
1158  real(wp), intent(in) :: scoef(:)
1159  real(wp), intent(out) :: sval(:)
1160 
1161  real(wp) :: bspl(degree + 1)
1162  real(wp) :: val
1163  integer :: i, j, imj, n
1164 
1165  ! get bspline values at knots
1166  call sll_s_uniform_bsplines_eval_basis(degree, 0.0_wp, bspl)
1167  n = size(scoef)
1168  do i = 1, n
1169  val = 0.0_wp
1170  do j = 1, degree
1171  imj = mod(i - 1 - j + n, n) + 1
1172  val = val + bspl(j)*scoef(imj)
1173  !print*, i,j, imj, bspl(j),val
1174  end do
1175  sval(i) = val
1176  end do
1177 
1178  end subroutine sll_s_eval_uniform_periodic_spline_curve
1179 
1180  !Wrapper function to use sll_s_eval_uniform_periodic_spline_curve with zero as spline_degree
1181  sll_pure subroutine sll_s_eval_uniform_periodic_spline_curve_with_zero(degree, scoef, sval)
1182 
1183  integer, intent(in) :: degree
1184  real(wp), intent(in) :: scoef(:)
1185  real(wp), intent(out) :: sval(:)
1186 
1187  if (degree == 0) then
1188  sval = scoef
1189  else
1190  call sll_s_eval_uniform_periodic_spline_curve(degree, scoef, sval)
1191  end if
1192 
1193  end subroutine sll_s_eval_uniform_periodic_spline_curve_with_zero
1194 
1195 end module sll_m_low_level_bsplines
Low level arbitrary degree splines.
integer, dimension(1:3), parameter allowed_bcs
List of allowed boundary conditions.
integer, parameter wp
Working precision.
subroutine, public sll_s_bsplines_init_from_grid(basis, degree, grid, bc_xmin, bc_xmax)
Build new sll_t_bsplines object based on a on a grid of strictly increasing points including the last...
subroutine, public sll_s_bsplines_free(spline)
Evaluates B-spline values at a point x in a given cell.
subroutine, public sll_s_bsplines_init_from_knots(basis, degree, n, knots)
Build new sll_t_bsplines object.
pure integer function, public sll_f_find_cell(basis, x)
return index i of grid cell such that: basisknots(i) <= x <= basisknots(i+1).
Module to select the kind parameter.
integer, parameter, public f64
f64 is the kind type for 64-bit reals (double precision)
Information for evaluation of B-splines on non-uniform grid.
    Report Typos and Errors