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.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 
181 
183 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
184 #include "sll_assert.h"
185 #include "sll_errors.h"
186 #include "sll_working_precision.h"
187 
189  sll_p_dirichlet, &
190  sll_p_neumann, &
191  sll_p_neumann_mode_0, &
192  sll_p_polar_origin
193 
194  use sll_m_fft, only: &
195  sll_t_fft, &
205 
206  use sll_m_tridiagonal, only: &
209 
210  use sll_m_poisson_2d_base, only: &
213 
214  use sll_m_utilities, only: &
216 
217  implicit none
218 
219  public :: &
224  sll_f_new_poisson_2d_polar ! Needed by some simulations
225 
226  private
227 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
228 
231 
232  private
233 
234  real(f64) :: rmin
235  real(f64) :: rmax
236  integer(i32) :: nr
237  integer(i32) :: nt
238  integer(i32) :: bc(2)
239 
240  type(sll_t_fft) :: fw
241  type(sll_t_fft) :: bw
242  complex(f64), allocatable :: z(:, :)
243  complex(f64), pointer :: temp_c(:)
244  real(f64), pointer :: temp_r(:)
245  real(f64), allocatable :: mat(:, :)
246  real(f64), allocatable :: cts(:)
247  integer(i32), allocatable :: ipiv(:)
248 
249  real(f64) :: bc_coeffs_rmin(2:3) ! needed for Neumann at r=r_min
250  real(f64) :: bc_coeffs_rmax(-2:-1) ! needed for Neumann at r=r_max
251  integer :: skip0 ! needed for full circle
252 
253  contains
254 
255  procedure :: compute_phi_from_rho => s_compute_phi_from_rho
256  procedure :: compute_e_from_rho => s_compute_e_from_rho
257  procedure :: l2norm_squared => f_l2norm_squared
258  procedure :: compute_rhs_from_function => s_compute_rhs_from_function
259  procedure :: free => s_free
260 
261  end type sll_t_poisson_2d_polar
262 
263 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
264 contains
265 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
266 
267  !=============================================================================
269  subroutine sll_s_poisson_2d_polar_init(solver, &
270  rmin, &
271  rmax, &
272  nr, &
273  ntheta, &
274  bc_rmin, &
275  bc_rmax, &
276  rgrid)
277 
278  type(sll_t_poisson_2d_polar), intent(out) :: solver
279  real(f64), intent(in) :: rmin
280  real(f64), intent(in) :: rmax
281  integer(i32), intent(in) :: nr
282  integer(i32), intent(in) :: ntheta
283  integer(i32), intent(in) :: bc_rmin
284  integer(i32), intent(in) :: bc_rmax
285  real(f64), optional, intent(in) :: rgrid(:)
286 
287  character(len=*), parameter :: this_sub_name = 'sll_s_poisson_2d_polar_init'
288 
289  real(f64) :: hp, hm
290  real(f64) :: inv_r
291  real(f64) :: d1_coeffs(3)
292  real(f64) :: d2_coeffs(3)
293  real(f64), allocatable :: r_nodes(:)
294 
295  integer(i32) :: i, k
296  integer(i32) :: bck(2)
297  integer(i32) :: last
298 
299  ! Consistency checks
300  sll_assert_always(rmin >= 0.0_f64)
301  sll_assert_always(rmin < rmax)
302  sll_assert_always(nr >= 1)
303  sll_assert_always(ntheta >= 1)
304 
305  if (bc_rmin == sll_p_polar_origin .and. rmin /= 0.0_f64) then
306  sll_error(this_sub_name, "BC option 'sll_p_polar_origin' requires r_min = 0")
307  end if
308 
309  ! Set boundary condition at r_min
310  select case (bc_rmin)
311  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0, sll_p_polar_origin)
312  solver%bc(1) = bc_rmin
313  case default
314  sll_error(this_sub_name, 'Unrecognized boundary condition at r_min')
315  end select
316 
317  ! Set boundary condition at r_max
318  select case (bc_rmax)
319  case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
320  solver%bc(2) = bc_rmax
321  case default
322  sll_error(this_sub_name, 'Unrecognized boundary condition at r_max')
323  end select
324 
325  ! Store information in solver
326  solver%rmin = rmin
327  solver%rmax = rmax
328  solver%nr = nr
329  solver%nt = ntheta
330 
331  ! Important: if full circle is simulated, center point is not solved for!
332  ! Therefore, solution is calculated on nr+1-skip0 points.
333  solver%skip0 = merge(1, 0, bc_rmin == sll_p_polar_origin)
334 
335  ! r grid (possibly non-uniform)
336  allocate (r_nodes(nr + 1))
337 
338  if (present(rgrid)) then !--> Create computational grid from user data
339 
340  sll_assert_always(all(rgrid > 0.0_f64))
341  if (bc_rmin == sll_p_polar_origin) then
342  sll_assert_always(size(rgrid) == nr)
343  sll_assert_always(rgrid(nr) == rmax)
344  r_nodes(1) = -rgrid(1)
345  r_nodes(2:) = rgrid(:)
346  else
347  sll_assert_always(size(rgrid) == nr + 1)
348  sll_assert_always(rgrid(1) == rmin)
349  sll_assert_always(rgrid(nr + 1) == rmax)
350  r_nodes(:) = rgrid(:)
351  end if
352 
353  else !-------------------------> Create uniform grid
354 
355  if (bc_rmin == sll_p_polar_origin) then
356  associate(rmin => rmax/real(2*nr + 1, f64))
357  r_nodes(1) = -rmin
358  call sll_s_new_array_linspace(r_nodes(2:), rmin, rmax, endpoint=.true.)
359  end associate
360  else
361  call sll_s_new_array_linspace(r_nodes, rmin, rmax, endpoint=.true.)
362  end if
363 
364  end if
365 
366  ! Allocate arrays for solution of linear systems along r
367  associate(sh => solver%skip0)
368  allocate (solver%z(nr + 1 - sh, 0:ntheta/2))
369  allocate (solver%mat((nr - 1)*3, 0:ntheta/2)) ! for each k, matrix depends on r
370  allocate (solver%cts((nr - 1)*7))
371  allocate (solver%ipiv(nr - 1))
372  end associate
373 
374  ! Allocate in ALIGNED fashion 1D work arrays for FFT
375  solver%temp_r => sll_f_fft_allocate_aligned_real(ntheta)
376  solver%temp_c => sll_f_fft_allocate_aligned_complex(ntheta/2 + 1)
377 
378  ! Initialize plans for forward and backward FFTs
379  call sll_s_fft_init_r2c_1d(solver%fw, &
380  ntheta, &
381  solver%temp_r(:), &
382  solver%temp_c(:), &
383  aligned=.true., &
384  normalized=.true.)
385 
386  call sll_s_fft_init_c2r_1d(solver%bw, &
387  ntheta, &
388  solver%temp_c(:), &
389  solver%temp_r(:), &
390  aligned=.true., &
391  normalized=.false.)
392 
393  ! Store matrix coefficients into solver%mat
394  ! Cycle over k
395  do k = 0, ntheta/2
396 
397  !--------------------------------------------
398  ! Compute boundary conditions type for mode k
399  !--------------------------------------------
400  bck(:) = solver%bc(:)
401  do i = 1, 2
402  if (bck(i) == sll_p_neumann_mode_0) then
403  if (k == 0) then
404  bck(i) = sll_p_neumann
405  else
406  bck(i) = sll_p_dirichlet
407  end if
408  end if
409  end do
410 
411  !--------------------------------------------
412  ! Compute matrix coefficients for a given k_j
413  !--------------------------------------------
414  do i = 2, nr
415  hp = r_nodes(i + 1) - r_nodes(i)
416  hm = r_nodes(i) - r_nodes(i - 1)
417  inv_r = 1.0_f64/r_nodes(i)
418  d1_coeffs(:) = [-hp/hm, (hp**2 - hm**2)/(hp*hm), hm/hp]/(hp + hm)
419  d2_coeffs(:) = [2*hp/(hp + hm), -2.0_f64, 2*hm/(hp + hm)]/(hp*hm)
420 
421  solver%mat(3*(i - 1) - 2:3*(i - 1), k) = &
422  -d2_coeffs(:) - d1_coeffs(:)*inv_r + [0.0_f64, (k*inv_r)**2, 0.0_f64]
423  end do
424 
425  !--------------------------------------------
426  ! Set boundary condition at rmin
427  !--------------------------------------------
428  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
429  solver%mat(1, k) = 0.0_f64
430 
431  else if (bck(1) == sll_p_neumann) then ! Neumann
432 
433  ! Coefficients of homogeneous boundary condition
434  hp = r_nodes(3) - r_nodes(2)
435  hm = r_nodes(2) - r_nodes(1)
436  d1_coeffs(:) = [-2 - hp/hm, 2 + hp/hm + hm/hp, -hm/hp]
437  solver%bc_coeffs_rmin(2:3) = -d1_coeffs(2:3)/d1_coeffs(1)
438 
439  ! Gaussian elimination: remove phi(1) variable using boundary condition
440  solver%mat(3, k) = solver%mat(3, k) + solver%bc_coeffs_rmin(3)*solver%mat(1, k)
441  solver%mat(2, k) = solver%mat(2, k) + solver%bc_coeffs_rmin(2)*solver%mat(1, k)
442  solver%mat(1, k) = 0.0_f64
443 
444  else if (bck(1) == sll_p_polar_origin) then ! center of circular domain
445 
446  ! Gaussian elimination: phi(1) = (-1)^k phi(2)
447  solver%mat(2, k) = solver%mat(2, k) + (-1)**k*solver%mat(1, k)
448  solver%mat(1, k) = 0.0_f64
449 
450  end if
451 
452  !--------------------------------------------
453  ! Set boundary condition at rmax
454  !--------------------------------------------
455  last = 3*(nr - 1)
456  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
457  solver%mat(last, k) = 0.0_f64
458 
459  else if (bck(2) == sll_p_neumann) then ! Neumann
460 
461  ! Coefficients of homogeneous boundary condition
462  hp = r_nodes(nr + 1) - r_nodes(nr)
463  hm = r_nodes(nr) - r_nodes(nr - 1)
464  d1_coeffs(:) = [hp/hm, -2 - hp/hm - hm/hp, 2 + hm/hp]
465  solver%bc_coeffs_rmax(-2:-1) = -d1_coeffs(1:2)/d1_coeffs(3)
466 
467  ! Gaussian elimination: remove phi(last) variable using boundary condition
468  solver%mat(last - 2, k) = solver%mat(last - 2, k) + solver%bc_coeffs_rmax(-2)*solver%mat(last, k)
469  solver%mat(last - 1, k) = solver%mat(last - 1, k) + solver%bc_coeffs_rmax(-1)*solver%mat(last, k)
470  solver%mat(last, k) = 0.0_f64
471 
472  end if
473 
474  end do
475 
476  end subroutine sll_s_poisson_2d_polar_init
477 
478  !=============================================================================
480  subroutine sll_s_poisson_2d_polar_solve(solver, rho, phi)
481  type(sll_t_poisson_2d_polar), intent(inout) :: solver
482  real(f64), intent(in) :: rho(:, :)
483  real(f64), intent(out) :: phi(:, :)
484 
485  integer(i32) :: nr, ntheta, bck(2)
486  integer(i32) :: i, k
487  integer(i32) :: nrpts
488  integer(i32) :: sh
489 
490  nr = solver%nr
491  ntheta = solver%nt
492 
493  ! Shift in radial grid indexing
494  sh = solver%skip0 ! =1 if bc_rmin==sll_p_polar_origin, =0 otherwise
495 
496  ! Number of points in radial grid
497  nrpts = nr + 1 - sh
498 
499  ! Consistency check: 'rho' and 'phi' have shape defined at initialization
500  sll_assert_always(all(shape(rho) == [nrpts, ntheta]))
501  sll_assert_always(all(shape(phi) == [nrpts, ntheta]))
502 
503  ! For each r_i, compute FFT of rho(r_i,theta) to obtain \hat{rho}(r_i,k)
504  do i = 1, nrpts
505  solver%temp_r(:) = rho(i, :)
506  call sll_s_fft_exec_r2c_1d(solver%fw, solver%temp_r(:), solver%temp_c(:))
507  solver%z(i, :) = solver%temp_c(:)
508  end do
509 
510  ! Cycle over k
511  do k = 0, ntheta/2
512 
513  ! rhok(r) is k-th Fourier mode of rho(r,theta)
514  ! phik(r) is k-th Fourier mode of phi(r,theta)
515  ! rhok is 1D contiguous slice (column) of solver%z
516  ! we will overwrite rhok with phik
517  associate(rhok => solver%z(:, k), phik => solver%z(:, k))
518 
519  ! Solve tridiagonal system to obtain \hat{phi}_{k_j}(r) at internal points
520  call sll_s_setup_cyclic_tridiag(solver%mat(:, k), nr - 1, solver%cts, solver%ipiv)
521  call sll_o_solve_cyclic_tridiag(solver%cts, solver%ipiv, rhok(2 - sh:nrpts - 1), nr - 1, phik(2 - sh:nrpts - 1))
522 
523  ! Compute boundary conditions type for mode k
524  bck(:) = solver%bc(:)
525  do i = 1, 2
526  if (bck(i) == sll_p_neumann_mode_0) then
527  if (k == 0) then
528  bck(i) = sll_p_neumann
529  else
530  bck(i) = sll_p_dirichlet
531  end if
532  end if
533  end do
534 
535  ! Boundary condition at rmin
536  if (bck(1) == sll_p_dirichlet) then ! Dirichlet
537  phik(1) = (0.0_f64, 0.0_f64)
538  else if (bck(1) == sll_p_neumann) then ! Neumann
539  associate(c => solver%bc_coeffs_rmin)
540  phik(1) = c(2)*phik(2) + c(3)*phik(3)
541  end associate
542  end if
543 
544  ! Boundary condition at rmax
545  if (bck(2) == sll_p_dirichlet) then ! Dirichlet
546  phik(nrpts) = (0.0_f64, 0.0_f64)
547  else if (bck(2) == sll_p_neumann) then ! Neumann
548  associate(c => solver%bc_coeffs_rmax)
549  phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
550  end associate
551  end if
552 
553  end associate
554 
555  end do
556 
557  ! For each r_i, compute inverse FFT of \hat{phi}(r_i,k) to obtain phi(r_i,theta)
558  do i = 1, nrpts
559  solver%temp_c(:) = solver%z(i, :)
560  call sll_s_fft_exec_c2r_1d(solver%bw, solver%temp_c(:), solver%temp_r(:))
561  phi(i, :) = solver%temp_r(:)
562  end do
563 
564  end subroutine sll_s_poisson_2d_polar_solve
565 
566  !=============================================================================
568  subroutine sll_s_poisson_2d_polar_free(solver)
569  type(sll_t_poisson_2d_polar), intent(inout) :: solver
570 
571  call sll_s_fft_free(solver%fw)
572  call sll_s_fft_free(solver%bw)
573 
574  call sll_s_fft_deallocate_aligned_real(solver%temp_r)
575  call sll_s_fft_deallocate_aligned_complex(solver%temp_c)
576 
577  deallocate (solver%z)
578  deallocate (solver%mat)
579  deallocate (solver%cts)
580  deallocate (solver%ipiv)
581 
582  end subroutine sll_s_poisson_2d_polar_free
583 
584  !=============================================================================
587  rmin, &
588  rmax, &
589  nr, &
590  ntheta, &
591  bc_r, &
592  rgrid) &
593  result(solver_ptr)
594 
595  real(f64), intent(in) :: rmin
596  real(f64), intent(in) :: rmax
597  integer(i32), intent(in) :: nr
598  integer(i32), intent(in) :: ntheta
599  integer(i32), intent(in) :: bc_r(2)
600  real(f64), optional, intent(in) :: rgrid(:)
601 
602  type(sll_t_poisson_2d_polar), pointer :: solver_ptr
603 
604  allocate (solver_ptr)
605  call sll_s_poisson_2d_polar_init(solver_ptr, &
606  rmin, &
607  rmax, &
608  nr, &
609  ntheta, &
610  bc_r(1), &
611  bc_r(2), &
612  rgrid)
613 
614  end function sll_f_new_poisson_2d_polar
615 
616  !=============================================================================
618  subroutine s_compute_phi_from_rho(poisson, phi, rho)
619  class(sll_t_poisson_2d_polar) :: poisson
620  real(f64), intent(out) :: phi(:, :)
621  real(f64), intent(in) :: rho(:, :)
622 
623  ! If last theta point is repeated, discard point and then manually apply
624  ! periodic boundary conditions
625  associate(ntheta => poisson%nt)
626  if (size(phi, 2) == ntheta + 1) then
627  call sll_s_poisson_2d_polar_solve(poisson, &
628  rho(:, 1:ntheta), &
629  phi(:, 1:ntheta))
630  phi(:, ntheta + 1) = phi(:, 1)
631  else
632  call sll_s_poisson_2d_polar_solve(poisson, rho, phi)
633  end if
634  end associate
635 
636  end subroutine s_compute_phi_from_rho
637 
638  !=============================================================================
640  subroutine s_compute_e_from_rho(poisson, E1, E2, rho)
641  class(sll_t_poisson_2d_polar) :: poisson
642  real(f64), intent(out) :: E1(:, :)
643  real(f64), intent(out) :: E2(:, :)
644  real(f64), intent(in) :: rho(:, :)
645  sll_error('sll_t_poisson_2d_polar % compute_E_from_rho', 'NOT IMPLEMENTED')
646  end subroutine s_compute_e_from_rho
647 
648  !=============================================================================
650  function f_l2norm_squared(poisson, coefs_dofs) result(r)
651  class(sll_t_poisson_2d_polar), intent(in) :: poisson
652  real(f64), intent(in) :: coefs_dofs(:, :)
653  real(f64) :: r
654  sll_error('sll_t_poisson_2d_polar % l2norm_squared', 'NOT IMPLEMENTED')
655  r = 0.0_f64
656  end function f_l2norm_squared
657 
658  !=============================================================================
660  subroutine s_compute_rhs_from_function(poisson, func, coefs_dofs)
661  class(sll_t_poisson_2d_polar) :: poisson
662  procedure(sll_i_function_of_position) :: func
663  real(f64), intent(out) :: coefs_dofs(:)
664  sll_error('sll_t_poisson_2d_polar % compute_rhs_from_function', 'NOT IMPLEMENTED')
665  end subroutine s_compute_rhs_from_function
666 
667  !=============================================================================
668  ! OO interface: release memory
669  subroutine s_free(poisson)
670  class(sll_t_poisson_2d_polar) :: poisson
671  call sll_s_poisson_2d_polar_free(poisson)
672  end subroutine s_free
673 
674 end module sll_m_poisson_2d_polar
Solve tridiagonal system (double or complex)
FFT interface for FFTW.
subroutine, public sll_s_fft_exec_r2c_1d(plan, array_in, array_out)
Compute fast Fourier transform in real to complex mode.
subroutine, public sll_s_fft_free(plan)
Delete a plan.
subroutine, public sll_s_fft_init_r2c_1d(plan, nx, array_in, array_out, normalized, aligned, optimization)
Create new 1d real to complex plan for forward FFT.
subroutine, public sll_s_fft_deallocate_aligned_complex(data)
Function to deallocate an aligned complex array.
subroutine, public sll_s_fft_exec_c2r_1d(plan, array_in, array_out)
Compute fast Fourier transform in complex to real mode.
subroutine, public sll_s_fft_init_c2r_1d(plan, nx, array_in, array_out, normalized, aligned, optimization)
Create new 1d complex to real plan for backward FFT.
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_deallocate_aligned_real(data)
Function to deallocate an aligned real array.
complex(c_double_complex) function, dimension(:), pointer, public sll_f_fft_allocate_aligned_complex(n)
Function to allocate an aligned complex array.
Module interface to solve Poisson equation in 2D.
Serial Poisson solver on 2D polar mesh; uses FFT in theta and 2nd-order FD in r.
subroutine s_compute_e_from_rho(poisson, E1, E2, rho)
OO interface: solve Poisson's equation and compute E field.
real(f64) function f_l2norm_squared(poisson, coefs_dofs)
OO interface: compute L2 norm squared of something (...)
subroutine, public sll_s_poisson_2d_polar_init(solver, rmin, rmax, nr, ntheta, bc_rmin, bc_rmax, rgrid)
sll_o_initialize the Poisson solver in polar coordinates
subroutine, public sll_s_poisson_2d_polar_free(solver)
Delete contents (local storage) of Poisson's solver.
type(sll_t_poisson_2d_polar) function, pointer, public sll_f_new_poisson_2d_polar(rmin, rmax, nr, ntheta, bc_r, rgrid)
OO interface: allocate pointer to Poisson solver and initialize it.
subroutine s_compute_phi_from_rho(poisson, phi, rho)
OO interface: solve Poisson's equation.
subroutine s_compute_rhs_from_function(poisson, func, coefs_dofs)
OO interface: project 2D function onto Finite Element space.
subroutine, public sll_s_poisson_2d_polar_solve(solver, rho, phi)
Solve the Poisson equation and get the electrostatic potential.
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