Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_poisson_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 
192 
194 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
195 #include "sll_assert.h"
196 #include "sll_errors.h"
197 #include "sll_working_precision.h"
198 
200  sll_p_dirichlet, &
201  sll_p_neumann, &
202  sll_p_neumann_mode_0, &
203  sll_p_polar_origin
204 
205  use sll_m_collective, only: &
209 
210  use sll_m_fft, only: &
211  sll_t_fft, &
220 
221  use sll_m_remapper, only: &
222  sll_t_layout_2d, &
223  sll_o_compute_local_sizes, &
224  sll_o_get_layout_global_size_i, &
225  sll_o_get_layout_global_size_j, &
226  sll_o_local_to_global, &
227  sll_t_remap_plan_2d_real64, &
228  sll_o_new_remap_plan, &
229  sll_o_apply_remap_2d
230 
231  use sll_m_tridiagonal, only: &
234 
235  use sll_m_utilities, only: &
237 
238  implicit none
239 
240  public :: &
245 
246  private
247 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
248 
251 
252  real(f64) :: rmin
253  real(f64) :: rmax
254  integer(i32) :: nr
255  integer(i32) :: ntheta
256  integer(i32) :: bc(2)
257 
258  type(sll_t_fft) :: fw
259  type(sll_t_fft) :: bw
260  real(f64), pointer :: tmp(:)
261  integer(i32), allocatable :: k_list(:)
262  real(f64), allocatable :: z_r(:, :)
263  real(f64), pointer :: z_a(:, :)
264  real(f64), allocatable :: mat(:, :)
265  real(f64), allocatable :: cts(:)
266  integer(i32), allocatable :: ipiv(:)
267 
268  real(f64) :: bc_coeffs_rmin(2:3) ! needed for Neumann at r=r_min
269  real(f64) :: bc_coeffs_rmax(-2:-1) ! needed for Neumann at r=r_max
270  integer :: skip0 ! needed for full circle
271 
272  type(sll_t_layout_2d), pointer :: layout_r
273  type(sll_t_layout_2d), pointer :: layout_a
274  type(sll_t_remap_plan_2d_real64), pointer :: rmp_ra
275  type(sll_t_remap_plan_2d_real64), pointer :: rmp_ar
276 
278 
279 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
280 contains
281 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
282 
283  !=============================================================================
285  subroutine sll_s_poisson_2d_polar_par_init(solver, &
286  layout_r, &
287  layout_a, &
288  rmin, &
289  rmax, &
290  nr, &
291  ntheta, &
292  bc_rmin, &
293  bc_rmax, &
294  rgrid)
295 
296  type(sll_t_poisson_2d_polar_par), intent(out) :: solver
297  type(sll_t_layout_2d), pointer :: layout_r
298  type(sll_t_layout_2d), pointer :: layout_a
299  real(f64), intent(in) :: rmin
300  real(f64), intent(in) :: rmax
301  integer(i32), intent(in) :: nr
302  integer(i32), intent(in) :: ntheta
303  integer(i32), intent(in) :: bc_rmin
304  integer(i32), intent(in) :: bc_rmax
305  real(f64), target, optional, intent(in) :: rgrid(:)
306 
307  character(len=*), parameter :: this_sub_name = 'sll_s_poisson_2d_polar_par_init'
308 
309  real(f64) :: hp, hm
310  real(f64) :: inv_r
311  real(f64) :: d1_coeffs(3)
312  real(f64) :: d2_coeffs(3)
313  real(f64), allocatable :: r_nodes(:)
314 
315  integer(i32) :: i, j, k
316  integer(i32) :: bck(2)
317  integer(i32) :: last
318  integer(i32) :: sh
319 
320  integer(i32) :: loc_sz_r(2) ! sequential in r direction
321  integer(i32) :: loc_sz_a(2) ! sequential in theta direction
322  integer(i32) :: glob_idx(2)
323 
324  integer(i32), allocatable :: k_list_glob(:)
325 
326  ! Consistency checks
327  sll_assert_always(rmin >= 0.0_f64)
328  sll_assert_always(rmin < rmax)
329  sll_assert_always(nr >= 1)
330  sll_assert_always(ntheta >= 1)
331 
332  if (bc_rmin == sll_p_polar_origin .and. rmin /= 0.0_f64) then
333  sll_error(this_sub_name, "BC option 'sll_p_polar_origin' requires r_min = 0")
334  end if
335 
336  ! Set boundary condition at r_min
337  select case (bc_rmin)
338  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0, sll_p_polar_origin)
339  solver%bc(1) = bc_rmin
340  case default
341  sll_error(this_sub_name, 'Unrecognized boundary condition at r_min')
342  end select
343 
344  ! Set boundary condition at r_max
345  select case (bc_rmax)
346  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
347  solver%bc(2) = bc_rmax
348  case default
349  sll_error(this_sub_name, 'Unrecognized boundary condition at r_max')
350  end select
351 
352  ! Important: if full circle is simulated, center point is not solved for!
353  ! Therefore, solution is calculated on nr+1-skip0 points.
354  sh = merge(1, 0, bc_rmin == sll_p_polar_origin)
355 
356  ! Consistency check: global size of 2D layouts must be (nr+1,ntheta)
357  sll_assert_always(sll_o_get_layout_global_size_i(layout_r) == nr + 1 - sh)
358  sll_assert_always(sll_o_get_layout_global_size_j(layout_r) == ntheta)
359  !
360  sll_assert_always(sll_o_get_layout_global_size_i(layout_a) == nr + 1 - sh)
361  sll_assert_always(sll_o_get_layout_global_size_j(layout_a) == ntheta)
362 
363  ! Compute local size of 2D arrays in the two layouts
364  call sll_o_compute_local_sizes(layout_r, loc_sz_r(1), loc_sz_r(2))
365  call sll_o_compute_local_sizes(layout_a, loc_sz_a(1), loc_sz_a(2))
366 
367  ! Consistency check: layout_r sequential in r, layout_a sequential in theta
368  sll_assert_always(loc_sz_r(1) == nr + 1 - sh)
369  sll_assert_always(loc_sz_a(2) == ntheta)
370 
371  ! Store global information in solver
372  solver%rmin = rmin
373  solver%rmax = rmax
374  solver%nr = nr
375  solver%ntheta = ntheta
376  solver%layout_a => layout_a
377  solver%layout_r => layout_r
378  solver%skip0 = sh
379 
380  ! r grid (possibly non-uniform)
381  allocate (r_nodes(nr + 1))
382 
383  if (present(rgrid)) then !--> Create computational grid from user data
384 
385  sll_assert_always(all(rgrid > 0.0_f64))
386  if (bc_rmin == sll_p_polar_origin) then
387  sll_assert_always(size(rgrid) == nr)
388  sll_assert_always(rgrid(nr) == rmax)
389  r_nodes(1) = -rgrid(1)
390  r_nodes(2:) = rgrid(:)
391  else
392  sll_assert_always(size(rgrid) == nr + 1)
393  sll_assert_always(rgrid(1) == rmin)
394  sll_assert_always(rgrid(nr + 1) == rmax)
395  r_nodes(:) = rgrid(:)
396  end if
397 
398  else !-------------------------> Create uniform grid
399 
400  if (bc_rmin == sll_p_polar_origin) then
401  associate(rmin => rmax/real(2*nr + 1, f64))
402  r_nodes(1) = -rmin
403  call sll_s_new_array_linspace(r_nodes(2:), rmin, rmax, endpoint=.true.)
404  end associate
405  else
406  call sll_s_new_array_linspace(r_nodes, rmin, rmax, endpoint=.true.)
407  end if
408 
409  end if
410 
411  ! Allocate arrays global in r
412  allocate (solver%z_r(nr + 1 + sh, loc_sz_r(2)))
413  allocate (solver%mat((nr - 1)*3, loc_sz_r(2))) ! for each k, matrix depends on r
414  allocate (solver%cts((nr - 1)*7))
415  allocate (solver%ipiv(nr - 1))
416 
417  ! Remap objects between two layouts (for transposing between f_r and f_a)
418  allocate (solver%z_a(loc_sz_a(1), ntheta))
419  solver%rmp_ra => sll_o_new_remap_plan(solver%layout_r, solver%layout_a, solver%z_r)
420  solver%rmp_ar => sll_o_new_remap_plan(solver%layout_a, solver%layout_r, solver%z_a)
421  deallocate (solver%z_a)
422 
423  ! Allocate in ALIGNED fashion 1D work array for FFT
424  solver%tmp => sll_f_fft_allocate_aligned_real(ntheta)
425 
426  ! Initialize plans for forward and backward FFTs
427  call sll_s_fft_init_r2r_1d(solver%fw, &
428  ntheta, &
429  solver%tmp(:), &
430  solver%tmp(:), &
432  aligned=.true., &
433  normalized=.true.)
434 
435  call sll_s_fft_init_r2r_1d(solver%bw, &
436  ntheta, &
437  solver%tmp(:), &
438  solver%tmp(:), &
440  aligned=.true., &
441  normalized=.false.)
442 
443  ! Determine global k_list
444  allocate (k_list_glob(ntheta))
445  call sll_s_fft_get_k_list_r2r_1d(solver%fw, k_list_glob)
446  ! Extract local k list
447  allocate (solver%k_list(loc_sz_r(2)))
448  do j = 1, loc_sz_r(2)
449  glob_idx(:) = sll_o_local_to_global(solver%layout_r, [1, j])
450  solver%k_list(j) = k_list_glob(glob_idx(2))
451  end do
452  deallocate (k_list_glob)
453 
454  ! Store matrix coefficients into solver%mat
455  ! Cycle over k_j
456  do j = 1, loc_sz_r(2)
457 
458  ! Get value of k_j from precomputed list of local values
459  k = solver%k_list(j)
460 
461  !----------------------------------------------
462  ! Compute boundary conditions type for mode k_j
463  !----------------------------------------------
464  bck(:) = solver%bc(:)
465  do i = 1, 2
466  if (bck(i) == sll_p_neumann_mode_0) then
467  if (k == 0) then
468  bck(i) = sll_p_neumann
469  else
470  bck(i) = sll_p_dirichlet
471  end if
472  end if
473  end do
474 
475  !----------------------------------------------
476  ! Compute matrix coefficients for a given k_j
477  !----------------------------------------------
478  do i = 2, nr
479  hp = r_nodes(i + 1) - r_nodes(i)
480  hm = r_nodes(i) - r_nodes(i - 1)
481  inv_r = 1.0_f64/r_nodes(i)
482  d1_coeffs(:) = [-hp/hm, (hp**2 - hm**2)/(hp*hm), hm/hp]/(hp + hm)
483  d2_coeffs(:) = [2*hp/(hp + hm), -2.0_f64, 2*hm/(hp + hm)]/(hp*hm)
484 
485  solver%mat(3*(i - 1) - 2:3*(i - 1), j) = &
486  -d2_coeffs(:) - d1_coeffs(:)*inv_r + [0.0_f64, (k*inv_r)**2, 0.0_f64]
487  end do
488 
489  !----------------------------------------------
490  ! Set boundary condition at rmin
491  !----------------------------------------------
492  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
493  solver%mat(1, j) = 0.0_f64
494 
495  else if (bck(1) == sll_p_neumann) then ! Neumann
496 
497  ! Coefficients of homogeneous boundary condition
498  hp = r_nodes(3) - r_nodes(2)
499  hm = r_nodes(2) - r_nodes(1)
500  d1_coeffs(:) = [-2 - hp/hm, 2 + hp/hm + hm/hp, -hm/hp]
501  solver%bc_coeffs_rmin(2:3) = -d1_coeffs(2:3)/d1_coeffs(1)
502 
503  ! Gaussian elimination: remove phi(1) variable using boundary condition
504  solver%mat(3, j) = solver%mat(3, j) + solver%bc_coeffs_rmin(3)*solver%mat(1, j)
505  solver%mat(2, j) = solver%mat(2, j) + solver%bc_coeffs_rmin(2)*solver%mat(1, j)
506  solver%mat(1, j) = 0.0_f64
507 
508  else if (bck(1) == sll_p_polar_origin) then ! center of circular domain
509 
510  ! Gaussian elimination: phi(1) = (-1)^k phi(2)
511  solver%mat(2, j) = solver%mat(2, j) + (-1)**k*solver%mat(1, j)
512  solver%mat(1, j) = 0.0_f64
513 
514  end if
515 
516  !----------------------------------------------
517  ! Set boundary condition at rmax
518  !----------------------------------------------
519  last = 3*(nr - 1)
520  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
521  solver%mat(last, j) = 0.0_f64
522 
523  else if (bck(2) == sll_p_neumann) then ! Neumann
524 
525  ! Coefficients of homogeneous boundary condition
526  hp = r_nodes(nr + 1) - r_nodes(nr)
527  hm = r_nodes(nr) - r_nodes(nr - 1)
528  d1_coeffs(:) = [hp/hm, -2 - hp/hm - hm/hp, 2 + hm/hp]
529  solver%bc_coeffs_rmax(-2:-1) = -d1_coeffs(1:2)/d1_coeffs(3)
530 
531  ! Gaussian elimination: remove phi(last) variable using boundary condition
532  solver%mat(last - 2, j) = solver%mat(last - 2, j) + solver%bc_coeffs_rmax(-2)*solver%mat(last, j)
533  solver%mat(last - 1, j) = solver%mat(last - 1, j) + solver%bc_coeffs_rmax(-1)*solver%mat(last, j)
534  solver%mat(last, j) = 0.0_f64
535 
536  end if
537 
538  end do
539 
540  end subroutine sll_s_poisson_2d_polar_par_init
541 
542  !=============================================================================
544  subroutine sll_s_poisson_2d_polar_par_solve(solver, rho, phi)
545  type(sll_t_poisson_2d_polar_par), intent(inout) :: solver
546  real(f64), intent(in) :: rho(:, :)
547  real(f64), target, intent(out) :: phi(:, :)
548 
549  integer(i32) :: nr, ntheta, bck(2)
550  integer(i32) :: i, j, k
551  integer(i32) :: nrpts
552  integer(i32) :: sh
553 
554  nr = solver%nr
555  ntheta = solver%ntheta
556 
557  ! Shift in radial grid indexing
558  sh = solver%skip0 ! =1 if bc_rmin==sll_p_polar_origin, =0 otherwise
559 
560  ! Number of points in radial grid
561  nrpts = nr + 1 - sh
562 
563  ! Consistency check: rho and phi must be given in layout sequential in theta
564  call verify_argument_sizes_par(solver%layout_a, rho, 'rho')
565  call verify_argument_sizes_par(solver%layout_a, phi, 'phi')
566 
567  ! Use output array 'phi' as 2D work array sequential in theta
568  solver%z_a => phi(:, :)
569 
570  ! For each r_i, compute FFT of rho(r_i,theta) to obtain \hat{rho}(r_i,k)
571  do i = 1, ubound(rho, 1)
572  solver%tmp(:) = rho(i, :)
573  call sll_s_fft_exec_r2r_1d(solver%fw, solver%tmp(:), solver%tmp(:))
574  solver%z_a(i, :) = solver%tmp(:)
575  end do
576 
577  ! Remap \hat{rho}(k) to layout distributed in k (global in r) -> \hat{rho}_k(r)
578  call sll_o_apply_remap_2d(solver%rmp_ar, solver%z_a, solver%z_r)
579 
580  ! Cycle over k_j
581  do j = 1, ubound(solver%z_r, 2)
582 
583  ! rhok(r) is k-th Fourier mode of rho(r,theta)
584  ! phik(r) is k-th Fourier mode of phi(r,theta)
585  ! rhok is 1D contiguous slice (column) of solver%z
586  ! we will overwrite rhok with phik
587  associate(rhok => solver%z_r(:, j), phik => solver%z_r(:, j))
588 
589  ! Solve tridiagonal system to obtain \hat{phi}_{k_j}(r) at internal points
590  call sll_s_setup_cyclic_tridiag(solver%mat(:, j), nr - 1, solver%cts, solver%ipiv)
591  call sll_o_solve_cyclic_tridiag(solver%cts, solver%ipiv, rhok(2 - sh:nrpts - 1), nr - 1, phik(2 - sh:nrpts - 1))
592 
593  ! Get value of k_j from precomputed list of local values
594  k = solver%k_list(j)
595 
596  ! Compute boundary conditions type for mode k_j
597  bck(:) = solver%bc(:)
598  do i = 1, 2
599  if (bck(i) == sll_p_neumann_mode_0) then
600  if (k == 0) then
601  bck(i) = sll_p_neumann
602  else
603  bck(i) = sll_p_dirichlet
604  end if
605  end if
606  end do
607 
608  ! Boundary condition at rmin
609  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
610  phik(1) = 0.0_f64
611  else if (bck(1) == sll_p_neumann) then ! Neumann
612  associate(c => solver%bc_coeffs_rmin)
613  phik(1) = c(2)*phik(2) + c(3)*phik(3)
614  end associate
615  end if
616 
617  ! Boundary condition at rmax
618  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
619  phik(nrpts) = 0.0_f64
620  else if (bck(2) == sll_p_neumann) then ! Neumann
621  associate(c => solver%bc_coeffs_rmax)
622  phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
623  end associate
624  end if
625 
626  end associate
627 
628  end do
629 
630  ! Redistribute \hat{phi}(r_i,k_j) into layout global in k
631  call sll_o_apply_remap_2d(solver%rmp_ra, solver%z_r, solver%z_a)
632 
633  ! For each r_i, compute inverse FFT of \hat{phi}(r_i,k) to obtain phi(r_i,theta)
634  do i = 1, ubound(solver%z_a, 1)
635  solver%tmp(:) = solver%z_a(i, :)
636  call sll_s_fft_exec_r2r_1d(solver%bw, solver%tmp(:), solver%tmp(:))
637  phi(i, :) = solver%tmp(:)
638  end do
639 
640  end subroutine sll_s_poisson_2d_polar_par_solve
641 
642  !=============================================================================
645  type(sll_t_poisson_2d_polar_par), intent(inout) :: solver
646 
647  call sll_s_fft_free(solver%fw)
648  call sll_s_fft_free(solver%bw)
649 
650  call sll_s_fft_deallocate_aligned_real(solver%tmp)
651 
652  deallocate (solver%z_r)
653  deallocate (solver%mat)
654  deallocate (solver%cts)
655  deallocate (solver%ipiv)
656 
657  deallocate (solver%rmp_ra)
658  deallocate (solver%rmp_ar)
659 
660  solver%layout_r => null()
661  solver%layout_a => null()
662  solver%rmp_ra => null()
663  solver%rmp_ar => null()
664 
665  end subroutine sll_s_poisson_2d_polar_par_free
666 
667  !=============================================================================
669  subroutine verify_argument_sizes_par(layout, array, array_name)
670  type(sll_t_layout_2d), pointer :: layout
671  real(f64), intent(in) :: array(:, :)
672  character(len=*), intent(in) :: array_name
673 
674  integer(i32) :: n(2) ! nx_loc, ny_loc
675  character(len=*), parameter :: sfmt = "('['(i0)','(i0)']')"
676  character(len=256) :: err_fmt
677  character(len=256) :: err_msg
678 
679  call sll_o_compute_local_sizes(layout, n(1), n(2))
680 
681  if (.not. all(shape(array) == n(:))) then
682  ! Create error format string
683  err_fmt = "(a,"//sfmt//",a,"//sfmt//",a)"
684  ! Write error message to string
685  write (err_msg, err_fmt) &
686  "shape("//array_name//") = ", shape(array), &
687  " is not compatible with local shape ", n, " of 2D layout"
688  ! Stop execution with error
689  sll_error("sll_s_poisson_2d_polar_par_solve", trim(err_msg))
690  end if
691 
692  end subroutine verify_argument_sizes_par
693 
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.
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 Poisson solver on 2D polar mesh; uses FFT in theta and 2nd-order FD in r.
subroutine, public sll_s_poisson_2d_polar_par_init(solver, layout_r, layout_a, rmin, rmax, nr, ntheta, bc_rmin, bc_rmax, rgrid)
sll_o_initialize the Poisson solver in polar coordinates
subroutine verify_argument_sizes_par(layout, array, array_name)
Check if array sizes are compatible with the layout.
subroutine, public sll_s_poisson_2d_polar_par_solve(solver, rho, phi)
Solve the Poisson equation and get the electrostatic potential.
subroutine, public sll_s_poisson_2d_polar_par_free(solver)
Delete contents (local storage) of Poisson's 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.
Class for the Poisson solver in polar coordinate.
    Report Typos and Errors