3#include "../afivo/src/cpp_macros.h"
11 logical,
private :: last_step
21 subroutine forward_euler(tree, dt, dt_stiff, dt_lim, time, s_deriv, n_prev, &
22 s_prev, w_prev, s_out, i_step, n_steps)
29 type(af_t),
intent(inout) :: tree
30 real(dp),
intent(in) :: dt
31 real(dp),
intent(in) :: dt_stiff
32 real(dp),
intent(inout) :: dt_lim
33 real(dp),
intent(in) :: time
34 integer,
intent(in) :: s_deriv
35 integer,
intent(in) :: n_prev
36 integer,
intent(in) :: s_prev(n_prev)
37 real(dp),
intent(in) :: w_prev(n_prev)
38 integer,
intent(in) :: s_out
39 integer,
intent(in) :: i_step
40 integer,
intent(in) :: n_steps
42 real(dp) :: t1, t2, t3
48 last_step = (i_step == n_steps)
59 flux_dummy_line_modify, af_limiter_koren_t)
66 call af_loop_box(tree, handle_ion_se_flux, .true.)
73 w_prev, s_out, add_source_terms, 2,
dt_limits(3:4), set_box_mask)
80 do ix = 1,
diel%max_ix
81 if (
diel%surfaces(ix)%in_use)
then
82 id_out =
diel%surfaces(ix)%id_out
87 diel%surfaces(ix), dt, n_prev, s_prev, w_prev, s_out)
91 diel%surfaces(ix), dt, s_out)
102 subroutine flux_upwind(nf, n_var, flux_dim, u, flux, cfl_sum, &
103 n_other_dt, other_dt, box, line_ix, s_deriv)
104 use m_af_flux_schemes
109 integer,
intent(in) :: nf
110 integer,
intent(in) :: n_var
111 integer,
intent(in) :: flux_dim
112 real(dp),
intent(in) :: u(nf, n_var)
113 real(dp),
intent(out) :: flux(nf, n_var)
115 real(dp),
intent(out) :: cfl_sum(nf-1)
116 integer,
intent(in) :: n_other_dt
117 real(dp),
intent(inout) :: other_dt(n_other_dt)
118 type(box_t),
intent(in) :: box
119 integer,
intent(in) :: line_ix(ndim-1)
120 integer,
intent(in) :: s_deriv
122 real(dp),
parameter :: five_third = 5/3.0_dp
124 real(dp) :: e_cc(0:nf)
126 real(dp) :: n_gas(0:nf)
127 real(dp) :: ne_cc(0:nf)
128 real(dp) :: en_cc(0:nf)
131 real(dp) :: tmp_fc(nf), n_inv(nf)
132 real(dp) :: mu(nf), sigma(nf), inv_dx, cfl_factor
133 integer :: n, nc, flux_ix
136 inv_dx = 1/box%dr(flux_dim)
140 if (box%cc(dtimes(1),
i_eps) > 1.0_dp)
then
150 call flux_get_line_1cc(box,
i_gas_dens, flux_dim, line_ix, n_gas)
152 n_inv(n) = 2 / (n_gas(n-1) + n_gas(n))
156 call flux_get_line_1fc(box,
electric_fld, flux_dim, line_ix, e_x)
157 call flux_get_line_1cc(box,
i_electron+s_deriv, flux_dim, line_ix, ne_cc)
161 flux_dim, line_ix, en_cc)
164 tmp_fc = mean_electron_energy(u(:, 2), u(:, 1))
168 call flux_get_line_1cc(box,
i_electric_fld, flux_dim, line_ix, e_cc)
172 tmp_fc = 0.5_dp * (e_cc(0:nc) + e_cc(1:nc+1)) *
si_to_townsend * n_inv
181 flux(:, 1) = v * u(:, 1) - dc * inv_dx * (ne_cc(1:nc+1) - ne_cc(0:nc))
187 flux(:, 2) = five_third * (v * u(:, 2) - &
188 dc * inv_dx * (en_cc(1:nc+1) - en_cc(0:nc)))
189 cfl_factor = five_third
195 cfl_sum = cfl_factor * max(abs(v(2:)), abs(v(:nf-1))) * inv_dx + &
196 2 * max(dc(2:), dc(:nf-1)) * inv_dx**2
203 flux(:, flux_ix) = v * u(:, flux_ix)
204 sigma = sigma + mu * u(:, flux_ix)
209 end subroutine flux_upwind
212 subroutine flux_direction(box, line_ix, s_deriv, n_var, flux_dim, direction_positive)
213 type(box_t),
intent(in) :: box
214 integer,
intent(in) :: line_ix(ndim-1)
215 integer,
intent(in) :: s_deriv
216 integer,
intent(in) :: flux_dim
217 integer,
intent(in) :: n_var
219 logical,
intent(out) :: direction_positive(box%n_cell+1, n_var)
220 real(dp) :: e_x(box%n_cell+1)
223 call flux_get_line_1fc(box,
electric_fld, flux_dim, line_ix, e_x)
227 end subroutine flux_direction
230 pure function cc_average_at_cell_face(box, IJK, idim, iv)
result(avg)
231 type(box_t),
intent(in) :: box
232 integer,
intent(in) :: ijk
233 integer,
intent(in) :: idim
234 integer,
intent(in) :: iv
238 avg = 0.5_dp * (box%cc(i-1, iv) + box%cc(i, iv))
242 avg = 0.5_dp * (box%cc(i-1, j, iv) + box%cc(i, j, iv))
244 avg = 0.5_dp * (box%cc(i, j-1, iv) + box%cc(i, j, iv))
249 avg = 0.5_dp * (box%cc(i-1, j, k, iv) + box%cc(i, j, k, iv))
251 avg = 0.5_dp * (box%cc(i, j-1, k, iv) + box%cc(i, j, k, iv))
253 avg = 0.5_dp * (box%cc(i, j, k-1, iv) + box%cc(i, j, k, iv))
256 end function cc_average_at_cell_face
259 pure function fc_inner_product(box, IJK, flux_a, flux_b)
result(inprod)
260 type(box_t),
intent(in) :: box
261 integer,
intent(in) :: ijk
262 integer,
intent(in) :: flux_a
263 integer,
intent(in) :: flux_b
266 inprod = 0.5_dp * sum(box%fc(ijk, :, flux_a) * box%fc(ijk, :, flux_b))
268 inprod = inprod + 0.5_dp * (&
269 box%fc(i+1, 1, flux_a) * box%fc(i+1, 1, flux_b))
271 inprod = inprod + 0.5_dp * (&
272 box%fc(i+1, j, 1, flux_a) * box%fc(i+1, j, 1, flux_b) + &
273 box%fc(i, j+1, 2, flux_a) * box%fc(i, j+1, 2, flux_b))
275 inprod = inprod + 0.5_dp * (&
276 box%fc(i+1, j, k, 1, flux_a) * box%fc(i+1, j, k, 1, flux_b) + &
277 box%fc(i, j+1, k, 2, flux_a) * box%fc(i, j+1, k, 2, flux_b) + &
278 box%fc(i, j, k+1, 3, flux_a) * box%fc(i, j, k+1, 3, flux_b))
280 end function fc_inner_product
284 pure real(dp) function get_n_inv_face(box, IJK, idim)
286 type(box_t),
intent(in) :: box
287 integer,
intent(in) :: ijk
288 integer,
intent(in) :: idim
293 get_n_inv_face = 1 / cc_average_at_cell_face(box, ijk, idim,
i_gas_dens)
295 end function get_n_inv_face
298 subroutine add_source_terms(box, dt, n_vars, i_cc, s_deriv, s_out, n_dt, dt_lim, mask)
307 type(box_t),
intent(inout) :: box
308 real(dp),
intent(in) :: dt
309 integer,
intent(in) :: n_vars
310 integer,
intent(in) :: i_cc(n_vars)
311 integer,
intent(in) :: s_deriv
312 integer,
intent(in) :: s_out
313 integer,
intent(in) :: n_dt
314 real(dp),
intent(inout) :: dt_lim(n_dt)
315 logical,
intent(in) :: mask(dtimes(box%n_cell))
317 real(dp) :: tmp, gain, loss_rate
319 real(dp) :: derivs(box%n_cell**ndim,
n_species)
320 real(dp) :: dens(box%n_cell**ndim,
n_species)
321 real(dp) :: fields(box%n_cell**ndim), box_rates(
n_reactions)
322 real(dp) :: source_factor(box%n_cell**ndim)
323 real(dp) :: mean_energies(box%n_cell**ndim)
324 integer :: ijk, ix, nc, n_cells, n, iv
326 real(dp),
parameter :: eps = 1e-100_dp
329 n_cells = box%n_cell**ndim
332 if (.not. any(mask))
return
342 pack(box%cc(dtimes(1:nc),
i_gas_dens), .true.)
356 dens = max(dens, 0.0_dp)
359 mean_energies = pack(mean_electron_energy(&
361 box%cc(dtimes(1:nc),
i_electron+s_out)), .true.)
362 call get_rates(fields, rates, n_cells, mean_energies)
364 mean_energies = 0.0_dp
370 call compute_source_factor(box, nc, dens(:,
ix_electron), &
371 fields, s_deriv, source_factor)
373 source_factor(:) = 1.0_dp
380 where (dens(:,
ix_electron) * minval(box%dr)**3 < &
382 source_factor = 0.0_dp
389 reshape(source_factor, [dtimes(nc)])
394 rates(:, n) = rates(:, n) * source_factor
403 tid = omp_get_thread_num() + 1
412 tmp = minval(max(dens, eps) / max(-derivs, eps))
420 call chemical_rates_box(box, nc, rates, box_rates)
427 call sum_global_jdote(box, tid)
433 if (.not. mask(ijk)) cycle
446 + dt * (gain - loss_rate * box%cc(ijk,
i_electron+s_out))
450 do n = n_gas_species+1, n_species
452 iv = species_itree(n)
455 if (.not. mask(ijk)) cycle
456 box%cc(ijk, iv+s_out) = box%cc(ijk, iv+s_out) + dt * derivs(ix, n)
460 if (model_has_energy_equation)
then
462 tmp = maxval(mean_energies)
463 dt_lim(2) = tmp/lt_get_col(td_ee_tbl, td_ee_loss, tmp)
466 end subroutine add_source_terms
469 subroutine set_box_mask(box, mask)
470 type(box_t),
intent(in) :: box
471 logical,
intent(out) :: mask(dtimes(box%n_cell))
472 integer :: n, nc, ijk
473 real(dp) :: coords(box%n_cell, ndim), r(ndim)
474 real(dp),
parameter :: eps = 1e-10_dp
480 if (st_use_electrode)
then
481 where (box%cc(dtimes(1:nc), i_lsf) <= 0.0_dp)
487 if (st_use_dielectric)
then
488 where (abs(box%cc(dtimes(1:nc), i_eps) - 1) > eps)
494 if (st_plasma_region_enabled)
then
497 coords(:, n) = box%r_min(n) + box%dr(n) * [(i-0.5_dp, i=1,nc)]
508 if (any(r < st_plasma_region_rmin) .or. &
509 any(r > st_plasma_region_rmax))
then
515 end subroutine set_box_mask
518 pure elemental real(dp) function mean_electron_energy(n_energy, n_e)
519 real(dp),
intent(in) :: n_energy, n_e
520 mean_electron_energy = n_energy / max(n_e, 1.0_dp)
521 end function mean_electron_energy
525 subroutine compute_source_factor(box, nc, elec_dens, fields, s_dt, source_factor)
529 type(box_t),
intent(inout) :: box
530 integer,
intent(in) :: nc
531 real(dp),
intent(in) :: elec_dens(nc**ndim)
532 real(dp),
intent(in) :: fields(nc**ndim)
533 integer,
intent(in) :: s_dt
534 real(dp),
intent(out) :: source_factor(nc**ndim)
535 real(dp) :: mobilities(nc**ndim)
536 real(dp) :: n_inv(nc**ndim)
537 real(dp) :: inv_dr(ndim)
538 real(dp),
parameter :: small_flux = 1.0e-9_dp
546 n_inv = pack(1 / box%cc(dtimes(1:nc),
i_gas_dens), .true.)
551 select case (st_source_factor)
552 case (source_factor_flux)
559 source_factor(ix) = 0.5_dp * norm2([ &
560 box%fc(i, 1, flux_elec) + box%fc(i+1, 1, flux_elec)])
562 source_factor(ix) = 0.5_dp * norm2([ &
563 box%fc(i, j, 1, flux_elec) + box%fc(i+1, j, 1, flux_elec), &
564 box%fc(i, j, 2, flux_elec) + box%fc(i, j+1, 2, flux_elec)])
566 source_factor(ix) = 0.5_dp * norm2([ &
567 box%fc(i, j, k, 1, flux_elec) + box%fc(i+1, j, k, 1, flux_elec), &
568 box%fc(i, j, k, 2, flux_elec) + box%fc(i, j+1, k, 2, flux_elec), &
569 box%fc(i, j, k, 3, flux_elec) + box%fc(i, j, k+1, 3, flux_elec)])
574 source_factor = (source_factor + small_flux) / (small_flux + &
575 elec_dens * mobilities * &
576 pack(box%cc(dtimes(1:nc), i_electric_fld), .true.))
578 error stop
"This type of source factor not implemented"
581 source_factor = min(1.0_dp, source_factor)
582 source_factor = max(0.0_dp, source_factor)
583 end subroutine compute_source_factor
586 subroutine handle_ion_se_flux(box)
588 type(box_t),
intent(inout) :: box
589 integer :: nc, nb, n, ion_flux, flux_ix
594 if (all(box%neighbors >= af_no_box))
return
596 do nb = 1, af_num_neighbors
598 if (box%neighbors(nb) < af_no_box)
then
602 flux_ix = flux_num_electron_vars + n
603 if (flux_species_charge(flux_ix) > 0.0_dp)
then
604 ion_flux = flux_variables(flux_ix)
607 case (af_neighb_lowx)
608 box%fc(1, 1, flux_elec) = box%fc(1, 1, flux_elec) - &
610 case (af_neighb_highx)
611 box%fc(nc+1, 1, flux_elec) = box%fc(nc+1, 1, flux_elec) - &
614 case (af_neighb_lowx)
615 box%fc(1, 1:nc, 1, flux_elec) = &
617 min(0.0_dp, box%fc(1, 1:nc, 1, ion_flux))
618 case (af_neighb_highx)
619 box%fc(nc+1, 1:nc, 1, flux_elec) = &
621 max(0.0_dp, box%fc(nc+1, 1:nc, 1, ion_flux))
622 case (af_neighb_lowy)
623 box%fc(1:nc, 1, 2, flux_elec) = &
625 min(0.0_dp, box%fc(1:nc, 1, 2, ion_flux))
626 case (af_neighb_highy)
627 box%fc(1:nc, nc+1, 2, flux_elec) = &
629 max(0.0_dp, box%fc(1:nc, nc+1, 2, ion_flux))
631 case (af_neighb_lowx)
632 box%fc(1, 1:nc, 1:nc, 1, flux_elec) = &
634 min(0.0_dp, box%fc(1, 1:nc, 1:nc, 1, ion_flux))
635 case (af_neighb_highx)
636 box%fc(nc+1, 1:nc, 1:nc, 1, flux_elec) = &
637 box%fc(nc+1, 1:nc, 1:nc, 1, flux_elec) -
ion_se_yield * &
638 max(0.0_dp, box%fc(nc+1, 1:nc, 1:nc, 1, ion_flux))
639 case (af_neighb_lowy)
640 box%fc(1:nc, 1:nc, 1, 2, flux_elec) = &
642 min(0.0_dp, box%fc(1:nc, 1:nc, 1, 2, ion_flux))
643 case (af_neighb_highy)
644 box%fc(1:nc, nc+1, 1:nc, 2, flux_elec) = &
645 box%fc(1:nc, nc+1, 1:nc, 2, flux_elec) -
ion_se_yield * &
646 max(0.0_dp, box%fc(1:nc, nc+1, 1:nc, 2, ion_flux))
647 case (af_neighb_lowz)
648 box%fc(1:nc, 1:nc, 1, 3, flux_elec) = &
650 min(0.0_dp, box%fc(1:nc, 1:nc, 1, 3, ion_flux))
651 case (af_neighb_highz)
652 box%fc(1:nc, 1:nc, nc+1, 3, flux_elec) = &
653 box%fc(1:nc, 1:nc, nc+1, 3, flux_elec) -
ion_se_yield * &
654 max(0.0_dp, box%fc(1:nc, 1:nc, nc+1, 3, ion_flux))
663 end subroutine handle_ion_se_flux
666 subroutine chemical_rates_box(box, nc, rates, box_rates)
668 type(box_t),
intent(in) :: box
669 integer,
intent(in) :: nc
670 real(dp),
intent(in) :: rates(nc**ndim,
n_reactions)
674 real(dp) :: w(nc), tmp(nc, nc)
677 if (box%coord_t == af_xyz)
then
678 box_rates = sum(rates, dim=1) * product(box%dr)
680 else if (box%coord_t == af_cyl)
then
685 w(i) = af_cyl_volume_cc(box, i)
689 tmp = reshape(rates(:, n), [nc, nc])
691 tmp(i, :) = w(i) * tmp(i, :)
693 box_rates(n) = box_rates(n) + sum(tmp)
697 error stop
"Unknown box coordinates"
699 end subroutine chemical_rates_box
702 subroutine sum_global_jdote(box, tid)
704 type(box_t),
intent(in) :: box
705 integer,
intent(in) :: tid
707 real(dp) :: jdote, tmp
708 real(dp) :: volume(box%n_cell)
712 volume = product(box%dr)
715 if (box%coord_t == af_cyl)
then
718 volume(i) = af_cyl_volume_cc(box, i)
725 tmp = fc_inner_product(box, ijk, flux_elec, electric_fld)
726 jdote = jdote + tmp * volume(i)
729 st_current_jdote(1, tid) = st_current_jdote(1, tid) + &
730 jdote * uc_elec_charge
731 end subroutine sum_global_jdote
Module for handling chemical reactions.
integer, parameter, public ionization_reaction
Identifier for ionization reactions.
subroutine, public get_rates(fields, rates, n_cells, energy_ev)
Compute reaction rates.
subroutine, public get_derivatives(dens, rates, derivs, n_cells)
Compute derivatives due to chemical reactions. Note that the 'rates' argument is modified.
integer, dimension(max_num_species), public, protected species_itree
species_itree(n) holds the index of species n in the tree (cell-centered variables)
integer, public, protected n_species
Number of species present.
integer, public, protected n_plasma_species
Number of plasma species present.
type(reaction_t), dimension(max_num_reactions), public, protected reactions
List of reactions.
integer, public, protected n_gas_species
Number of gas species present.
integer, public, protected n_reactions
Number of reactions present.
Module with settings and routines to handle dielectrics.
type(surfaces_t), public diel
To store dielectric surface.
subroutine, public dielectric_photon_emission(box, surface, dt, s_out)
subroutine, public dielectric_update_surface_charge(box, surface, dt, n_prev, s_prev, w_prev, s_out)
Update charge on dielectric surface based on particle fluxes. In case of secondary emission,...
Module to set the time step.
real(dp), dimension(dt_num_cond), public dt_limits
real(dp), public, protected dt_max
real(dp), public, protected dt_chemistry_nmin
If > 0, a density to control the accuracy of the chemistry time step.
logical, public, protected dt_chemistry_limit_loss
Limit dt to prevent negative densities due to loss reactions.
real(dp), public, protected dt_cfl_number
CFL number to use.
Module to compute electric fields.
subroutine, public field_compute(tree, mg, s_in, time, have_guess)
Compute electric field on the tree. First perform multigrid to get electric potential,...
subroutine, public forward_euler(tree, dt, dt_stiff, dt_lim, time, s_deriv, n_prev, s_prev, w_prev, s_out, i_step, n_steps)
Advance fluid model using forward Euler step. If the equation is written as y' = f(y),...
Module that stores parameters related to the gas.
real(dp), dimension(:), allocatable, public, protected gas_fractions
integer, public, protected i_gas_dens
real(dp), parameter, public si_to_townsend
logical, public, protected gas_constant_density
Whether the gas has a constant density.
real(dp), public, protected gas_number_density
real(dp), public, protected gas_inverse_number_density
Module to set the type of model.
logical, public, protected model_has_energy_equation
Whether the model has an energy equation.
Top-module for photoionization, which can make use of different methods.
integer, public, protected photoi_species_index
Index of species ionized by photoionization (in list of chemical species)
logical, public, protected photoi_enabled
Whether photoionization is enabled.
integer, public, protected i_photo
Optional variable (when using photoionization)
This module contains several pre-defined variables like:
integer, public, protected flux_num_electron_vars
Number of electron flux variables.
integer, dimension(:), allocatable, public, protected flux_species_charge_sign
List of the signs of the charges of the flux species (+- 1)
integer, parameter, public source_factor_none
integer, public, protected i_eps
Index can be set to include a dielectric.
real(dp), public wc_time_field
integer, public, protected i_srcfac
Index of correction factor for source terms.
integer, dimension(:), allocatable, public, protected flux_variables
List of all flux variables (face-centered index)
integer, public, protected st_source_factor
Use source factor to prevent unphysical effects due to diffusion.
type(mg_t), public mg
Multigrid option structure.
real(dp), dimension(:, :), allocatable, public st_current_jdote
Current sum of J.E per thread.
integer, public, protected i_electron_energy
Index of electron energy density.
integer, dimension(:), allocatable, public, protected flux_species
List of all flux species (cell-centered index)
real(dp), public wc_time_source
integer, public, protected ix_electron
Index of electron density (in species list)
logical, public, protected st_use_dielectric
Whether a dielectric is used.
integer, public, protected flux_elec
Index of electron flux.
integer, public, protected i_electron
Index of electron density.
integer, public, protected electric_fld
Index of electric field vector.
real(dp), public wc_time_flux
integer, public, protected i_electric_fld
Index of electric field norm.
integer, dimension(:), allocatable, public, protected all_densities
Index of all densities that evolve in time.
integer, public, protected flux_num_species
Number of flux variables.
real(dp), dimension(:, :), allocatable, public st_current_rates
Current sum of reaction rates per thread.
real(dp), public, protected st_source_min_electrons_per_cell
Minimum number of electrons per cell to include source terms.
Module that provides routines for reading in arbritrary transport data.
real(dp), public, protected ion_se_yield
Secondary electron emission yield for positive ions.
type(lt_t), public, protected td_tbl
integer, parameter, public td_diffusion
Electron diffusion constant.
integer, public, protected td_ee_diffusion
Electron diffusion coefficient as a function of energy.
integer, public, protected td_ee_mobility
Electron mobility as a function of energy.
integer, parameter, public td_mobility
Electron mobility.
type(lt_t), public, protected td_ee_tbl
integer, public, protected td_ee_loss
Electron energy loss.
type(ion_transport_t), public transport_data_ions
Module that contains physical and numerical constants.
real(dp), parameter uc_elem_charge
real(dp), parameter uc_eps0