221 #include "sll_assert.h"
222 #include "sll_errors.h"
223 #include "sll_working_precision.h"
231 sll_p_neumann_mode_0, &
250 use sll_m_remapper,
only: &
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, &
284 integer(i32) :: ntheta
285 integer(i32) :: bc(2)
286 real(f64) :: epsilon_0
287 real(f64),
allocatable :: g(:)
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(:)
299 real(f64) :: bc_coeffs_rmin(2:3)
300 real(f64) :: bc_coeffs_rmax(-2:-1)
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
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(:)
348 character(len=*),
parameter :: this_sub_name =
'sll_s_qn_solver_2d_polar_par_init'
352 real(f64) :: d0_coeffs(3)
353 real(f64) :: d1_coeffs(3)
354 real(f64) :: d2_coeffs(3)
355 real(f64) :: ddr_ln_g
357 real(f64),
allocatable :: r_nodes(:)
359 integer(i32) :: i, j, k
360 integer(i32) :: bck(2)
364 integer(i32) :: loc_sz_r(2)
365 integer(i32) :: loc_sz_a(2)
366 integer(i32) :: glob_idx(2)
368 integer(i32),
allocatable :: k_list_glob(:)
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")
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
379 sll_error(this_sub_name,
'Unrecognized boundary condition at r_min')
383 select case (bc_rmax)
384 case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
385 solver%bc(2) = bc_rmax
387 sll_error(this_sub_name,
'Unrecognized boundary condition at r_max')
392 sh = merge(1, 0, bc_rmin == sll_p_polar_origin)
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)
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)
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))
406 sll_assert_always(loc_sz_r(1) == nr + 1 - sh)
407 sll_assert_always(loc_sz_a(2) == ntheta)
410 if (
present(epsilon_0))
then
411 solver%epsilon_0 = epsilon_0
420 solver%ntheta = ntheta
421 solver%layout_a => layout_a
422 solver%layout_r => layout_r
426 allocate (r_nodes(nr + 1))
428 if (
present(rgrid))
then
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(:)
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(:)
445 if (bc_rmin == sll_p_polar_origin)
then
446 associate(rmin => rmax/real(2*nr + 1, f64))
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)))
460 allocate (solver%cts((nr - 1)*7))
461 allocate (solver%ipiv(nr - 1))
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)
490 solver%g(1 + sh:) = rho_m0(:)/(b_magn(:)**2*solver%epsilon_0)
492 solver%g(1) = solver%g(2)
496 allocate (k_list_glob(ntheta))
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))
504 deallocate (k_list_glob)
508 do j = 1, loc_sz_r(2)
516 bck(:) = solver%bc(:)
518 if (bck(i) == sll_p_neumann_mode_0)
then
520 bck(i) = sll_p_neumann
522 bck(i) = sll_p_dirichlet
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)
541 ddr_ln_g = dot_product(d1_coeffs(:), solver%g(i - 1:i + 1))/solver%g(i)
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
556 solver%mat(3*(i - 1) - 2:3*(i - 1), j) = &
558 - d1_coeffs(:)*(inv_r + ddr_ln_g) &
559 + d0_coeffs(:)*((k*inv_r)**2 + c)
566 if (bck(1) == sll_p_dirichlet)
then
567 solver%mat(1, j) = 0.0_f64
569 else if (bck(1) == sll_p_neumann)
then
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)
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
582 else if (bck(1) == sll_p_polar_origin)
then
585 solver%mat(2, j) = solver%mat(2, j) + (-1)**k*solver%mat(1, j)
586 solver%mat(1, j) = 0.0_f64
594 if (bck(2) == sll_p_dirichlet)
then
595 solver%mat(last, j) = 0.0_f64
597 else if (bck(2) == sll_p_neumann)
then
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)
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
620 real(f64),
intent(in) :: rho(:, :)
621 real(f64),
target,
intent(out) :: phi(:, :)
623 integer(i32) :: nr, ntheta, bck(2)
624 integer(i32) :: i, j, k
625 integer(i32) :: nrpts
629 ntheta = solver%ntheta
642 solver%z_a => phi(:, :)
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(:)
652 call sll_o_apply_remap_2d(solver%rmp_ar, solver%z_a, solver%z_r)
655 do j = 1, ubound(solver%z_r, 2)
661 associate(rhok => solver%z_r(:, j), phik => solver%z_r(:, j))
663 rhok(:) = rhok(:)/(solver%g(1 + sh:)*solver%epsilon_0)
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))
674 bck(:) = solver%bc(:)
676 if (bck(i) == sll_p_neumann_mode_0)
then
678 bck(i) = sll_p_neumann
680 bck(i) = sll_p_dirichlet
686 if (bck(1) == sll_p_dirichlet)
then
688 else if (bck(1) == sll_p_neumann)
then
689 associate(c => solver%bc_coeffs_rmin)
690 phik(1) = c(2)*phik(2) + c(3)*phik(3)
695 if (bck(2) == sll_p_dirichlet)
then
696 phik(nrpts) = 0.0_f64
697 else if (bck(2) == sll_p_neumann)
then
698 associate(c => solver%bc_coeffs_rmax)
699 phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
708 call sll_o_apply_remap_2d(solver%rmp_ra, solver%z_r, solver%z_a)
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(:)
724 call sll_s_fft_free(solver%fw)
725 call sll_s_fft_free(solver%bw)
727 call sll_s_fft_deallocate_aligned_real(solver%tmp)
729 deallocate (solver%z_r)
730 deallocate (solver%mat)
731 deallocate (solver%cts)
732 deallocate (solver%ipiv)
734 deallocate (solver%rmp_ra)
735 deallocate (solver%rmp_ar)
737 solver%layout_r => null()
738 solver%layout_a => null()
739 solver%rmp_ra => null()
740 solver%rmp_ar => null()
747 type(sll_t_layout_2d),
pointer :: layout
748 real(f64),
intent(in) :: array(:, :)
749 character(len=*),
intent(in) :: array_name
752 character(len=*),
parameter :: sfmt =
"('['(i0)','(i0)']')"
753 character(len=256) :: err_fmt
754 character(len=256) :: err_msg
756 call sll_o_compute_local_sizes(layout, n(1), n(2))
758 if (.not. all(shape(array) == n(:)))
then
760 err_fmt =
"(a,"//sfmt//
",a,"//sfmt//
",a)"
762 write (err_msg, err_fmt) &
763 "shape("//array_name//
") = ", shape(array), &
764 " is not compatible with local shape ", n,
" of 2D layout"
766 sll_error(
"sll_s_qn_solver_2d_polar_par_solve", trim(err_msg))
Solve tridiagonal system (double or complex)
Describe different boundary conditions.
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
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.
Class for the Poisson solver in polar coordinate.