195 #include "sll_assert.h"
196 #include "sll_errors.h"
197 #include "sll_working_precision.h"
202 sll_p_neumann_mode_0, &
221 use sll_m_remapper,
only: &
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, &
255 integer(i32) :: ntheta
256 integer(i32) :: bc(2)
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(:)
268 real(f64) :: bc_coeffs_rmin(2:3)
269 real(f64) :: bc_coeffs_rmax(-2:-1)
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
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(:)
307 character(len=*),
parameter :: this_sub_name =
'sll_s_poisson_2d_polar_par_init'
311 real(f64) :: d1_coeffs(3)
312 real(f64) :: d2_coeffs(3)
313 real(f64),
allocatable :: r_nodes(:)
315 integer(i32) :: i, j, k
316 integer(i32) :: bck(2)
320 integer(i32) :: loc_sz_r(2)
321 integer(i32) :: loc_sz_a(2)
322 integer(i32) :: glob_idx(2)
324 integer(i32),
allocatable :: k_list_glob(:)
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)
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")
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
341 sll_error(this_sub_name,
'Unrecognized boundary condition at r_min')
345 select case (bc_rmax)
346 case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
347 solver%bc(2) = bc_rmax
349 sll_error(this_sub_name,
'Unrecognized boundary condition at r_max')
354 sh = merge(1, 0, bc_rmin == sll_p_polar_origin)
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)
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)
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))
368 sll_assert_always(loc_sz_r(1) == nr + 1 - sh)
369 sll_assert_always(loc_sz_a(2) == ntheta)
375 solver%ntheta = ntheta
376 solver%layout_a => layout_a
377 solver%layout_r => layout_r
381 allocate (r_nodes(nr + 1))
383 if (
present(rgrid))
then
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(:)
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(:)
400 if (bc_rmin == sll_p_polar_origin)
then
401 associate(rmin => rmax/real(2*nr + 1, f64))
412 allocate (solver%z_r(nr + 1 + sh, loc_sz_r(2)))
413 allocate (solver%mat((nr - 1)*3, loc_sz_r(2)))
414 allocate (solver%cts((nr - 1)*7))
415 allocate (solver%ipiv(nr - 1))
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)
444 allocate (k_list_glob(ntheta))
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))
452 deallocate (k_list_glob)
456 do j = 1, loc_sz_r(2)
464 bck(:) = solver%bc(:)
466 if (bck(i) == sll_p_neumann_mode_0)
then
468 bck(i) = sll_p_neumann
470 bck(i) = sll_p_dirichlet
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)
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]
492 if (bck(1) == sll_p_dirichlet)
then
493 solver%mat(1, j) = 0.0_f64
495 else if (bck(1) == sll_p_neumann)
then
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)
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
508 else if (bck(1) == sll_p_polar_origin)
then
511 solver%mat(2, j) = solver%mat(2, j) + (-1)**k*solver%mat(1, j)
512 solver%mat(1, j) = 0.0_f64
520 if (bck(2) == sll_p_dirichlet)
then
521 solver%mat(last, j) = 0.0_f64
523 else if (bck(2) == sll_p_neumann)
then
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)
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
546 real(f64),
intent(in) :: rho(:, :)
547 real(f64),
target,
intent(out) :: phi(:, :)
549 integer(i32) :: nr, ntheta, bck(2)
550 integer(i32) :: i, j, k
551 integer(i32) :: nrpts
555 ntheta = solver%ntheta
568 solver%z_a => phi(:, :)
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(:)
578 call sll_o_apply_remap_2d(solver%rmp_ar, solver%z_a, solver%z_r)
581 do j = 1, ubound(solver%z_r, 2)
587 associate(rhok => solver%z_r(:, j), phik => solver%z_r(:, j))
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))
597 bck(:) = solver%bc(:)
599 if (bck(i) == sll_p_neumann_mode_0)
then
601 bck(i) = sll_p_neumann
603 bck(i) = sll_p_dirichlet
609 if (bck(1) == sll_p_dirichlet)
then
611 else if (bck(1) == sll_p_neumann)
then
612 associate(c => solver%bc_coeffs_rmin)
613 phik(1) = c(2)*phik(2) + c(3)*phik(3)
618 if (bck(2) == sll_p_dirichlet)
then
619 phik(nrpts) = 0.0_f64
620 else if (bck(2) == sll_p_neumann)
then
621 associate(c => solver%bc_coeffs_rmax)
622 phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
631 call sll_o_apply_remap_2d(solver%rmp_ra, solver%z_r, solver%z_a)
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(:)
647 call sll_s_fft_free(solver%fw)
648 call sll_s_fft_free(solver%bw)
650 call sll_s_fft_deallocate_aligned_real(solver%tmp)
652 deallocate (solver%z_r)
653 deallocate (solver%mat)
654 deallocate (solver%cts)
655 deallocate (solver%ipiv)
657 deallocate (solver%rmp_ra)
658 deallocate (solver%rmp_ar)
660 solver%layout_r => null()
661 solver%layout_a => null()
662 solver%rmp_ra => null()
663 solver%rmp_ar => null()
670 type(sll_t_layout_2d),
pointer :: layout
671 real(f64),
intent(in) :: array(:, :)
672 character(len=*),
intent(in) :: array_name
675 character(len=*),
parameter :: sfmt =
"('['(i0)','(i0)']')"
676 character(len=256) :: err_fmt
677 character(len=256) :: err_msg
679 call sll_o_compute_local_sizes(layout, n(1), n(2))
681 if (.not. all(shape(array) == n(:)))
then
683 err_fmt =
"(a,"//sfmt//
",a,"//sfmt//
",a)"
685 write (err_msg, err_fmt) &
686 "shape("//array_name//
") = ", shape(array), &
687 " is not compatible with local shape ", n,
" of 2D layout"
689 sll_error(
"sll_s_poisson_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.
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.