184 #include "sll_assert.h"
185 #include "sll_errors.h"
186 #include "sll_working_precision.h"
191 sll_p_neumann_mode_0, &
238 integer(i32) :: bc(2)
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(:)
249 real(f64) :: bc_coeffs_rmin(2:3)
250 real(f64) :: bc_coeffs_rmax(-2:-1)
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(:)
287 character(len=*),
parameter :: this_sub_name =
'sll_s_poisson_2d_polar_init'
291 real(f64) :: d1_coeffs(3)
292 real(f64) :: d2_coeffs(3)
293 real(f64),
allocatable :: r_nodes(:)
296 integer(i32) :: bck(2)
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)
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")
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
314 sll_error(this_sub_name,
'Unrecognized boundary condition at r_min')
318 select case (bc_rmax)
319 case (sll_p_dirichlet, sll_p_neumann, sll_p_neumann_mode_0)
320 solver%bc(2) = bc_rmax
322 sll_error(this_sub_name,
'Unrecognized boundary condition at r_max')
333 solver%skip0 = merge(1, 0, bc_rmin == sll_p_polar_origin)
336 allocate (r_nodes(nr + 1))
338 if (
present(rgrid))
then
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(:)
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(:)
355 if (bc_rmin == sll_p_polar_origin)
then
356 associate(rmin => rmax/real(2*nr + 1, f64))
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))
370 allocate (solver%cts((nr - 1)*7))
371 allocate (solver%ipiv(nr - 1))
375 solver%temp_r => sll_f_fft_allocate_aligned_real(ntheta)
376 solver%temp_c => sll_f_fft_allocate_aligned_complex(ntheta/2 + 1)
379 call sll_s_fft_init_r2c_1d(solver%fw, &
386 call sll_s_fft_init_c2r_1d(solver%bw, &
400 bck(:) = solver%bc(:)
402 if (bck(i) == sll_p_neumann_mode_0)
then
404 bck(i) = sll_p_neumann
406 bck(i) = sll_p_dirichlet
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)
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]
428 if (bck(1) == sll_p_dirichlet)
then
429 solver%mat(1, k) = 0.0_f64
431 else if (bck(1) == sll_p_neumann)
then
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)
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
444 else if (bck(1) == sll_p_polar_origin)
then
447 solver%mat(2, k) = solver%mat(2, k) + (-1)**k*solver%mat(1, k)
448 solver%mat(1, k) = 0.0_f64
456 if (bck(2) == sll_p_dirichlet)
then
457 solver%mat(last, k) = 0.0_f64
459 else if (bck(2) == sll_p_neumann)
then
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)
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
482 real(f64),
intent(in) :: rho(:, :)
483 real(f64),
intent(out) :: phi(:, :)
485 integer(i32) :: nr, ntheta, bck(2)
487 integer(i32) :: nrpts
500 sll_assert_always(all(shape(rho) == [nrpts, ntheta]))
501 sll_assert_always(all(shape(phi) == [nrpts, ntheta]))
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(:)
517 associate(rhok => solver%z(:, k), phik => solver%z(:, k))
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))
524 bck(:) = solver%bc(:)
526 if (bck(i) == sll_p_neumann_mode_0)
then
528 bck(i) = sll_p_neumann
530 bck(i) = sll_p_dirichlet
536 if (bck(1) == sll_p_dirichlet)
then
537 phik(1) = (0.0_f64, 0.0_f64)
538 else if (bck(1) == sll_p_neumann)
then
539 associate(c => solver%bc_coeffs_rmin)
540 phik(1) = c(2)*phik(2) + c(3)*phik(3)
545 if (bck(2) == sll_p_dirichlet)
then
546 phik(nrpts) = (0.0_f64, 0.0_f64)
547 else if (bck(2) == sll_p_neumann)
then
548 associate(c => solver%bc_coeffs_rmax)
549 phik(nrpts) = c(-2)*phik(nrpts - 2) + c(-1)*phik(nrpts - 1)
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(:)
571 call sll_s_fft_free(solver%fw)
572 call sll_s_fft_free(solver%bw)
574 call sll_s_fft_deallocate_aligned_real(solver%temp_r)
575 call sll_s_fft_deallocate_aligned_complex(solver%temp_c)
577 deallocate (solver%z)
578 deallocate (solver%mat)
579 deallocate (solver%cts)
580 deallocate (solver%ipiv)
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(:)
604 allocate (solver_ptr)
620 real(f64),
intent(out) :: phi(:, :)
621 real(f64),
intent(in) :: rho(:, :)
625 associate(ntheta => poisson%nt)
626 if (
size(phi, 2) == ntheta + 1)
then
630 phi(:, ntheta + 1) = phi(:, 1)
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')
652 real(f64),
intent(in) :: coefs_dofs(:, :)
654 sll_error(
'sll_t_poisson_2d_polar % l2norm_squared',
'NOT IMPLEMENTED')
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')
Solve tridiagonal system (double or complex)
Describe different boundary conditions.
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_free(poisson)
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.