Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_qn_solver_2d_polar_par.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 
218 
220 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
221 #include "sll_assert.h"
222 #include "sll_errors.h"
223 #include "sll_working_precision.h"
224 
225  use sll_m_constants, only: &
227 
229  sll_p_dirichlet, &
230  sll_p_neumann, &
231  sll_p_neumann_mode_0, &
232  sll_p_polar_origin
233 
234  use sll_m_collective, only: &
238 
239  use sll_m_fft, only: &
240  sll_t_fft, &
249 
250  use sll_m_remapper, only: &
251  sll_t_layout_2d, &
252  sll_o_compute_local_sizes, &
253  sll_o_get_layout_global_size_i, &
254  sll_o_get_layout_global_size_j, &
255  sll_o_local_to_global, &
256  sll_t_remap_plan_2d_real64, &
257  sll_o_new_remap_plan, &
258  sll_o_apply_remap_2d
259 
260  use sll_m_tridiagonal, only: &
263 
264  use sll_m_utilities, only: &
266 
267  implicit none
268 
269  public :: &
274 
275  private
276 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
277 
280 
281  real(f64) :: rmin
282  real(f64) :: rmax
283  integer(i32) :: nr
284  integer(i32) :: ntheta
285  integer(i32) :: bc(2)
286  real(f64) :: epsilon_0
287  real(f64), allocatable :: g(:)
288 
289  type(sll_t_fft) :: fw
290  type(sll_t_fft) :: bw
291  real(f64), pointer :: tmp(:)
292  integer(i32), allocatable :: k_list(:)
293  real(f64), allocatable :: z_r(:, :)
294  real(f64), pointer :: z_a(:, :)
295  real(f64), allocatable :: mat(:, :)
296  real(f64), allocatable :: cts(:)
297  integer(i32), allocatable :: ipiv(:)
298 
299  real(f64) :: bc_coeffs_rmin(2:3) ! needed for Neumann at r=r_min
300  real(f64) :: bc_coeffs_rmax(-2:-1) ! needed for Neumann at r=r_max
301  integer :: skip0 ! needed for full circle
302 
303  type(sll_t_layout_2d), pointer :: layout_r
304  type(sll_t_layout_2d), pointer :: layout_a
305  type(sll_t_remap_plan_2d_real64), pointer :: rmp_ra
306  type(sll_t_remap_plan_2d_real64), pointer :: rmp_ar
307 
309 
310 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
311 contains
312 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
313 
314  !=============================================================================
317  layout_r, &
318  layout_a, &
319  rmin, &
320  rmax, &
321  nr, &
322  ntheta, &
323  bc_rmin, &
324  bc_rmax, &
325  rho_m0, &
326  b_magn, &
327  lambda, &
328  use_zonal_flow, &
329  epsilon_0, &
330  rgrid)
331 
332  type(sll_t_qn_solver_2d_polar_par), intent(out) :: solver
333  type(sll_t_layout_2d), pointer :: layout_r
334  type(sll_t_layout_2d), pointer :: layout_a
335  real(f64), intent(in) :: rmin
336  real(f64), intent(in) :: rmax
337  integer(i32), intent(in) :: nr
338  integer(i32), intent(in) :: ntheta
339  integer(i32), intent(in) :: bc_rmin
340  integer(i32), intent(in) :: bc_rmax
341  real(f64), intent(in) :: rho_m0(:)
342  real(f64), intent(in) :: b_magn(:)
343  real(f64), optional, intent(in) :: lambda(:)
344  logical, optional, intent(in) :: use_zonal_flow
345  real(f64), optional, intent(in) :: epsilon_0
346  real(f64), target, optional, intent(in) :: rgrid(:)
347 
348  character(len=*), parameter :: this_sub_name = 'sll_s_qn_solver_2d_polar_par_init'
349 
350  real(f64) :: hp, hm
351  real(f64) :: inv_r
352  real(f64) :: d0_coeffs(3)
353  real(f64) :: d1_coeffs(3)
354  real(f64) :: d2_coeffs(3)
355  real(f64) :: ddr_ln_g
356  real(f64) :: c
357  real(f64), allocatable :: r_nodes(:)
358 
359  integer(i32) :: i, j, k
360  integer(i32) :: bck(2)
361  integer(i32) :: last
362  integer(i32) :: sh
363 
364  integer(i32) :: loc_sz_r(2) ! local shape of layout_r
365  integer(i32) :: loc_sz_a(2) ! local shape of layout_a
366  integer(i32) :: glob_idx(2) ! global indices
367 
368  integer(i32), allocatable :: k_list_glob(:) ! global list of k values
369 
370  if (bc_rmin == sll_p_polar_origin .and. rmin /= 0.0_f64) then
371  sll_error(this_sub_name, "BC option 'sll_p_polar_origin' requires r_min = 0")
372  end if
373 
374  ! Set boundary condition at r_min
375  select case (bc_rmin)
376  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0, sll_p_polar_origin)
377  solver%bc(1) = bc_rmin
378  case default
379  sll_error(this_sub_name, 'Unrecognized boundary condition at r_min')
380  end select
381 
382  ! Set boundary condition at r_max
383  select case (bc_rmax)
384  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
385  solver%bc(2) = bc_rmax
386  case default
387  sll_error(this_sub_name, 'Unrecognized boundary condition at r_max')
388  end select
389 
390  ! Important: if full circle is simulated, center point is not solved for!
391  ! Therefore, solution is calculated on nr+1-skip0 points.
392  sh = merge(1, 0, bc_rmin == sll_p_polar_origin)
393 
394  ! Consistency check: global size of 2D layouts must be (nr+1,ntheta)
395  sll_assert_always(sll_o_get_layout_global_size_i(layout_r) == nr + 1 - sh)
396  sll_assert_always(sll_o_get_layout_global_size_j(layout_r) == ntheta)
397  !
398  sll_assert_always(sll_o_get_layout_global_size_i(layout_a) == nr + 1 - sh)
399  sll_assert_always(sll_o_get_layout_global_size_j(layout_a) == ntheta)
400 
401  ! Compute local size of 2D arrays in the two layouts
402  call sll_o_compute_local_sizes(layout_r, loc_sz_r(1), loc_sz_r(2))
403  call sll_o_compute_local_sizes(layout_a, loc_sz_a(1), loc_sz_a(2))
404 
405  ! Consistency check: layout_r sequential in r, layout_a sequential in theta
406  sll_assert_always(loc_sz_r(1) == nr + 1 - sh)
407  sll_assert_always(loc_sz_a(2) == ntheta)
408 
409  ! Override vacuum permittivity in SI units
410  if (present(epsilon_0)) then
411  solver%epsilon_0 = epsilon_0
412  else
413  solver%epsilon_0 = sll_p_epsilon_0
414  end if
415 
416  ! Store global information in solver
417  solver%rmin = rmin
418  solver%rmax = rmax
419  solver%nr = nr
420  solver%ntheta = ntheta
421  solver%layout_a => layout_a
422  solver%layout_r => layout_r
423  solver%skip0 = sh
424 
425  ! r grid (possibly non-uniform)
426  allocate (r_nodes(nr + 1))
427 
428  if (present(rgrid)) then !--> Create computational grid from user data
429 
430  sll_assert_always(all(rgrid > 0.0_f64))
431  if (bc_rmin == sll_p_polar_origin) then
432  sll_assert_always(size(rgrid) == nr)
433  sll_assert_always(rgrid(nr) == rmax)
434  r_nodes(1) = -rgrid(1)
435  r_nodes(2:) = rgrid(:)
436  else
437  sll_assert_always(size(rgrid) == nr + 1)
438  sll_assert_always(rgrid(1) == rmin)
439  sll_assert_always(rgrid(nr + 1) == rmax)
440  r_nodes(:) = rgrid(:)
441  end if
442 
443  else !-------------------------> Create uniform grid
444 
445  if (bc_rmin == sll_p_polar_origin) then
446  associate(rmin => rmax/real(2*nr + 1, f64))
447  r_nodes(1) = -rmin
448  call sll_s_new_array_linspace(r_nodes(2:), rmin, rmax, endpoint=.true.)
449  end associate
450  else
451  call sll_s_new_array_linspace(r_nodes, rmin, rmax, endpoint=.true.)
452  end if
453 
454  end if
455 
456  ! Allocate arrays global in r
457  allocate (solver%g(nr + 1))
458  allocate (solver%z_r(nr + 1 - sh, loc_sz_r(2)))
459  allocate (solver%mat((nr - 1)*3, loc_sz_r(2))) ! for each k, matrix depends on r
460  allocate (solver%cts((nr - 1)*7))
461  allocate (solver%ipiv(nr - 1))
462 
463  ! Remap objects between two layouts (for transposing between f_r and f_a)
464  allocate (solver%z_a(loc_sz_a(1), ntheta))
465  solver%rmp_ra => sll_o_new_remap_plan(solver%layout_r, solver%layout_a, solver%z_r)
466  solver%rmp_ar => sll_o_new_remap_plan(solver%layout_a, solver%layout_r, solver%z_a)
467  deallocate (solver%z_a)
468 
469  ! Allocate in ALIGNED fashion 1D array for storing one row of f_a
470  solver%tmp => sll_f_fft_allocate_aligned_real(ntheta)
471 
472  ! Initialize plans for forward and backward FFTs
473  call sll_s_fft_init_r2r_1d(solver%fw, &
474  ntheta, &
475  solver%tmp(:), &
476  solver%tmp(:), &
478  aligned=.true., &
479  normalized=.true.)
480 
481  call sll_s_fft_init_r2r_1d(solver%bw, &
482  ntheta, &
483  solver%tmp(:), &
484  solver%tmp(:), &
486  aligned=.true., &
487  normalized=.false.)
488 
489  ! Store non-dimensional coefficient g(r) = \rho(r) / (B(r)^2 \epsilon_0)
490  solver%g(1 + sh:) = rho_m0(:)/(b_magn(:)**2*solver%epsilon_0)
491  if (sh == 1) then
492  solver%g(1) = solver%g(2) ! ghost point
493  end if
494 
495  ! Determine global k_list
496  allocate (k_list_glob(ntheta))
497  call sll_s_fft_get_k_list_r2r_1d(solver%fw, k_list_glob)
498  ! Extract local k list
499  allocate (solver%k_list(loc_sz_r(2)))
500  do j = 1, loc_sz_r(2)
501  glob_idx(:) = sll_o_local_to_global(solver%layout_r, [1, j])
502  solver%k_list(j) = k_list_glob(glob_idx(2))
503  end do
504  deallocate (k_list_glob)
505 
506  ! Store matrix coefficients into solver%mat
507  ! Cycle over k_j
508  do j = 1, loc_sz_r(2)
509 
510  ! Get value of k_j from precomputed list of local values
511  k = solver%k_list(j)
512 
513  !----------------------------------------------
514  ! Compute boundary conditions type for mode k_j
515  !----------------------------------------------
516  bck(:) = solver%bc(:)
517  do i = 1, 2
518  if (bck(i) == sll_p_neumann_mode_0) then
519  if (k == 0) then
520  bck(i) = sll_p_neumann
521  else
522  bck(i) = sll_p_dirichlet
523  end if
524  end if
525  end do
526 
527  !----------------------------------------------
528  ! Compute matrix coefficients for a given k_j
529  !----------------------------------------------
530  do i = 2, nr
531 
532  ! Finite difference coefficients for 0th (trivial), 1st and 2nd derivatives
533  hp = r_nodes(i + 1) - r_nodes(i)
534  hm = r_nodes(i) - r_nodes(i - 1)
535  inv_r = 1.0_f64/r_nodes(i)
536  d0_coeffs(:) = [0.0_f64, 1.0_f64, 0.0_f64]
537  d1_coeffs(:) = [-hp/hm, (hp**2 - hm**2)/(hp*hm), hm/hp]/(hp + hm)
538  d2_coeffs(:) = [2*hp/(hp + hm), -2.0_f64, 2*hm/(hp + hm)]/(hp*hm)
539 
540  ! Compute d/dr(ln(g)) as (1/g)*(dg/dr) using finite differences
541  ddr_ln_g = dot_product(d1_coeffs(:), solver%g(i - 1:i + 1))/solver%g(i)
542 
543  ! Determine contribution from zonal flow
544  if (present(lambda)) then
545  c = 1.0_f64/(lambda(i - sh)**2*solver%g(i))
546  if (present(use_zonal_flow)) then
547  if (use_zonal_flow .and. k == 0) then
548  c = 0.0_f64
549  end if
550  end if
551  else
552  c = 0.0_f64
553  end if
554 
555  ! Fill in elements of i-th matrix row
556  solver%mat(3*(i - 1) - 2:3*(i - 1), j) = &
557  -d2_coeffs(:) &
558  - d1_coeffs(:)*(inv_r + ddr_ln_g) &
559  + d0_coeffs(:)*((k*inv_r)**2 + c)
560 
561  end do
562 
563  !----------------------------------------------
564  ! Set boundary condition at rmin
565  !----------------------------------------------
566  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
567  solver%mat(1, j) = 0.0_f64
568 
569  else if (bck(1) == sll_p_neumann) then ! Neumann
570 
571  ! Coefficients of homogeneous boundary condition
572  hp = r_nodes(3) - r_nodes(2)
573  hm = r_nodes(2) - r_nodes(1)
574  d1_coeffs(:) = [-2 - hp/hm, 2 + hp/hm + hm/hp, -hm/hp]
575  solver%bc_coeffs_rmin(2:3) = -d1_coeffs(2:3)/d1_coeffs(1)
576 
577  ! Gaussian elimination: remove phi(1) variable using boundary condition
578  solver%mat(3, j) = solver%mat(3, j) + solver%bc_coeffs_rmin(3)*solver%mat(1, j)
579  solver%mat(2, j) = solver%mat(2, j) + solver%bc_coeffs_rmin(2)*solver%mat(1, j)
580  solver%mat(1, j) = 0.0_f64
581 
582  else if (bck(1) == sll_p_polar_origin) then ! center of circular domain
583 
584  ! Gaussian elimination: phi(1) = (-1)^k phi(2)
585  solver%mat(2, j) = solver%mat(2, j) + (-1)**k*solver%mat(1, j)
586  solver%mat(1, j) = 0.0_f64
587 
588  end if
589 
590  !----------------------------------------------
591  ! Set boundary condition at rmax
592  !----------------------------------------------
593  last = 3*(nr - 1)
594  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
595  solver%mat(last, j) = 0.0_f64
596 
597  else if (bck(2) == sll_p_neumann) then ! Neumann
598 
599  ! Coefficients of homogeneous boundary condition
600  hp = r_nodes(nr + 1) - r_nodes(nr)
601  hm = r_nodes(nr) - r_nodes(nr - 1)
602  d1_coeffs(:) = [hp/hm, -2 - hp/hm - hm/hp, 2 + hm/hp]
603  solver%bc_coeffs_rmax(-2:-1) = -d1_coeffs(1:2)/d1_coeffs(3)
604 
605  ! Gaussian elimination: remove phi(last) variable using boundary condition
606  solver%mat(last - 2, j) = solver%mat(last - 2, j) + solver%bc_coeffs_rmax(-2)*solver%mat(last, j)
607  solver%mat(last - 1, j) = solver%mat(last - 1, j) + solver%bc_coeffs_rmax(-1)*solver%mat(last, j)
608  solver%mat(last, j) = 0.0_f64
609 
610  end if
611 
612  end do
613 
614  end subroutine sll_s_qn_solver_2d_polar_par_init
615 
616  !=============================================================================
618  subroutine sll_s_qn_solver_2d_polar_par_solve(solver, rho, phi)
619  type(sll_t_qn_solver_2d_polar_par), intent(inout) :: solver
620  real(f64), intent(in) :: rho(:, :)
621  real(f64), target, intent(out) :: phi(:, :)
622 
623  integer(i32) :: nr, ntheta, bck(2)
624  integer(i32) :: i, j, k
625  integer(i32) :: nrpts
626  integer(i32) :: sh
627 
628  nr = solver%nr
629  ntheta = solver%ntheta
630 
631  ! Shift in radial grid indexing
632  sh = solver%skip0 ! =1 if bc_rmin==sll_p_polar_origin, =0 otherwise
633 
634  ! Number of points in radial grid
635  nrpts = nr + 1 - sh
636 
637  ! Consistency check: rho and phi must be given in layout sequential in theta
638  call verify_argument_sizes_par(solver%layout_a, rho, 'rho')
639  call verify_argument_sizes_par(solver%layout_a, phi, 'phi')
640 
641  ! Use output array 'phi' as 2D work array sequential in theta
642  solver%z_a => phi(:, :)
643 
644  ! For each r_i, compute FFT of rho(r_i,theta) to obtain \hat{rho}(r_i,k)
645  do i = 1, ubound(rho, 1)
646  solver%tmp(:) = rho(i, :)
647  call sll_s_fft_exec_r2r_1d(solver%fw, solver%tmp(:), solver%tmp(:))
648  solver%z_a(i, :) = solver%tmp(:)
649  end do
650 
651  ! Remap \hat{rho}(k) to layout distributed in k (global in r) -> \hat{rho}_k(r)
652  call sll_o_apply_remap_2d(solver%rmp_ar, solver%z_a, solver%z_r)
653 
654  ! Cycle over k_j
655  do j = 1, ubound(solver%z_r, 2)
656 
657  ! rhok(r) is k-th Fourier mode of rho(r,theta)
658  ! phik(r) is k-th Fourier mode of phi(r,theta)
659  ! rhok is 1D contiguous slice (column) of solver%z
660  ! we will overwrite rhok with phik
661  associate(rhok => solver%z_r(:, j), phik => solver%z_r(:, j))
662 
663  rhok(:) = rhok(:)/(solver%g(1 + sh:)*solver%epsilon_0)
664 
665  ! Solve tridiagonal system to obtain \hat{phi}_{k_j}(r) at internal points
666  call sll_s_setup_cyclic_tridiag(solver%mat(:, j), nr - 1, solver%cts, solver%ipiv)
667  call sll_o_solve_cyclic_tridiag(solver%cts, solver%ipiv, &
668  rhok(2 - sh:nrpts - 1), nr - 1, phik(2 - sh:nrpts - 1))
669 
670  ! Get value of k_j from precomputed list of local values
671  k = solver%k_list(j)
672 
673  ! Compute boundary conditions type for mode k_j
674  bck(:) = solver%bc(:)
675  do i = 1, 2
676  if (bck(i) == sll_p_neumann_mode_0) then
677  if (k == 0) then
678  bck(i) = sll_p_neumann
679  else
680  bck(i) = sll_p_dirichlet
681  end if
682  end if
683  end do
684 
685  ! Boundary condition at rmin
686  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
687  phik(1) = 0.0_f64
688  else if (bck(1) == sll_p_neumann) then ! Neumann
689  associate(c => solver%bc_coeffs_rmin)
690  phik(1) = c(2)*phik(2) + c(3)*phik(3)
691  end associate
692  end if
693 
694  ! Boundary condition at rmax
695  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
696  phik(nrpts) = 0.0_f64
697  else if (bck(2) == sll_p_neumann) then ! Neumann
698  associate(c => solver%bc_coeffs_rmax)
699  phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
700  end associate
701  end if
702 
703  end associate
704 
705  end do
706 
707  ! Redistribute \hat{phi}(r_i,k_j) into layout global in k
708  call sll_o_apply_remap_2d(solver%rmp_ra, solver%z_r, solver%z_a)
709 
710  ! For each r_i, compute inverse FFT of \hat{phi}(r_i,k) to obtain phi(r_i,theta)
711  do i = 1, ubound(solver%z_a, 1)
712  solver%tmp(:) = solver%z_a(i, :)
713  call sll_s_fft_exec_r2r_1d(solver%bw, solver%tmp(:), solver%tmp(:))
714  phi(i, :) = solver%tmp(:)
715  end do
716 
718 
719  !=============================================================================
722  type(sll_t_qn_solver_2d_polar_par), intent(inout) :: solver
723 
724  call sll_s_fft_free(solver%fw)
725  call sll_s_fft_free(solver%bw)
726 
727  call sll_s_fft_deallocate_aligned_real(solver%tmp)
728 
729  deallocate (solver%z_r)
730  deallocate (solver%mat)
731  deallocate (solver%cts)
732  deallocate (solver%ipiv)
733 
734  deallocate (solver%rmp_ra)
735  deallocate (solver%rmp_ar)
736 
737  solver%layout_r => null()
738  solver%layout_a => null()
739  solver%rmp_ra => null()
740  solver%rmp_ar => null()
741 
742  end subroutine sll_s_qn_solver_2d_polar_par_free
743 
744  !=============================================================================
746  subroutine verify_argument_sizes_par(layout, array, array_name)
747  type(sll_t_layout_2d), pointer :: layout
748  real(f64), intent(in) :: array(:, :)
749  character(len=*), intent(in) :: array_name
750 
751  integer(i32) :: n(2) ! nx_loc, ny_loc
752  character(len=*), parameter :: sfmt = "('['(i0)','(i0)']')"
753  character(len=256) :: err_fmt
754  character(len=256) :: err_msg
755 
756  call sll_o_compute_local_sizes(layout, n(1), n(2))
757 
758  if (.not. all(shape(array) == n(:))) then
759  ! Create error format string
760  err_fmt = "(a,"//sfmt//",a,"//sfmt//",a)"
761  ! Write error message to string
762  write (err_msg, err_fmt) &
763  "shape("//array_name//") = ", shape(array), &
764  " is not compatible with local shape ", n, " of 2D layout"
765  ! Stop execution with error
766  sll_error("sll_s_qn_solver_2d_polar_par_solve", trim(err_msg))
767  end if
768 
769  end subroutine verify_argument_sizes_par
770 
Solve tridiagonal system (double or complex)
Parallelizing facility.
subroutine, public sll_s_halt_collective()
Ends the paralell environment.
type(sll_t_collective_t), pointer, public sll_v_world_collective
Control of subset assignment. Processes with the same color are in the same new communicator.
integer(kind=i32) function, public sll_f_get_collective_size(col)
Determines the size of the group associated with a communicator.
Fortran module where set some physical and mathematical constants.
real(kind=f64), parameter, public sll_p_epsilon_0
FFT interface for FFTW.
subroutine, public sll_s_fft_get_k_list_r2r_1d(plan, k_list)
Get a list of the k modes in an r2r FFT k_list = [0, 1, 2, ..., n/2, (n+1)/2-1, .....
subroutine, public sll_s_fft_free(plan)
Delete a plan.
integer, parameter, public sll_p_fft_forward
Flag to specify forward FFT (negative sign) [value -1].
real(c_double) function, dimension(:), pointer, public sll_f_fft_allocate_aligned_real(n)
Function to allocate an aligned real array.
subroutine, public sll_s_fft_init_r2r_1d(plan, nx, array_in, array_out, direction, normalized, aligned, optimization)
Create new 1d real to real plan.
subroutine, public sll_s_fft_deallocate_aligned_real(data)
Function to deallocate an aligned real array.
integer, parameter, public sll_p_fft_backward
Flag to specify backward FFT (positive sign) [value 1].
subroutine, public sll_s_fft_exec_r2r_1d(plan, array_in, array_out)
Compute fast Fourier transform in real to real mode.
Parallel quasi-neutrality solver on 2D polar mesh; uses FFT in theta and 2nd-order FD in r.
subroutine, public sll_s_qn_solver_2d_polar_par_init(solver, layout_r, layout_a, rmin, rmax, nr, ntheta, bc_rmin, bc_rmax, rho_m0, b_magn, lambda, use_zonal_flow, epsilon_0, rgrid)
Initialize the solver.
subroutine, public sll_s_qn_solver_2d_polar_par_solve(solver, rho, phi)
Solve the quasi-neutrality equation and get the electrostatic potential.
subroutine verify_argument_sizes_par(layout, array, array_name)
Check if array sizes are compatible with the layout.
subroutine, public sll_s_qn_solver_2d_polar_par_free(solver)
Delete contents (local storage) of quasi-neutrality solver.
Tridiagonal system solver.
subroutine, public sll_s_setup_cyclic_tridiag(a, n, cts, ipiv)
Give the factorization of the matrix in argument.
Some common numerical utilities.
pure subroutine, public sll_s_new_array_linspace(array, vmin, vmax, endpoint, step)
Equivalent to numpy.linspace @contact Yaman Güçlü, IPP Garching.
Type for FFT plan in SeLaLib.
    Report Typos and Errors