210 #include "sll_assert.h"
211 #include "sll_errors.h"
212 #include "sll_working_precision.h"
220 sll_p_neumann_mode_0, &
260 integer(i32) :: bc(2)
261 real(f64) :: epsilon_0
262 real(f64),
allocatable :: g(:)
266 complex(f64),
allocatable :: z(:, :)
267 complex(f64),
pointer :: temp_c(:)
268 real(f64),
pointer :: temp_r(:)
269 real(f64),
allocatable :: mat(:, :)
270 real(f64),
allocatable :: cts(:)
271 integer(i32),
allocatable :: ipiv(:)
273 real(f64) :: bc_coeffs_rmin(2:3)
274 real(f64) :: bc_coeffs_rmax(-2:-1)
300 real(f64),
intent(in) :: rmin
301 real(f64),
intent(in) :: rmax
302 integer(i32),
intent(in) :: nr
303 integer(i32),
intent(in) :: ntheta
304 integer(i32),
intent(in) :: bc_rmin
305 integer(i32),
intent(in) :: bc_rmax
306 real(f64),
intent(in) :: rho_m0(:)
307 real(f64),
intent(in) :: b_magn(:)
308 real(f64),
optional,
intent(in) :: lambda(:)
309 logical,
optional,
intent(in) :: use_zonal_flow
310 real(f64),
optional,
intent(in) :: epsilon_0
311 real(f64),
target,
optional,
intent(in) :: rgrid(:)
313 character(len=*),
parameter :: this_sub_name =
'sll_s_qn_solver_2d_polar_init'
317 real(f64) :: d0_coeffs(3)
318 real(f64) :: d1_coeffs(3)
319 real(f64) :: d2_coeffs(3)
320 real(f64) :: ddr_ln_g
322 real(f64),
allocatable :: r_nodes(:)
325 integer(i32) :: bck(2)
329 sll_assert_always(rmin >= 0.0_f64)
330 sll_assert_always(rmin < rmax)
331 sll_assert_always(nr >= 1)
332 sll_assert_always(ntheta >= 1)
334 if (bc_rmin == sll_p_polar_origin .and. rmin /= 0.0_f64)
then
335 sll_error(this_sub_name,
"BC option 'sll_p_polar_origin' requires r_min = 0")
339 select case (bc_rmin)
340 case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0, sll_p_polar_origin)
341 solver%bc(1) = bc_rmin
343 sll_error(this_sub_name,
'Unrecognized boundary condition at r_min')
347 select case (bc_rmax)
348 case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
349 solver%bc(2) = bc_rmax
351 sll_error(this_sub_name,
'Unrecognized boundary condition at r_max')
355 if (
present(epsilon_0))
then
356 solver%epsilon_0 = epsilon_0
369 solver%skip0 = merge(1, 0, bc_rmin == sll_p_polar_origin)
372 allocate (r_nodes(nr + 1))
374 if (
present(rgrid))
then
376 sll_assert_always(all(rgrid > 0.0_f64))
377 if (bc_rmin == sll_p_polar_origin)
then
378 sll_assert_always(
size(rgrid) == nr)
379 sll_assert_always(rgrid(nr) == rmax)
380 r_nodes(1) = -rgrid(1)
381 r_nodes(2:) = rgrid(:)
383 sll_assert_always(
size(rgrid) == nr + 1)
384 sll_assert_always(rgrid(1) == rmin)
385 sll_assert_always(rgrid(nr + 1) == rmax)
386 r_nodes(:) = rgrid(:)
391 if (bc_rmin == sll_p_polar_origin)
then
392 associate(rmin => rmax/real(2*nr + 1, f64))
403 associate(sh => solver%skip0)
404 allocate (solver%g(nr + 1))
405 allocate (solver%z(nr + 1 - sh, 0:ntheta/2))
406 allocate (solver%mat((nr - 1)*3, 0:ntheta/2))
407 allocate (solver%cts((nr - 1)*7))
408 allocate (solver%ipiv(nr - 1))
412 solver%temp_r => sll_f_fft_allocate_aligned_real(ntheta)
413 solver%temp_c => sll_f_fft_allocate_aligned_complex(ntheta/2 + 1)
416 call sll_s_fft_init_r2c_1d(solver%fw, &
423 call sll_s_fft_init_c2r_1d(solver%bw, &
431 associate(sh => solver%skip0)
432 solver%g(1 + sh:) = rho_m0(:)/(b_magn(:)**2*solver%epsilon_0)
434 solver%g(1) = solver%g(2)
445 bck(:) = solver%bc(:)
447 if (bck(i) == sll_p_neumann_mode_0)
then
449 bck(i) = sll_p_neumann
451 bck(i) = sll_p_dirichlet
462 hp = r_nodes(i + 1) - r_nodes(i)
463 hm = r_nodes(i) - r_nodes(i - 1)
464 inv_r = 1.0_f64/r_nodes(i)
465 d0_coeffs(:) = [0.0_f64, 1.0_f64, 0.0_f64]
466 d1_coeffs(:) = [-hp/hm, (hp**2 - hm**2)/(hp*hm), hm/hp]/(hp + hm)
467 d2_coeffs(:) = [2*hp/(hp + hm), -2.0_f64, 2*hm/(hp + hm)]/(hp*hm)
470 ddr_ln_g = dot_product(d1_coeffs(:), solver%g(i - 1:i + 1))/solver%g(i)
473 if (
present(lambda))
then
474 associate(sh => solver%skip0)
475 c = 1.0_f64/(lambda(i - sh)**2*solver%g(i))
477 if (
present(use_zonal_flow))
then
478 if (use_zonal_flow .and. k == 0)
then
487 solver%mat(3*(i - 1) - 2:3*(i - 1), k) = &
489 - d1_coeffs(:)*(inv_r + ddr_ln_g) &
490 + d0_coeffs(:)*((k*inv_r)**2 + c)
497 if (bck(1) == sll_p_dirichlet)
then
498 solver%mat(1, k) = 0.0_f64
500 else if (bck(1) == sll_p_neumann)
then
503 hp = r_nodes(3) - r_nodes(2)
504 hm = r_nodes(2) - r_nodes(1)
505 d1_coeffs(:) = [-2 - hp/hm, 2 + hp/hm + hm/hp, -hm/hp]
506 solver%bc_coeffs_rmin(2:3) = -d1_coeffs(2:3)/d1_coeffs(1)
509 solver%mat(3, k) = solver%mat(3, k) + solver%bc_coeffs_rmin(3)*solver%mat(1, k)
510 solver%mat(2, k) = solver%mat(2, k) + solver%bc_coeffs_rmin(2)*solver%mat(1, k)
511 solver%mat(1, k) = 0.0_f64
513 else if (bck(1) == sll_p_polar_origin)
then
516 solver%mat(2, k) = solver%mat(2, k) + (-1)**k*solver%mat(1, k)
517 solver%mat(1, k) = 0.0_f64
525 if (bck(2) == sll_p_dirichlet)
then
526 solver%mat(last, k) = 0.0_f64
528 else if (bck(2) == sll_p_neumann)
then
531 hp = r_nodes(nr + 1) - r_nodes(nr)
532 hm = r_nodes(nr) - r_nodes(nr - 1)
533 d1_coeffs(:) = [hp/hm, -2 - hp/hm - hm/hp, 2 + hm/hp]
534 solver%bc_coeffs_rmax(-2:-1) = -d1_coeffs(1:2)/d1_coeffs(3)
537 solver%mat(last - 2, k) = solver%mat(last - 2, k) + solver%bc_coeffs_rmax(-2)*solver%mat(last, k)
538 solver%mat(last - 1, k) = solver%mat(last - 1, k) + solver%bc_coeffs_rmax(-1)*solver%mat(last, k)
539 solver%mat(last, k) = 0.0_f64
551 real(f64),
intent(in) :: rho(:, :)
552 real(f64),
intent(out) :: phi(:, :)
554 integer(i32) :: nr, ntheta, bck(2)
556 integer(i32) :: nrpts
569 sll_assert_always(all(shape(rho) == [nrpts, ntheta]))
570 sll_assert_always(all(shape(phi) == [nrpts, ntheta]))
574 solver%temp_r(:) = rho(i, :)
575 call sll_s_fft_exec_r2c_1d(solver%fw, solver%temp_r(:), solver%temp_c(:))
576 solver%z(i, :) = solver%temp_c(:)
586 associate(rhok => solver%z(:, k), phik => solver%z(:, k))
588 rhok(:) = rhok(:)/(solver%g(1 + sh:)*solver%epsilon_0)
591 call sll_s_setup_cyclic_tridiag(solver%mat(:, k), nr - 1, solver%cts, solver%ipiv)
592 call sll_o_solve_cyclic_tridiag(solver%cts, solver%ipiv, &
593 rhok(2 - sh:nrpts - 1), nr - 1, phik(2 - sh:nrpts - 1))
596 bck(:) = solver%bc(:)
598 if (bck(i) == sll_p_neumann_mode_0)
then
600 bck(i) = sll_p_neumann
602 bck(i) = sll_p_dirichlet
608 if (bck(1) == sll_p_dirichlet)
then
609 phik(1) = (0.0_f64, 0.0_f64)
610 else if (bck(1) == sll_p_neumann)
then
611 associate(c => solver%bc_coeffs_rmin)
612 phik(1) = c(2)*phik(2) + c(3)*phik(3)
617 if (bck(2) == sll_p_dirichlet)
then
618 phik(nrpts) = (0.0_f64, 0.0_f64)
619 else if (bck(2) == sll_p_neumann)
then
620 associate(c => solver%bc_coeffs_rmax)
621 phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
631 solver%temp_c(:) = solver%z(i, :)
632 call sll_s_fft_exec_c2r_1d(solver%bw, solver%temp_c(:), solver%temp_r(:))
633 phi(i, :) = solver%temp_r(:)
643 call sll_s_fft_free(solver%fw)
644 call sll_s_fft_free(solver%bw)
646 call sll_s_fft_deallocate_aligned_real(solver%temp_r)
647 call sll_s_fft_deallocate_aligned_complex(solver%temp_c)
649 deallocate (solver%z)
650 deallocate (solver%mat)
651 deallocate (solver%cts)
652 deallocate (solver%ipiv)
Solve tridiagonal system (double or complex)
Describe different boundary conditions.
Fortran module where set some physical and mathematical constants.
real(kind=f64), parameter, public sll_p_epsilon_0
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.
Serial 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_solve(solver, rho, phi)
Solve the quasi-neutrality equation and get the electrostatic potential.
subroutine, public sll_s_qn_solver_2d_polar_free(solver)
Delete contents (local storage) of quasi-neutrality solver.
subroutine, public sll_s_qn_solver_2d_polar_init(solver, rmin, rmax, nr, ntheta, bc_rmin, bc_rmax, rho_m0, b_magn, lambda, use_zonal_flow, epsilon_0, rgrid)
Initialize the 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 2D Poisson solver in polar coordinates.