3#include "../afivo/src/cpp_macros.h"
15 real(dp) :: frac_in_tbl
22 logical,
protected :: phmc_const_dx = .true.
25 real(dp),
protected :: phmc_min_dx = 1e-9_dp
28 real(dp),
protected :: phmc_absorp_fac = 0.25_dp
31 integer,
protected :: phmc_num_photons = 5000*1000
34 real(dp),
protected :: phmc_min_weight = 1.0_dp
55 type(cfg_t),
intent(inout) :: cfg
56 logical,
intent(in) :: is_used
61 "Whether physical photons are used")
62 call cfg_add_get(cfg,
"photoi_mc%min_weight", phmc_min_weight, &
63 "Minimal photon weight (default: 1.0)")
64 call cfg_add_get(cfg,
"photoi_mc%const_dx", phmc_const_dx, &
65 "Whether a constant grid spacing is used for photoionization")
66 call cfg_add_get(cfg,
"photoi_mc%min_dx", phmc_min_dx, &
67 "Minimum grid spacing for photoionization")
68 call cfg_add_get(cfg,
"photoi_mc%absorp_fac", phmc_absorp_fac, &
69 "At which grid spacing photons are absorbed compared to their mean distance")
70 call cfg_add_get(cfg,
"photoi_mc%num_photons", phmc_num_photons, &
71 "Maximum number of discrete photons to use")
74 if (phmc_absorp_fac <= 0.0_dp) error stop
"photoi_mc%absorp_fac <= 0.0"
75 if (phmc_num_photons < 1) error stop
"photoi_mc%num_photons < 1"
80 if (ix == -1) error stop
"Photoionization: no oxygen present"
87 if (.not. phmc_const_dx) &
88 error stop
"phmc_initialize: with dielectric use const_dx"
89 if (phmc_absorp_fac > 1e-9_dp) &
90 error stop
"Use phmc_absorp_fac <= 1e-9 with dielectric"
98 real(dp),
intent(in) :: dr_base
99 real(dp) :: lengthscale, dx
103 lengthscale = lt_get_col(
phmc_tbl%tbl, 1, phmc_absorp_fac)
107 lvl = get_lvl_length(dr_base, lengthscale)
108 dx = dr_base * (0.5_dp**(lvl-1))
110 if (phmc_const_dx)
then
111 write(*,
"(A,E12.3)")
" Monte-Carlo photoionization spacing: ", dx
113 write(*,
"(A)")
" Monte-Carlo photoionization uses adaptive spacing"
126 real(dp),
intent(in) :: p_o2
128 real(dp),
intent(in) :: max_dist
131 integer,
parameter :: tbl_size = 500
132 real(dp),
allocatable :: fsum(:), dist(:)
133 real(dp) :: df, drdf, r, f, fmax_guess
143 df = fmax_guess / (tbl_size-1)
148 drdf = rk4_drdf(r, df, p_o2)
152 if (r > max_dist)
then
161 allocate(fsum(2 * tbl_size))
162 allocate(dist(2 * tbl_size))
165 df = fmax_guess / (tbl_size-1)
170 do n = 2, 2 * tbl_size
171 drdf = rk4_drdf(dist(n-1), df, p_o2)
172 fsum(n) = fsum(n-1) + df
173 dist(n) = dist(n-1) + df * drdf
174 if (dist(n) > max_dist)
exit
177 if (n > tbl_size + 10) &
178 stop
"phmc_get_table_air: integration accuracy fail"
180 if (st_use_dielectric)
then
187 fsum(1:n-1) = fsum(1:n-1) / fsum(n-1)
190 phmc_tbl%tbl = lt_create(0.0_dp, 1.0_dp, tbl_size, 1)
191 call lt_set_col(
phmc_tbl%tbl, 1, fsum(1:n-1), dist(1:n-1))
193 write(*,
"(A,E12.3,A)")
" Average photon absorption length", &
194 1e3_dp * sum(dist(1:n-1))/(n-1),
" mm"
199 real(dp) function rk4_drdf(r, df, p_o2)
200 real(dp),
intent(in) :: r
201 real(dp),
intent(in) :: df
202 real(dp),
intent(in) :: p_o2
205 real(dp),
parameter :: one_sixth = 1 / 6.0_dp
213 sum_drdf = sum_drdf + 2 * drdf
217 sum_drdf = sum_drdf + 2 * drdf
221 sum_drdf = sum_drdf + drdf
224 rk4_drdf = one_sixth * sum_drdf
225 end function rk4_drdf
230 real(dp),
intent(in) :: dist
231 real(dp),
intent(in) :: p_o2
235 real(dp),
parameter :: eps = epsilon(1.0_dp)
238 if (r * (c0 + c1) < eps)
then
242 else if (r * c0 > -log(eps))
then
251 integer function get_lvl_length(dr_base, length)
252 real(dp),
intent(in) :: dr_base
253 real(dp),
intent(in) :: length
254 real(dp),
parameter :: invlog2 = 1 / log(2.0_dp)
257 ratio = dr_base / length
261 get_lvl_length = 1 + ceiling(log(ratio) * invlog2)
263 end function get_lvl_length
266 integer function get_rlvl_length(dr_base, length, rng)
268 real(dp),
intent(in) :: dr_base
269 real(dp),
intent(in) :: length
270 type(rng_t),
intent(inout) :: rng
271 real(dp),
parameter :: invlog2 = 1 / log(2.0_dp)
272 real(dp) :: ratio, tmp
274 ratio = dr_base / length
278 tmp = log(ratio) * invlog2
279 get_rlvl_length = floor(tmp)
280 if (rng%unif_01() < tmp - get_rlvl_length) &
281 get_rlvl_length = get_rlvl_length + 1
283 end function get_rlvl_length
291 integer,
intent(in) :: n_photons
293 real(dp),
intent(in) :: xyz_in(3, n_photons)
295 real(dp),
intent(out) :: xyz_out(3, n_photons)
296 integer,
intent(in) :: n_dim
298 type(lt_t),
intent(in) :: tbl
299 type(prng_t),
intent(inout) :: prng
300 integer :: n, proc_id
304 proc_id = 1+omp_get_thread_num()
309 rr = prng%rngs(proc_id)%unif_01()
310 dist = lt_get_col(tbl, 1, rr)
312 xyz_out(1:3, n) = xyz_in(1:3, n) + &
313 prng%rngs(proc_id)%sphere(dist)
316 else if (n_dim == 3)
then
319 rr = prng%rngs(proc_id)%unif_01()
320 dist = lt_get_col(tbl, 1, rr)
321 xyz_out(:, n) = xyz_in(:, n) + prng%rngs(proc_id)%sphere(dist)
325 print *,
"phmc_do_absorption: unknown n_dim", n_dim
385 type(af_t),
intent(inout) :: tree
386 type(rng_t),
intent(inout) :: rng
388 integer,
intent(in) :: i_src
390 integer,
intent(in) :: i_photo
392 logical,
intent(in) :: use_cyl
394 real(dp),
intent(in),
optional :: dt
396 integer :: lvl, id, nc, min_lvl
397 integer :: ijk, n, n_used
398 integer :: proc_id, n_procs
399 integer :: pho_lvl, max_num_photons
400 real(dp) :: dr(ndim), dt_fac, dist, n_produced
401 real(dp) :: sum_production_rate, pi_lengthscale
402 real(dp),
allocatable :: xyz_src(:, :)
403 real(dp),
allocatable :: xyz_abs(:, :)
405 real(dp) :: tmp, r(3)
406 real(dp),
parameter :: pi = acos(-1.0_dp)
409 type(af_loc_t),
allocatable :: ph_loc(:)
410 real(dp),
parameter :: small_value = 1e-100_dp
412 if (ndim == 3 .and. use_cyl) error stop
"phmc_set_src: use_cyl is .true."
414 if (st_use_dielectric)
then
421 n_procs = omp_get_max_threads()
422 call prng%init_parallel(n_procs, rng)
425 call af_tree_sum_cc(tree, i_src, sum_production_rate)
431 if (
present(dt))
then
434 n_produced = dt * sum_production_rate / phmc_min_weight
436 if (n_produced < phmc_num_photons)
then
437 dt_fac = dt/phmc_min_weight
439 dt_fac = phmc_num_photons / (sum_production_rate + small_value)
443 dt_fac = phmc_num_photons / (sum_production_rate + small_value)
447 max_num_photons = nint(1.2_dp * dt_fac * sum_production_rate + 1000)
449 call phmc_generate_photons(tree, dt_fac, i_src, xyz_src, &
450 max_num_photons, n_used, prng)
452 allocate(xyz_abs(3, n_used))
462 xyz_abs(1, n) = sqrt(xyz_abs(1, n)**2 + xyz_abs(3, n)**2)
463 xyz_abs(2, n) = xyz_abs(2, n)
467 if (st_use_dielectric)
then
470 2, n_used, 1.0_dp/dt_fac)
476 if (st_use_dielectric)
then
479 ndim, n_used, 1.0_dp/dt_fac)
483 allocate(ph_loc(n_used))
485 if (phmc_const_dx)
then
487 pi_lengthscale = lt_get_col(
phmc_tbl%tbl, 1, phmc_absorp_fac)
491 pho_lvl = get_lvl_length(maxval(tree%dr_base), pi_lengthscale)
495 ph_loc(n) = af_get_loc(tree, xyz_abs(1:ndim, n), pho_lvl)
500 proc_id = 1+omp_get_thread_num()
503 dist = phmc_absorp_fac * norm2(xyz_abs(1:ndim, n) - xyz_src(1:ndim, n))
504 if (dist < phmc_min_dx) dist = phmc_min_dx
505 lvl = get_rlvl_length(maxval(tree%dr_base), dist, prng%rngs(proc_id))
506 ph_loc(n) = af_get_loc(tree, xyz_abs(1:ndim, n), lvl)
513 call af_tree_clear_cc(tree, i_photo)
517 if (id > af_no_box)
then
520 dr = tree%boxes(id)%dr
522 tree%boxes(id)%cc(ijk, i_photo) = &
523 tree%boxes(id)%cc(ijk, i_photo) + &
524 phmc_tbl%frac_in_tbl/(dt_fac * product(dr))
528 dr = tree%boxes(id)%dr
531 tmp = dt_fac * 2 * pi
532 r(1:2) = af_r_cc(tree%boxes(id), [i, j])
533 tree%boxes(id)%cc(i, j, i_photo) = &
534 tree%boxes(id)%cc(i, j, i_photo) + &
535 phmc_tbl%frac_in_tbl/(tmp * product(dr) * r(1))
537 tree%boxes(id)%cc(ijk, i_photo) = &
538 tree%boxes(id)%cc(ijk, i_photo) + &
539 phmc_tbl%frac_in_tbl/(dt_fac * product(dr))
545 dr = tree%boxes(id)%dr
547 tree%boxes(id)%cc(ijk, i_photo) = &
548 tree%boxes(id)%cc(ijk, i_photo) + &
549 phmc_tbl%frac_in_tbl/(dt_fac * product(dr))
555 if (phmc_const_dx)
then
563 do lvl = min_lvl, tree%highest_lvl-1
565 do i = 1,
size(tree%lvls(lvl)%parents)
566 id = tree%lvls(lvl)%parents(i)
567 call af_gc_box(tree, id, [i_photo])
572 do i = 1,
size(tree%lvls(lvl)%parents)
573 id = tree%lvls(lvl)%parents(i)
574 call af_prolong_linear_from(tree%boxes, id, i_photo, add=.true.)
580 call prng%update_seed(rng)
686 subroutine phmc_generate_photons(tree, dt_fac, i_src, xyz_src, n_max, n_used, prng)
689 type(af_t),
intent(in) :: tree
691 real(dp),
intent(in) :: dt_fac
693 integer,
intent(in) :: i_src
695 real(dp),
allocatable,
intent(inout) :: xyz_src(:, :)
697 integer,
intent(in) :: n_max
699 integer,
intent(out) :: n_used
701 type(prng_t),
intent(inout) :: prng
703 integer :: proc_id, n_procs
704 integer :: lvl, ix, id, ijk, n, nc, i_ph
705 integer :: n_create, my_num_photons
706 real(dp) :: tmp, dr(ndim), r(3)
707 integer,
allocatable :: photons_per_thread(:), photon_thread(:)
708 integer,
allocatable :: ix_offset(:)
709 real(dp),
allocatable :: xyz_tmp(:, :)
712 n_procs = omp_get_max_threads()
714 allocate(xyz_src(3, n_max))
715 allocate(photons_per_thread(n_procs))
716 allocate(photon_thread(n_max))
723 proc_id = 1+omp_get_thread_num()
726 do lvl = 1, tree%highest_lvl
727 dr = af_lvl_dr(tree, lvl)
729 do ix = 1,
size(tree%lvls(lvl)%leaves)
730 id = tree%lvls(lvl)%leaves(ix)
735 if (tree%boxes(id)%coord_t == af_cyl)
then
736 tmp = af_cyl_radius_cc(tree%boxes(id), i)
737 tmp = dt_fac * 2 *
uc_pi * tmp * &
738 tree%boxes(id)%cc(i, j, i_src) * product(dr)
740 tmp = dt_fac * tree%boxes(id)%cc(i, j, i_src) * product(dr)
743 tmp = dt_fac * tree%boxes(id)%cc(ijk, i_src) * product(dr)
746 n_create = floor(tmp)
748 if (prng%rngs(proc_id)%unif_01() < tmp - n_create) &
749 n_create = n_create + 1
751 if (n_create > 0)
then
754 n_used = n_used + n_create
756 my_num_photons = my_num_photons + n_create
760 r(1) = prng%rngs(proc_id)%unif_01()
761 r(2) = prng%rngs(proc_id)%unif_01()
763 xyz_src(1:ndim, i_ph+n) = af_rr_cc(tree%boxes(id), &
764 [ijk] - 0.5_dp + r(1:ndim))
765 xyz_src(3, i_ph+n) = 0.0_dp
767 r(3) = prng%rngs(proc_id)%unif_01()
768 xyz_src(:, i_ph+n) = af_rr_cc(tree%boxes(id), &
769 [ijk] - 0.5_dp + r(1:ndim))
771 photon_thread(i_ph+n) = proc_id
779 photons_per_thread(proc_id) = my_num_photons
784 allocate(ix_offset(n_procs))
785 allocate(xyz_tmp(3, n_used))
789 ix_offset(n) = ix_offset(n-1) + photons_per_thread(n-1)
792 photons_per_thread = 0
795 photons_per_thread(i) = photons_per_thread(i) + 1
796 ix = ix_offset(i) + photons_per_thread(i)
797 xyz_tmp(:, ix) = xyz_src(:, n)
799 xyz_src(:, 1:n_used) = xyz_tmp(:, 1:n_used)
801 end subroutine phmc_generate_photons
Module with settings and routines to handle dielectrics.
subroutine, public dielectric_reset_photons()
subroutine, public dielectric_photon_absorption(tree, xyz_start, xyz_end, n_dim, n_photons, photon_weight)
Determine whether and where photons hit a dielectric, and change their absorption location to the fir...
Module that stores parameters related to the gas.
elemental integer function, public gas_index(name)
Find index of a gas component, return -1 if not found.
real(dp), dimension(:), allocatable, public, protected gas_fractions
real(dp), public, protected gas_pressure
Module that provides routines for Monte Carlo photoionization.
real(dp) function, public phmc_absorption_func_air(dist, p_o2)
The absorption function for photons in air according to Zheleznyak's model.
subroutine, public phmc_set_src(tree, rng, i_src, i_photo, use_cyl, dt)
Set the source term due to photoionization. At most phmc_num_photons discrete photons are produced.
subroutine, public phmc_initialize(cfg, is_used)
Initialize photoionization parameters.
type(phmc_tbl_t), public, protected phmc_tbl
Table for photoionization.
subroutine, public phmc_do_absorption(xyz_in, xyz_out, n_dim, n_photons, tbl, prng)
Given a list of photon production positions (xyz_in), compute where they end up (xyz_out).
subroutine, public phmc_print_grid_spacing(dr_base)
Print the grid spacing used for the absorption of photons.
logical, public, protected phmc_physical_photons
Whether physical photons are used.
subroutine, public phmc_get_table_air(phmc_tbl, p_o2, max_dist)
Compute the photonization table for air. If the absorption function is f(r), this table contains r as...
This module contains several pre-defined variables like:
integer, public, protected st_box_size
The size of the boxes that we use to construct our mesh.
logical, public, protected st_use_dielectric
Whether a dielectric is used.
real(dp), dimension(ndim), public, protected st_domain_len
Domain length per dimension.
Module that contains physical and numerical constants.
real(dp), parameter uc_pi
real(dp), parameter uc_torr_to_bar
Type to quickly look up absorption lengths from a table.