3#include "../afivo/src/cpp_macros.h"
17 real(dp) :: full_integral
24 logical,
protected :: phmc_const_dx = .true.
27 real(dp),
protected :: phmc_min_dx = 1e-9_dp
30 real(dp),
protected :: phmc_absorp_fac = 0.25_dp
33 integer,
protected :: phmc_num_photons = 5000*1000
36 real(dp),
protected :: phmc_min_weight = 1.0_dp
42 real(dp) function absfunc_t(dist, p_o2, p_h2o)
44 real(dp),
intent(in) :: dist
45 real(dp),
intent(in) :: p_o2
46 real(dp),
intent(in) :: p_h2o
47 end function absfunc_t
65 type(cfg_t),
intent(inout) :: cfg
66 logical,
intent(in) :: is_used
67 character(len=*),
intent(in) :: source_type
69 real(dp) :: p_o2, p_h2o
73 "Whether physical photons are used")
74 call cfg_add_get(cfg,
"photoi_mc%min_weight", phmc_min_weight, &
75 "Minimal photon weight (default: 1.0)")
76 call cfg_add_get(cfg,
"photoi_mc%const_dx", phmc_const_dx, &
77 "Whether a constant grid spacing is used for photoionization")
78 call cfg_add_get(cfg,
"photoi_mc%min_dx", phmc_min_dx, &
79 "Minimum grid spacing for photoionization")
80 call cfg_add_get(cfg,
"photoi_mc%absorp_fac", phmc_absorp_fac, &
81 "At which grid spacing photons are absorbed compared to their mean distance")
82 call cfg_add_get(cfg,
"photoi_mc%num_photons", phmc_num_photons, &
83 "Maximum number of discrete photons to use")
86 if (phmc_absorp_fac <= 0.0_dp) error stop
"photoi_mc%absorp_fac <= 0.0"
87 if (phmc_num_photons < 1) error stop
"photoi_mc%num_photons < 1"
93 error stop
"Photoionization: no oxygen present"
105 select case (source_type)
109 case (
"Naidis_humid")
116 error stop
"Invalid photoionization source_type"
122 if (.not. phmc_const_dx) &
123 error stop
"phmc_initialize: with dielectric use const_dx"
124 if (phmc_absorp_fac > 1e-9_dp) &
125 error stop
"Use phmc_absorp_fac <= 1e-9 with dielectric"
133 real(dp),
intent(in) :: dr_base
134 real(dp) :: lengthscale, dx
138 lengthscale = lt_get_col(
phmc_tbl%tbl, 1, phmc_absorp_fac)
142 lvl = get_lvl_length(dr_base, lengthscale)
143 dx = dr_base * (0.5_dp**(lvl-1))
145 if (phmc_const_dx)
then
146 write(*,
"(A,E12.3)")
" Monte-Carlo photoionization spacing: ", dx
148 write(*,
"(A)")
" Monte-Carlo photoionization uses adaptive spacing"
160 real(dp),
intent(in) :: p_o2
162 real(dp),
intent(in) :: p_h2o
164 procedure(absfunc_t) :: absfunc
167 real(dp) :: large_distance = 1e3_dp
169 integer,
parameter :: tbl_size = 500
170 real(dp),
allocatable :: fsum(:), dist(:)
172 allocate(fsum(tbl_size))
173 allocate(dist(tbl_size))
175 call integrate_absfunc(p_o2, p_h2o, absfunc, 1.0_dp, large_distance, &
176 tbl_size, fsum, dist, n)
182 fsum(1:n) = fsum(1:n) / fsum(n)
184 phmc_tbl%tbl = lt_create(0.0_dp, 1.0_dp, tbl_size, 1)
185 call lt_set_col(
phmc_tbl%tbl, 1, fsum(1:n), dist(1:n))
187 write(*,
"(A,E12.3,A)")
" Average photon absorption length", &
188 1e3_dp * sum(dist(1:n-1))/(n-1),
" mm"
191 subroutine integrate_absfunc(p_O2, p_H2O, absfunc, F_max, r_max, n_steps, fsum, r, n)
192 real(dp),
intent(in) :: p_o2
193 real(dp),
intent(in) :: p_h2o
194 procedure(absfunc_t) :: absfunc
195 real(dp),
intent(in) :: f_max
196 real(dp),
intent(in) :: r_max
197 integer,
intent(in) :: n_steps
198 real(dp),
intent(inout) :: fsum(n_steps)
199 real(dp),
intent(inout) :: r(n_steps)
200 integer,
intent(inout) :: n
203 df = f_max / (n_steps-1)
208 drdf = rk4_drdf(r(n-1), df, p_o2, p_h2o, absfunc)
209 r(n) = r(n-1) + df * drdf
211 if (fsum(n) > f_max .or. r(n) > r_max)
exit
217 end subroutine integrate_absfunc
221 real(dp) function rk4_drdf(r, df, p_o2, p_h2o, absfunc)
222 real(dp),
intent(in) :: r
223 real(dp),
intent(in) :: df
224 real(dp),
intent(in) :: p_o2
225 real(dp),
intent(in) :: p_h2o
226 procedure(absfunc_t) :: absfunc
229 real(dp),
parameter :: one_sixth = 1 / 6.0_dp
232 drdf = 1 / absfunc(r, p_o2, p_h2o)
236 drdf = 1 / absfunc(r + 0.5_dp * df * drdf, p_o2, p_h2o)
237 sum_drdf = sum_drdf + 2 * drdf
240 drdf = 1 / absfunc(r + 0.5_dp * df * drdf, p_o2, p_h2o)
241 sum_drdf = sum_drdf + 2 * drdf
244 drdf = 1 / absfunc(r + df * drdf, p_o2, p_h2o)
245 sum_drdf = sum_drdf + drdf
248 rk4_drdf = one_sixth * sum_drdf
249 end function rk4_drdf
255 real(dp) function absfunc_zheleznyak(dist, p_o2, p_h2o) result(f)
257 real(dp),
intent(in) :: dist
258 real(dp),
intent(in) :: p_o2
259 real(dp),
intent(in) :: p_h2o
263 real(dp),
parameter :: eps = epsilon(1.0_dp)
268 if (r * k1 < eps)
then
270 f = (k2 - k1 + 0.5_dp * (k1**2 - k2**2) * r) &
275 (exp(-k1 * r) - exp(-k2 * r)) / (dist * log(k2/k1)))
277 end function absfunc_zheleznyak
282 real(dp) function absfunc_naidis_humid(dist, p_o2, p_h2o) result(f)
283 real(dp),
intent(in) :: dist
284 real(dp),
intent(in) :: p_o2
285 real(dp),
intent(in) :: p_h2o
289 exp(-k3 * p_h2o * dist) * absfunc_zheleznyak(dist, p_o2, p_h2o))
290 end function absfunc_naidis_humid
296 real(dp) function absfunc_aints_humid(dist, p_o2, p_h2o) result(f)
297 real(dp),
intent(in) :: dist
298 real(dp),
intent(in) :: p_o2
299 real(dp),
intent(in) :: p_h2o
305 real(dp),
parameter :: eps = epsilon(1.0_dp)
307 k_a = k1 * p_o2 + k4 * p_h2o
308 k_b = k2 * p_o2 + k5 * p_h2o
310 if (dist * k_a < eps)
then
312 f = (k_b - k_a + 0.5_dp * (k_a**2 - k_b**2) * dist) / log(k_b/k_a)
316 (exp(-k_a * dist) - exp(-k_b * dist)) / (dist * log(k_b/k_a)))
318 end function absfunc_aints_humid
321 integer function get_lvl_length(dr_base, length)
322 real(dp),
intent(in) :: dr_base
323 real(dp),
intent(in) :: length
324 real(dp),
parameter :: invlog2 = 1 / log(2.0_dp)
327 ratio = dr_base / length
331 get_lvl_length = 1 + ceiling(log(ratio) * invlog2)
333 end function get_lvl_length
336 integer function get_rlvl_length(dr_base, length, rng)
338 real(dp),
intent(in) :: dr_base
339 real(dp),
intent(in) :: length
340 type(rng_t),
intent(inout) :: rng
341 real(dp),
parameter :: invlog2 = 1 / log(2.0_dp)
342 real(dp) :: ratio, tmp
344 ratio = dr_base / length
348 tmp = log(ratio) * invlog2
349 get_rlvl_length = floor(tmp)
350 if (rng%unif_01() < tmp - get_rlvl_length) &
351 get_rlvl_length = get_rlvl_length + 1
353 end function get_rlvl_length
361 integer,
intent(in) :: n_photons
363 real(dp),
intent(in) :: xyz_in(3, n_photons)
365 real(dp),
intent(out) :: xyz_out(3, n_photons)
366 integer,
intent(in) :: n_dim
368 type(lt_t),
intent(in) :: tbl
369 type(prng_t),
intent(inout) :: prng
370 integer :: n, proc_id
374 proc_id = 1+omp_get_thread_num()
379 rr = prng%rngs(proc_id)%unif_01()
380 dist = lt_get_col(tbl, 1, rr)
382 xyz_out(1:3, n) = xyz_in(1:3, n) + &
383 prng%rngs(proc_id)%sphere(dist)
386 else if (n_dim == 3)
then
389 rr = prng%rngs(proc_id)%unif_01()
390 dist = lt_get_col(tbl, 1, rr)
391 xyz_out(:, n) = xyz_in(:, n) + prng%rngs(proc_id)%sphere(dist)
395 print *,
"phmc_do_absorption: unknown n_dim", n_dim
455 type(af_t),
intent(inout) :: tree
456 type(rng_t),
intent(inout) :: rng
458 integer,
intent(in) :: i_src
460 integer,
intent(in) :: i_photo
462 logical,
intent(in) :: use_cyl
464 real(dp),
intent(in),
optional :: dt
466 integer :: lvl, id, nc, min_lvl
467 integer :: ijk, n, n_used
468 integer :: proc_id, n_procs
469 integer :: pho_lvl, max_num_photons
470 real(dp) :: dr(ndim), dt_fac, dist, n_produced
471 real(dp) :: sum_production_rate, pi_lengthscale
472 real(dp),
allocatable :: xyz_src(:, :)
473 real(dp),
allocatable :: xyz_abs(:, :)
475 real(dp) :: tmp, r(3)
476 real(dp),
parameter :: pi = acos(-1.0_dp)
479 type(af_loc_t),
allocatable :: ph_loc(:)
480 real(dp),
parameter :: small_value = 1e-100_dp
482 if (ndim == 3 .and. use_cyl) error stop
"phmc_set_src: use_cyl is .true."
484 if (st_use_dielectric)
then
491 n_procs = omp_get_max_threads()
492 call prng%init_parallel(n_procs, rng)
495 call af_tree_sum_cc(tree, i_src, sum_production_rate)
501 if (
present(dt))
then
504 n_produced = dt * sum_production_rate / phmc_min_weight
506 if (n_produced < phmc_num_photons)
then
507 dt_fac = dt/phmc_min_weight
509 dt_fac = phmc_num_photons / (sum_production_rate + small_value)
513 dt_fac = phmc_num_photons / (sum_production_rate + small_value)
517 max_num_photons = nint(1.2_dp * dt_fac * sum_production_rate + 1000)
519 call phmc_generate_photons(tree, dt_fac, i_src, xyz_src, &
520 max_num_photons, n_used, prng)
522 allocate(xyz_abs(3, n_used))
532 xyz_abs(1, n) = sqrt(xyz_abs(1, n)**2 + xyz_abs(3, n)**2)
533 xyz_abs(2, n) = xyz_abs(2, n)
537 if (st_use_dielectric)
then
540 2, n_used, 1.0_dp/dt_fac)
546 if (st_use_dielectric)
then
549 ndim, n_used, 1.0_dp/dt_fac)
553 allocate(ph_loc(n_used))
555 if (phmc_const_dx)
then
557 pi_lengthscale = lt_get_col(
phmc_tbl%tbl, 1, phmc_absorp_fac)
561 pho_lvl = get_lvl_length(maxval(tree%dr_base), pi_lengthscale)
565 ph_loc(n) = af_get_loc(tree, xyz_abs(1:ndim, n), pho_lvl)
570 proc_id = 1+omp_get_thread_num()
573 dist = phmc_absorp_fac * norm2(xyz_abs(1:ndim, n) - xyz_src(1:ndim, n))
574 if (dist < phmc_min_dx) dist = phmc_min_dx
575 lvl = get_rlvl_length(maxval(tree%dr_base), dist, prng%rngs(proc_id))
576 ph_loc(n) = af_get_loc(tree, xyz_abs(1:ndim, n), lvl)
583 call af_tree_clear_cc(tree, i_photo)
587 if (id > af_no_box)
then
590 dr = tree%boxes(id)%dr
592 tree%boxes(id)%cc(ijk, i_photo) = &
593 tree%boxes(id)%cc(ijk, i_photo) + &
594 phmc_tbl%full_integral/(dt_fac * product(dr))
598 dr = tree%boxes(id)%dr
601 tmp = dt_fac * 2 * pi
602 r(1:2) = af_r_cc(tree%boxes(id), [i, j])
603 tree%boxes(id)%cc(i, j, i_photo) = &
604 tree%boxes(id)%cc(i, j, i_photo) + &
605 phmc_tbl%full_integral/(tmp * product(dr) * r(1))
607 tree%boxes(id)%cc(ijk, i_photo) = &
608 tree%boxes(id)%cc(ijk, i_photo) + &
609 phmc_tbl%full_integral/(dt_fac * product(dr))
615 dr = tree%boxes(id)%dr
617 tree%boxes(id)%cc(ijk, i_photo) = &
618 tree%boxes(id)%cc(ijk, i_photo) + &
619 phmc_tbl%full_integral/(dt_fac * product(dr))
625 if (phmc_const_dx)
then
633 do lvl = min_lvl, tree%highest_lvl-1
635 do i = 1,
size(tree%lvls(lvl)%parents)
636 id = tree%lvls(lvl)%parents(i)
637 call af_gc_box(tree, id, [i_photo])
642 do i = 1,
size(tree%lvls(lvl)%parents)
643 id = tree%lvls(lvl)%parents(i)
644 call af_prolong_linear_from(tree%boxes, id, i_photo, add=.true.)
650 call prng%update_seed(rng)
756 subroutine phmc_generate_photons(tree, dt_fac, i_src, xyz_src, n_max, n_used, prng)
759 type(af_t),
intent(in) :: tree
761 real(dp),
intent(in) :: dt_fac
763 integer,
intent(in) :: i_src
765 real(dp),
allocatable,
intent(inout) :: xyz_src(:, :)
767 integer,
intent(in) :: n_max
769 integer,
intent(out) :: n_used
771 type(prng_t),
intent(inout) :: prng
773 integer :: proc_id, n_procs
774 integer :: lvl, ix, id, ijk, n, nc, i_ph
775 integer :: n_create, my_num_photons
776 real(dp) :: tmp, dr(ndim), r(3)
777 integer,
allocatable :: photons_per_thread(:), photon_thread(:)
778 integer,
allocatable :: ix_offset(:)
779 real(dp),
allocatable :: xyz_tmp(:, :)
782 n_procs = omp_get_max_threads()
784 allocate(xyz_src(3, n_max))
785 allocate(photons_per_thread(n_procs))
786 allocate(photon_thread(n_max))
793 proc_id = 1+omp_get_thread_num()
796 do lvl = 1, tree%highest_lvl
797 dr = af_lvl_dr(tree, lvl)
799 do ix = 1,
size(tree%lvls(lvl)%leaves)
800 id = tree%lvls(lvl)%leaves(ix)
805 if (tree%boxes(id)%coord_t == af_cyl)
then
806 tmp = af_cyl_radius_cc(tree%boxes(id), i)
807 tmp = dt_fac * 2 *
uc_pi * tmp * &
808 tree%boxes(id)%cc(i, j, i_src) * product(dr)
810 tmp = dt_fac * tree%boxes(id)%cc(i, j, i_src) * product(dr)
813 tmp = dt_fac * tree%boxes(id)%cc(ijk, i_src) * product(dr)
816 n_create = floor(tmp)
818 if (prng%rngs(proc_id)%unif_01() < tmp - n_create) &
819 n_create = n_create + 1
821 if (n_create > 0)
then
824 n_used = n_used + n_create
826 my_num_photons = my_num_photons + n_create
830 r(1) = prng%rngs(proc_id)%unif_01()
831 r(2) = prng%rngs(proc_id)%unif_01()
833 xyz_src(1:ndim, i_ph+n) = af_rr_cc(tree%boxes(id), &
834 [ijk] - 0.5_dp + r(1:ndim))
835 xyz_src(3, i_ph+n) = 0.0_dp
837 r(3) = prng%rngs(proc_id)%unif_01()
838 xyz_src(:, i_ph+n) = af_rr_cc(tree%boxes(id), &
839 [ijk] - 0.5_dp + r(1:ndim))
841 photon_thread(i_ph+n) = proc_id
849 photons_per_thread(proc_id) = my_num_photons
854 allocate(ix_offset(n_procs))
855 allocate(xyz_tmp(3, n_used))
859 ix_offset(n) = ix_offset(n-1) + photons_per_thread(n-1)
862 photons_per_thread = 0
865 photons_per_thread(i) = photons_per_thread(i) + 1
866 ix = ix_offset(i) + photons_per_thread(i)
867 xyz_tmp(:, ix) = xyz_src(:, n)
869 xyz_src(:, 1:n_used) = xyz_tmp(:, 1:n_used)
871 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.
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_get_table_air(phmc_tbl, p_o2, p_h2o, absfunc)
Compute the photonization table for air. If the absorption function is f(r), this table contains r as...
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_initialize(cfg, is_used, source_type)
Initialize photoionization parameters.
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.
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.