afivo-streamer 1.1
1D/2D/3D streamer simulations with AMR
Loading...
Searching...
No Matches
m_chemistry.f90
Go to the documentation of this file.
1!> Module for handling chemical reactions
3#include "../afivo/src/cpp_macros.h"
4 use m_types
5 use m_af_all
6 use m_lookup_table
8 use m_model
9 use m_random
10 use omp_lib
11
12 implicit none
13 private
14
15 !> Identifier for ionization reactions
16 integer, parameter, public :: ionization_reaction = 1
17 !> Identifier for attachment reactions
18 integer, parameter, public :: attachment_reaction = 2
19 !> Identifier for recombination reactions
20 integer, parameter, public :: recombination_reaction = 3
21 !> Identifier for detachment reactions
22 integer, parameter, public :: detachment_reaction = 4
23 !> Identifier for general reactions (not of any particular type)
24 integer, parameter, public :: general_reaction = 5
25
26 character(len=20), parameter, public :: reaction_names(*) = &
27 [character(len=20) :: "ionization", "attachment", "recombination", &
28 "detachment", "general"]
29
30 !> Maximum number of coefficients for a reaction rate function
31 integer, parameter :: rate_max_num_coeff = 9
32
33 !> Basic chemical reaction type
34 type reaction_t
35 integer, allocatable :: ix_in(:) !< Index of input species
36 integer, allocatable :: ix_out(:) !< Index of output species
37 integer, allocatable :: multiplicity_out(:) !< Multiplicity of output
38 integer :: n_species_in !< Number of input species
39 integer :: rate_type !< Type of reaction rate
40 !> Type of reaction (e.g. ionization)
41 integer :: reaction_type = general_reaction
42 real(dp) :: rate_factor !< Multiply rate by this factor
43 integer :: n_coeff !< Number of stored coefficients
44 !> Data for the reaction rate
45 real(dp) :: rate_data(rate_max_num_coeff) = -huge(1.0_dp)
46 integer :: lookup_table_index !< Index in lookup table
47 real(dp), allocatable :: x_data(:)
48 real(dp), allocatable :: y_data(:)
49 character(len=50) :: description
50 end type reaction_t
51
52 !> Compact reaction type, for efficiency
53 type tiny_react_t
54 integer, allocatable :: ix_in(:)
55 integer, allocatable :: ix_out(:)
56 integer, allocatable :: multiplicity_out(:)
57 end type tiny_react_t
58
59 !> Type for stochastic reaction
60 type stochastic_species_t
61 integer :: i_from !< Index of stochastic density (in tree)
62 integer :: i_to !< Index of regular density (in tree)
63 end type stochastic_species_t
64
65 !> Reaction with a field-dependent reaction rate
66 integer, parameter :: rate_tabulated_energy = 0
67
68 !> Reaction with a field-dependent reaction rate
69 integer, parameter :: rate_tabulated_field = 1
70
71 !> Reaction of the form c1
72 integer, parameter :: rate_analytic_constant = 2
73
74 !> Reaction of the form c1 * (Td - c2)
75 integer, parameter :: rate_analytic_linear = 3
76
77 !> Reaction of the form c1 * exp(-(c2/(c3 + Td))**2)
78 integer, parameter :: rate_analytic_exp_v1 = 4
79
80 !> Reaction of the form c1 * exp(-(Td/c2)**2)
81 integer, parameter :: rate_analytic_exp_v2 = 5
82
83 !> Reaction of the form c1 * (300 / Te)**c2
84 integer, parameter :: rate_analytic_k1 = 6
85
86 !> Reaction of the form (c1 * (kB_eV * Te + c2)**2 - c3) * c4
87 integer, parameter :: rate_analytic_k3 = 8
88
89 !> Reaction of the form c1 * (Tg / 300)**c2 * exp(-c3 / Tg)
90 integer, parameter :: rate_analytic_k4 = 9
91
92 !> Reaction of the form c1 * exp(-c2 / Tg)
93 integer, parameter :: rate_analytic_k5 = 10
94
95 !> Reaction of the form c1 * Tg**c2
96 integer, parameter :: rate_analytic_k6 = 11
97
98 !> Reaction of the form c1 * (Tg / c2)**c3
99 integer, parameter :: rate_analytic_k7 = 12
100
101 !> Reaction of the form c1 * (300 / Tg)**c2
102 integer, parameter :: rate_analytic_k8 = 13
103
104 !> Reaction of the form c1 * exp(-c2 * Tg)
105 integer, parameter :: rate_analytic_k9 = 14
106
107 !> Reaction of the form 10**(c1 + c2 * (Tg - 300))
108 integer, parameter :: rate_analytic_k10 = 15
109
110 !> Reaction of the form c1 * (300 / Tg)**c2 * exp(-c3 / Tg)
111 integer, parameter :: rate_analytic_k11 = 16
112
113 !> Reaction of the form c1 * Tg**c2 * exp(-c3 / Tg)
114 integer, parameter :: rate_analytic_k12 = 17
115
116 !> Reaction of the form c1 * exp(-(c2 / (c3 + Td))**c4)
117 integer, parameter :: rate_analytic_k13 = 18
118
119 !> Reaction of the form c1 * exp(-(Td / c2)**c3)
120 integer, parameter :: rate_analytic_k14 = 19
121
122 !> Reaction of the form c1 * exp(-(c2 /(kb * (Tg + Td/c3)))**c4)
123 integer, parameter :: rate_analytic_k15 = 20
124
125 !> Reaction of the form c1 * (300/Te)**c2 * exp(-c3/Tg) * exp(c4 * (Te - Tg) / (Te * Tg))
126 integer, parameter :: rate_analytic_k16 = 21
127
128 !> Maximum number of species
129 integer, parameter :: max_num_species = 100
130
131 !> Maximum number of reactions
132 integer, parameter :: max_num_reactions = 500
133
134 !> Number of species present
135 integer, public, protected :: n_species = 0
136
137 !> Number of gas species present
138 integer, public, protected :: n_gas_species = 0
139
140 !> Number of plasma species present
141 integer, public, protected :: n_plasma_species = 0
142
143 !> Number of reactions present
144 integer, public, protected :: n_reactions = 0
145
146 !> Number of stochastic species
147 integer, public, protected :: n_stochastic_species = 0
148
149 !> List of the species
150 character(len=comp_len), public, protected :: species_list(max_num_species)
151
152 !> List of stochastic species
153 type(stochastic_species_t) :: stochastic_species_list(max_num_species)
154
155 !> Charge of the species
156 integer, public, protected :: species_charge(max_num_species) = 0
157
158 !> species_itree(n) holds the index of species n in the tree (cell-centered variables)
159 integer, public, protected :: species_itree(max_num_species)
160
161 !> List of reactions
162 type(reaction_t), public, protected :: reactions(max_num_reactions)
163
164 !> A copy of the list of reactions for performance reasons
165 type(tiny_react_t) :: tiny_react(max_num_reactions)
166
167 !> Lookup table with reaction rates versus field
168 type(lt_t) :: chemtbl_fld
169
170 !> Lookup table with reaction rates versus electron energy
171 type(lt_t) :: chemtbl_ee
172
173 !> List with indices of charged species
174 integer, allocatable, protected :: charged_species_itree(:)
175
176 !> List with charges of charged species
177 integer, allocatable, protected :: charged_species_charge(:)
178
179 public :: charged_species_itree
180 public :: charged_species_charge
181
182 public :: chemistry_initialize
186 public :: get_rates
187 public :: get_derivatives
188 public :: species_index
189
190 public :: read_reactions
191
192contains
193
194 !> Initialize module and load chemical reactions
195 subroutine chemistry_initialize(tree, cfg)
196 use m_config
198 use m_table_data
200 use m_gas
201 use m_dt
202 type(af_t), intent(inout) :: tree
203 type(cfg_t), intent(inout) :: cfg
204 integer :: n, i, j, i_elec, rtype
205 character(len=string_len) :: reaction_file
206 character(len=comp_len) :: tmp_name
207 logical :: read_success
208
209 call cfg_get(cfg, "input_data%file", reaction_file)
210
211 if (.not. gas_constant_density) then
212 ! Make sure the gas components are the first species
216 end if
217
218 call read_reactions(trim(reaction_file), read_success)
219
220 if (.not. read_success) then
221 print *, "m_chemistry: no reaction table found, using standard model"
222
223 species_list(1) = "e"
224 species_list(2) = "M+"
225 species_list(3) = "M-"
226 n_species = 3
227 n_reactions = 2
228
229 ! Ionization reaction
230 if (gas_constant_density) then
231 reactions(1)%ix_in = [1]
232 reactions(1)%ix_out = [1, 2]
233 reactions(1)%multiplicity_out = [2, 1]
234 reactions(1)%n_species_in = 2
235 reactions(1)%rate_type = rate_tabulated_field
236 reactions(1)%rate_factor = 1.0_dp
237 reactions(1)%x_data = td_tbl%x
238 reactions(1)%y_data = td_tbl%rows_cols(:, td_alpha) * &
239 td_tbl%rows_cols(:, td_mobility) * reactions(1)%x_data * &
241 reactions(1)%description = "e + M -> e + e + M+"
242
243 ! Attachment reaction
244 reactions(2)%ix_in = [1]
245 reactions(2)%ix_out = [3]
246 reactions(2)%multiplicity_out = [1]
247 reactions(2)%n_species_in = 2
248 reactions(2)%rate_type = rate_tabulated_field
249 reactions(2)%rate_factor = 1.0_dp
250 reactions(2)%x_data = td_tbl%x
251 reactions(2)%y_data = td_tbl%rows_cols(:, td_eta) * &
252 td_tbl%rows_cols(:, td_mobility) * reactions(2)%x_data * &
254 reactions(2)%description = "e + M -> M-"
255 else
256 error stop "Varying gas density not yet supported"
257 end if
258 end if
259
260 ! In case of energy equation, add an extra species
262 n_species = n_species + 1
263 species_list(n_species) = "e_energy"
264 end if
265
266 ! Convert names to simple ascii
267 do n = 1, n_species
268 tmp_name = species_list(n)
269 call to_simple_ascii(trim(tmp_name), species_list(n), &
271 end do
272
273 ! Also store in more memory-efficient structure
274 do n = 1, n_reactions
275 tiny_react(n)%ix_in = reactions(n)%ix_in
276 tiny_react(n)%ix_out = reactions(n)%ix_out
277 tiny_react(n)%multiplicity_out = reactions(n)%multiplicity_out
278 end do
279
280 ! Gas species are not stored in the tree for now
283
284 do n = n_gas_species+1, n_species
285 call af_add_cc_variable(tree, trim(species_list(n)), &
286 n_copies=af_advance_num_steps(time_integrator)+1, &
287 ix=species_itree(n))
288 end do
289
290 ! Store list with only charged species
291 n = count(species_charge(1:n_species) /= 0)
292 allocate(charged_species_itree(n))
293 allocate(charged_species_charge(n))
294
295 i = 0
296 do n = 1, n_species
297 if (species_charge(n) /= 0) then
298 i = i + 1
301 end if
302 end do
303
304 call check_charge_conservation()
305
306 ! Specify reaction types
307 i_elec = species_index("e")
308
309 do n = 1, n_reactions
310 if (any(reactions(n)%ix_in == i_elec) .and. &
311 .not. any(reactions(n)%ix_out == i_elec) .and. &
312 .not. any(species_charge(reactions(n)%ix_in) > 0)) then
313 ! In: an electron and no positive ions, out: no electrons
314 reactions(n)%reaction_type = attachment_reaction
315 else if (any(reactions(n)%ix_in == i_elec) .and. &
316 any(reactions(n)%ix_out == i_elec .and. &
317 reactions(n)%multiplicity_out == 2)) then
318 ! An electron in and out (with multiplicity 2)
319 reactions(n)%reaction_type = ionization_reaction
320 else if (any(species_charge(reactions(n)%ix_in) /= 0) .and. &
321 .not. any(species_charge(reactions(n)%ix_out) /= 0)) then
322 ! In: charged species, out: no charged species
323 reactions(n)%reaction_type = recombination_reaction
324 else if (all(reactions(n)%ix_in /= i_elec) .and. &
325 any(reactions(n)%ix_out == i_elec)) then
326 ! In: no electrons, out: an electron
327 reactions(n)%reaction_type = detachment_reaction
328 end if
329 end do
330
331 ! Create lookup tables for tabulated reaction data. First determine number
332 ! of reactions. In case of an energy equation, ionization and attachment
333 ! reactions are converted to use the electron energy instead of the field.
334 i = 0
335 j = count(reactions(1:n_reactions)%rate_type == rate_tabulated_energy)
336 do n = 1, n_reactions
337 if (reactions(n)%rate_type == rate_tabulated_field) then
338 rtype = reactions(n)%reaction_type
340 .or. rtype == attachment_reaction)) then
341 j = j + 1
342 else
343 i = i + 1
344 end if
345 end if
346 end do
347
348 chemtbl_fld = lt_create(td_tbl%x(1), td_tbl%x(td_tbl%n_points), &
350 chemtbl_ee = lt_create(0.0_dp, td_max_ev, &
352
353 i = 0
354 j = 0
355 do n = 1, n_reactions
356 if (reactions(n)%rate_type == rate_tabulated_field) then
357 rtype = reactions(n)%reaction_type
358
360 .or. rtype == attachment_reaction)) then
361 reactions(n)%rate_type = rate_tabulated_energy
362 j = j + 1
363 reactions(n)%lookup_table_index = j
364 ! Convert field to energy
365 call table_set_column(chemtbl_ee, j, &
366 lt_get_col(td_tbl, td_energy_ev, reactions(n)%x_data), &
367 reactions(n)%y_data)
368 else
369 i = i + 1
370 reactions(n)%lookup_table_index = i
371 call table_set_column(chemtbl_fld, i, reactions(n)%x_data, &
372 reactions(n)%y_data)
373 end if
374 else if (reactions(n)%rate_type == rate_tabulated_energy) then
375 j = j + 1
376 reactions(n)%lookup_table_index = j
377 call table_set_column(chemtbl_ee, j, reactions(n)%x_data, &
378 reactions(n)%y_data)
379 end if
380 end do
381
382 call store_stochastic_species()
383
384 print *, "--- List of reactions ---"
385 do n = 1, n_reactions
386 write(*, "(I4,' (',I0,') ',A15,A)") n, reactions(n)%n_species_in, &
387 reaction_names(reactions(n)%reaction_type), &
388 reactions(n)%description
389 end do
390 print *, "-------------------------"
391 print *, ""
392 print *, "--- List of gas species ---"
393 do n = 1, n_gas_species
394 write(*, "(I4,A)") n, ": " // species_list(n)
395 end do
396 print *, "-------------------------"
397 print *, ""
398 print *, "--- List of plasma species ---"
399 do n = n_gas_species+1, n_species
400 write(*, "(I4,A)") n, ": " // species_list(n)
401 end do
402 print *, "-------------------------"
403
404 ! Optionally modify rates
405 call chemistry_modify_rates(cfg)
406
407 end subroutine chemistry_initialize
408
409 !> Store stochastic species, for which species have a prefix "stoch_"
410 subroutine store_stochastic_species()
411 integer :: n, i_stoch, namelen, iv
412
413 i_stoch = 0
414 do n = n_gas_species+1, n_species
415 namelen = len_trim(species_list(n))
416
417 if (species_list(n)(1:6) == "stoch_") then
418 i_stoch = i_stoch + 1
419
420 stochastic_species_list(i_stoch)%i_from = species_itree(n)
421 iv = species_index(species_list(n)(7:))
422
423 if (iv == -1) then
424 print *, "Could not find [", species_list(n)(7:), "]"
425 print *, "which should be present for stochastic species (stoch_)"
426 error stop "Species for stochastic reaction not found"
427 else
428 stochastic_species_list(i_stoch)%i_to = species_itree(iv)
429 end if
430 end if
431 end do
432
433 n_stochastic_species = i_stoch
434
435 end subroutine store_stochastic_species
436
437 !> Convert stochastic species into regular species, using random numbers
439 type(af_t), intent(inout) :: tree !< Tree
440 type(rng_t), intent(inout) :: rng !< Random number generator
441 integer :: lvl, iblock, id, ijk, n
442 integer :: i_to, i_from, proc_id, n_procs
443 real(dp) :: dv, lambda, product_dr, n_produced
444#if NDIM == 2
445 real(dp), parameter :: two_pi = 2 * acos(-1.0_dp)
446#endif
447 type(prng_t) :: prng
448
449 ! Initialize parallel random numbers
450 n_procs = omp_get_max_threads()
451 call prng%init_parallel(n_procs, rng)
452
453 !$omp parallel private(lvl, iblock, id, proc_id, dV, i_to, &
454 !$omp &i_from, n, n_produced, IJK, lambda, product_dr)
455 proc_id = 1+omp_get_thread_num()
456
457 do lvl = 1, tree%highest_lvl
458 !$omp do
459 do iblock = 1, size(tree%lvls(lvl)%leaves)
460 id = tree%lvls(lvl)%leaves(iblock)
461 product_dr = product(tree%boxes(id)%dr)
462
463 do n = 1, n_stochastic_species
464 i_to = stochastic_species_list(n)%i_to
465 i_from = stochastic_species_list(n)%i_from
466
467 do kji_do(1, tree%n_cell)
468#if NDIM == 2
469 if (tree%coord_t == af_cyl) then
470 dv = two_pi * product_dr * af_cyl_radius_cc(tree%boxes(id), i)
471 else
472 dv = product_dr
473 end if
474#else
475 dv = product_dr
476#endif
477
478 lambda = dv * tree%boxes(id)%cc(ijk, i_from)
479 n_produced = prng%rngs(proc_id)%poisson(lambda)
480
481 tree%boxes(id)%cc(ijk, i_from) = 0.0_dp
482 tree%boxes(id)%cc(ijk, i_to) = tree%boxes(id)%cc(ijk, i_to) + &
483 n_produced/dv
484 end do; close_do
485 end do
486
487 end do
488 !$omp end do
489 end do
490 !$omp end parallel
491
492 call prng%update_seed(rng)
493
495
496 !> Modify reaction rates for sensitivity analysis
497 subroutine chemistry_modify_rates(cfg)
498 use m_config
499 type(cfg_t), intent(inout) :: cfg
500 integer :: n_modified, n, dummy_int(0), ix
501 real(dp) :: dummy_real(0)
502 integer, allocatable :: reaction_ix(:)
503 real(dp), allocatable :: rate_factors(:)
504
505 call cfg_add(cfg, "input_data%modified_reaction_ix", dummy_int, &
506 "Indices of reactions to be modified", .true.)
507 call cfg_add(cfg, "input_data%modified_rate_factors", dummy_real, &
508 "Reaction rate factors for modified reactions", .true.)
509 call cfg_get_size(cfg, "input_data%modified_reaction_ix", n_modified)
510 call cfg_get_size(cfg, "input_data%modified_rate_factors", n)
511
512 if (n /= n_modified) &
513 error stop "size(modified_reaction_ix) /= size(modified_rate_factors)"
514
515 if (n_modified > 0) then
516 allocate(reaction_ix(n_modified), rate_factors(n_modified))
517 call cfg_get(cfg, "input_data%modified_reaction_ix", reaction_ix)
518 call cfg_get(cfg, "input_data%modified_rate_factors", rate_factors)
519
520 if (minval(rate_factors) < 0) &
521 error stop "Negative value in modified_rate_factors"
522 if (minval(reaction_ix) < 1 .or. maxval(reaction_ix) > n_reactions) &
523 error stop "modified_reaction_ix outside valid range"
524
525 do n = 1, n_modified
526 ix = reaction_ix(n)
527 reactions(ix)%rate_factor = reactions(ix)%rate_factor * rate_factors(n)
528 end do
529 end if
530
531 end subroutine chemistry_modify_rates
532
533 !> Write a summary of the reactions (TODO) and the ionization and attachment
534 !> coefficients (if working at constant pressure)
535 subroutine chemistry_write_summary(fname)
536 use m_gas
538 character(len=*), intent(in) :: fname
539 real(dp), allocatable :: fields(:), energies(:)
540 real(dp), allocatable :: rates(:, :)
541 real(dp), allocatable :: eta(:), alpha(:), src(:), loss(:)
542 real(dp), allocatable :: v(:), mu(:), diff(:)
543 integer :: n, n_fields, i_elec
544 integer :: my_unit
545
546 if (gas_constant_density) then
547 i_elec = species_index("e")
548 n_fields = td_tbl%n_points
549
550 if (n_fields < 3) error stop "Not enough data for linear extrapolation"
551
552 allocate(fields(n_fields))
553 allocate(energies(n_fields))
554 allocate(rates(n_fields, n_reactions))
555 allocate(eta(n_fields), alpha(n_fields), src(n_fields), loss(n_fields))
556
557 fields = td_tbl%x
558
559 if (model_has_energy_equation) then
560 energies = lt_get_col(td_tbl, td_energy_ev, td_tbl%x)
561 call get_rates(fields, rates, n_fields, energies)
562 else
563 call get_rates(fields, rates, n_fields)
564 end if
565
566 loss(:) = 0.0_dp
567 src(:) = 0.0_dp
568
569 do n = 1, n_reactions
570 if (reactions(n)%reaction_type == attachment_reaction) then
571 loss(:) = loss(:) + rates(:, n)
572 else if (reactions(n)%reaction_type == ionization_reaction) then
573 src(:) = src(:) + rates(:, n)
574 end if
575 end do
576
577 allocate(diff(n_fields))
578 diff = lt_get_col(td_tbl, td_diffusion, fields)
579
580 allocate(mu(n_fields))
581 allocate(v(n_fields))
582 mu = lt_get_col(td_tbl, td_mobility, fields)
583 v = mu * fields * townsend_to_si
584
585 ! v(1) is zero, so extrapolate linearly
586 eta(2:) = loss(2:) / v(2:)
587 eta(1) = 2 * eta(2) - eta(3)
588 alpha(2:) = src(2:) / v(2:)
589 alpha(1) = 2 * alpha(2) - alpha(3)
590
591 ! Write to a file
592 open(newunit=my_unit, file=trim(fname), action="write")
593 write(my_unit, "(A)") &
594 "E/N[Td] E[V/m] Electron_mobility[m^2/(Vs)] &
595 &Electron_diffusion[m^2/s] Townsend_ioniz._coef._alpha[1/m] &
596 &Townsend_attach._coef._eta[1/m] Ionization_rate[1/s] &
597 &Attachment_rate[1/s]"
598 do n = 1, n_fields
599 write(my_unit, *) fields(n), &
600 fields(n) * townsend_to_si * gas_number_density, &
601 mu(n) / gas_number_density, &
602 diff(n) / gas_number_density, alpha(n), eta(n), &
603 src(n), loss(n)
604 end do
605 write(my_unit, *) ""
606 close(my_unit)
607 end if
608 end subroutine chemistry_write_summary
609
610 subroutine check_charge_conservation()
611 integer :: n, q_in, q_out
612
613 do n = 1, n_reactions
614 q_in = sum(species_charge(reactions(n)%ix_in))
615 q_out = sum(species_charge(reactions(n)%ix_out) * &
616 reactions(n)%multiplicity_out)
617 if (q_in /= q_out) then
618 print *, trim(reactions(n)%description)
619 error stop "Charge is not conserved in the above reaction"
620 end if
621 end do
622 end subroutine check_charge_conservation
623
624 !> Get the breakdown field in Townsend
625 subroutine chemistry_get_breakdown_field(field_td, min_growth_rate)
627 !> Breakdown field in Townsend
628 real(dp), intent(out) :: field_td
629 !> Minimal growth rate for breakdown
630 real(dp), intent(in) :: min_growth_rate
631
632 integer :: n, n_fields
633 real(dp), allocatable :: energies(:)
634 real(dp), allocatable :: fields(:), rates(:, :), src(:), loss(:)
635
636 n_fields = td_tbl%n_points
637 allocate(fields(n_fields))
638 allocate(energies(n_fields))
639 allocate(rates(n_fields, n_reactions))
640 allocate(src(n_fields), loss(n_fields))
641
642 fields = td_tbl%x
643 if (model_has_energy_equation) then
644 energies = lt_get_col(td_tbl, td_energy_ev, td_tbl%x)
645 call get_rates(fields, rates, n_fields, energies)
646 else
647 call get_rates(fields, rates, n_fields)
648 end if
649
650 loss(:) = 0.0_dp
651 src(:) = 0.0_dp
652
653 do n = 1, n_reactions
654 if (reactions(n)%reaction_type == attachment_reaction) then
655 loss(:) = loss(:) + rates(:, n)
656 else if (reactions(n)%reaction_type == ionization_reaction) then
657 src(:) = src(:) + rates(:, n)
658 end if
659 end do
660
661 do n = n_fields, 1, -1
662 if (src(n) - loss(n) < min_growth_rate) exit
663 end do
664
665 field_td = 0.0_dp
666 if (n > 0) field_td = fields(n)
667 end subroutine chemistry_get_breakdown_field
668
669 !> Compute reaction rates
670 !>
671 !> @todo These reactions do not take into account a variable gas_temperature
672 subroutine get_rates(fields, rates, n_cells, energy_eV)
674 use m_gas
676 integer, intent(in) :: n_cells !< Number of cells
677 real(dp), intent(in) :: fields(n_cells) !< The field (in Td) in the cells
678 real(dp), intent(out) :: rates(n_cells, n_reactions) !< The reaction rates
679 real(dp), intent(in), optional :: energy_ev(n_cells) !< Electron energy in eV
680 integer :: n, n_coeff
681 real(dp) :: c0, c(rate_max_num_coeff)
682 real(dp) :: te(n_cells) !> Electron Temperature in Kelvin
683 logical :: te_available
684
685 ! Conversion factor to go from eV to Kelvin
686 real(dp), parameter :: electron_ev_to_k = 2 * uc_elec_volt / &
688
689 te_available = .false.
690
691 do n = 1, n_reactions
692 ! A factor that the reaction rate is multiplied with, for example to
693 ! account for a constant gas number density and if the length unit is cm instead of m
694 c0 = reactions(n)%rate_factor
695
696 ! Coefficients for the reaction rate
697 n_coeff = reactions(n)%n_coeff
698 c(1:n_coeff) = reactions(n)%rate_data(1:n_coeff)
699
700 select case (reactions(n)%rate_type)
702 if (.not. present(energy_ev)) error stop "energy_eV required"
703 rates(:, n) = c0 * lt_get_col(chemtbl_ee, &
704 reactions(n)%lookup_table_index, energy_ev)
705 case (rate_tabulated_field)
706 rates(:, n) = c0 * lt_get_col(chemtbl_fld, &
707 reactions(n)%lookup_table_index, fields)
708 case (rate_analytic_constant)
709 rates(:, n) = c0 * c(1)
710 case (rate_analytic_linear)
711 rates(:, n) = c0 * c(1) * (fields - c(2))
712 case (rate_analytic_exp_v1)
713 rates(:, n) = c0 * c(1) * exp(-(c(2) / (c(3) + fields))**2)
714 case (rate_analytic_exp_v2)
715 rates(:, n) = c0 * c(1) * exp(-(fields/c(2))**2)
716 case (rate_analytic_k1)
717 if (.not. te_available) then
718 ! Note that we could use energy_eV if present, but this energy is
719 ! not guaranteed to be well-behaved
720 te = electron_ev_to_k * lt_get_col(td_tbl, td_energy_ev, fields)
721 te_available = .true.
722 end if
723 rates(:, n) = c0 * c(1) * (300 / te)**c(2)
724 case (rate_analytic_k3)
725 if (.not. te_available) then
726 te = electron_ev_to_k * lt_get_col(td_tbl, td_energy_ev, fields)
727 te_available = .true.
728 end if
729 ! We convert boltzmann_const from J / K to eV / K
730 rates(:, n) = c0 * (c(1) * ((uc_boltzmann_const / uc_elec_volt) * te + c(2))**2 - c(3)) * c(4)
731 case (rate_analytic_k4)
732 rates(:, n) = c0 * c(1) * (gas_temperature / 300)**c(2) * exp(-c(3) / gas_temperature)
733 case (rate_analytic_k5)
734 rates(:, n) = c0 * c(1) * exp(-c(2) / gas_temperature)
735 case (rate_analytic_k6)
736 rates(:, n) = c0 * c(1) * gas_temperature**c(2)
737 case (rate_analytic_k7)
738 rates(:, n) = c0 * c(1) * (gas_temperature / c(2))**c(3)
739 case (rate_analytic_k8)
740 rates(:, n) = c0 * c(1) * (300 / gas_temperature)**c(2)
741 case (rate_analytic_k9)
742 rates(:, n) = c0 * c(1) * exp(-c(2) * gas_temperature)
743 case (rate_analytic_k10)
744 rates(:, n) = c0 * 10**(c(1) + c(2) * (gas_temperature - 300))
745 case (rate_analytic_k11)
746 rates(:, n) = c0 * c(1) * (300 / gas_temperature)**c(2) * exp(-c(3) / gas_temperature)
747 case (rate_analytic_k12)
748 rates(:, n) = c0 * c(1) * gas_temperature**c(2) * exp(-c(3) / gas_temperature)
749 case (rate_analytic_k13)
750 rates(:, n) = c0 * c(1) * exp(-(c(2) / (c(3) + fields))**c(4))
751 case (rate_analytic_k14)
752 rates(:, n) = c0 * c(1) * exp(-(fields / c(2))**c(3))
753 case (rate_analytic_k15)
754 ! Note that this reaction depends on Ti, ionic temperature.
755 ! According to Galimberti(1979): Ti = T_gas + fields/c(3),
756 ! with c(3) = 0.18 Td/Kelvin, UC_boltzmann_const is in J/Kelvin, c(2)
757 ! is given in Joule in the input file
758 rates(:, n) = c0 * c(1) * exp(-(c(2) / (uc_boltzmann_const * &
759 (gas_temperature + fields/c(3))))**c(4))
760 case (rate_analytic_k16)
761 if (.not. te_available) then
762 te = electron_ev_to_k * lt_get_col(td_tbl, td_energy_ev, fields)
763 te_available = .true.
764 end if
765 rates(:, n) = c0 * c(1) * (300/te)**c(2) * exp(-c(3)/gas_temperature) * &
766 exp(c(4) * (te - gas_temperature) / (te * gas_temperature))
767 end select
768 end do
769 end subroutine get_rates
770
771 !> Compute derivatives due to chemical reactions. Note that the 'rates'
772 !> argument is modified.
773 subroutine get_derivatives(dens, rates, derivs, n_cells)
774 integer, intent(in) :: n_cells
775 !> Species densities
776 real(dp), intent(in) :: dens(n_cells, n_species)
777 !> On input, reaction rate coefficients. On output, actual reaction rates.
778 real(dp), intent(inout) :: rates(n_cells, n_reactions)
779 !> Derivatives of the chemical species
780 real(dp), intent(out) :: derivs(n_cells, n_species)
781 integer :: n, i, ix
782
783 derivs(:, :) = 0.0_dp
784
785 ! Loop over reactions and add to derivative
786 do n = 1, n_reactions
787 ! Determine production rate of 'full' reaction
788 rates(:, n) = rates(:, n) * &
789 product(dens(:, tiny_react(n)%ix_in), dim=2)
790
791 ! Input species are removed
792 do i = 1, size(tiny_react(n)%ix_in)
793 ix = tiny_react(n)%ix_in(i)
794 derivs(:, ix) = derivs(:, ix) - rates(:, n)
795 end do
796
797 ! Output species are created
798 do i = 1, size(tiny_react(n)%ix_out)
799 ix = tiny_react(n)%ix_out(i)
800 derivs(:, ix) = derivs(:, ix) + rates(:, n) * &
801 tiny_react(n)%multiplicity_out(i)
802 end do
803 end do
804 end subroutine get_derivatives
805
806 !> Try to read a list species whose production will be ignored
807 subroutine read_ignored_species(filename, ignored_species)
808 character(len=*), intent(in) :: filename
809 character(len=comp_len), allocatable, intent(inout) :: &
810 ignored_species(:)
811 character(len=comp_len) :: tmp(max_num_species)
812 character(len=string_len) :: line
813 integer :: my_unit, n_ignored
814
815 n_ignored = 0
816
817 open(newunit=my_unit, file=filename, action="read")
818
819 ! Find list of reactions
820 do
821 read(my_unit, "(A)", end=998) line
822 line = adjustl(line)
823
824 if (line == "ignored_species") then
825 ! Read next line starting with at least 5 dashes
826 read(my_unit, "(A)") line
827 if (line(1:5) /= "-----") &
828 error stop "ignored_species not followed by -----"
829 exit
830 end if
831 end do
832
833 ! Read ignored species, one per line
834 do
835 read(my_unit, "(A)", end=999) line
836 line = adjustl(line)
837
838 ! Ignore comments
839 if (line(1:1) == "#") cycle
840
841 ! Exit when we read a line of dashes
842 if (line(1:5) == "-----") exit
843
844 n_ignored = n_ignored + 1
845 read(line, *) tmp(n_ignored)
846 end do
847
848998 close(my_unit)
849 ignored_species = tmp(1:n_ignored)
850 return
851
852 ! Error messages
853999 error stop "read_ignored_species: no closing dashes"
854 end subroutine read_ignored_species
855
856 !> Read reactions from a file
857 subroutine read_reactions(filename, read_success)
858 character(len=*), intent(in) :: filename
859 logical, intent(out) :: read_success
860 character(len=string_len) :: line
861 integer, parameter :: field_len = 80
862 character(len=field_len) :: data_value(max_num_reactions)
863 character(len=field_len) :: reaction(max_num_reactions)
864 character(len=field_len) :: how_to_get(max_num_reactions)
865 character(len=10) :: length_unit(max_num_reactions)
866 type(reaction_t) :: new_reaction
867 integer :: my_unit
868 integer :: n_reactions_found
869 integer, parameter :: n_fields_max = 40
870 integer :: i0(n_fields_max), i1(n_fields_max)
871 integer :: n, i, k, n_found, lo, hi
872 logical :: keep_reaction
873
874 character(len=comp_len), allocatable :: ignored_species(:)
875
876 type group
877 character(len=name_len) :: name
878 character(len=field_len), allocatable :: members(:)
879 end type group
880
881 integer, parameter :: max_groups = 10
882 integer :: i_group, group_size
883 type(group) :: groups(max_groups)
884
885 call read_ignored_species(filename, ignored_species)
886
887 open(newunit=my_unit, file=filename, action="read")
888
889 n_reactions_found = 0
890 i_group = 0
891 group_size = 0
892 read_success = .false.
893
894 ! Find list of reactions
895 do
896 read(my_unit, "(A)", end=998) line
897 line = adjustl(line)
898
899 if (line == "reaction_list") then
900 ! Read next line starting with at least 5 dashes
901 read(my_unit, "(A)") line
902 if (line(1:5) /= "-----") &
903 error stop "read_reactions: reaction_list not followed by -----"
904 exit
905 end if
906 end do
907
908 ! Start reading reactions
909 do
910 read(my_unit, "(A)", end=999) line
911 line = adjustl(line)
912
913 ! Ignore comments
914 if (line(1:1) == "#") cycle
915
916 ! Exit when we read a line of dashes
917 if (line(1:5) == "-----") exit
918
919 ! Group syntax for multiple species
920 if (line(1:1) == "@") then
921 call get_fields_string(line, "=,", n_fields_max, n_found, i0, i1)
922 i_group = i_group + 1
923
924 if (i_group > max_groups) error stop "Too many groups"
925
926 if (i_group == 1) then
927 group_size = n_found - 1
928 else if (n_found - 1 /= group_size) then
929 print *, trim(line)
930 error stop "Groups for a reaction should have the same size"
931 end if
932
933 groups(i_group)%name = line(i0(1):i1(1))
934 allocate(groups(i_group)%members(n_found - 1))
935
936 do n = 2, n_found
937 groups(i_group)%members(n-1) = adjustl(line(i0(n):i1(n)))
938 end do
939 cycle
940 end if
941
942 if (i_group > 0) then
943 ! Handle groups
944 lo = n_reactions_found
945 hi = n_reactions_found+group_size-1
946 reaction(lo+1:hi) = reaction(lo)
947 how_to_get(lo+1:hi) = how_to_get(lo)
948 data_value(lo+1:hi) = data_value(lo)
949 length_unit(lo+1:hi) = length_unit(lo)
950 n_reactions_found = hi
951
952 do k = 1, group_size
953 do i = 1, i_group
954 n = lo + k - 1
955 reaction(n) = string_replace(reaction(n), &
956 groups(i)%name, groups(i)%members(k))
957 how_to_get(n) = string_replace(how_to_get(n), &
958 groups(i)%name, groups(i)%members(k))
959 data_value(n) = string_replace(data_value(n), &
960 groups(i)%name, groups(i)%members(k))
961 end do
962 end do
963
964 ! Check if there are duplicate reactions
965 do n = lo, hi
966 if (count(reaction(lo:hi) == reaction(n)) > 1 .and. &
967 count(data_value(lo:hi) == data_value(n)) > 1) then
968 do k = lo, hi
969 print *, trim(reaction(k)), ",", data_value(k)
970 end do
971 error stop "Groups lead to duplicate reactions"
972 end if
973 end do
974
975 i_group = 0
976 group_size = 0
977 end if
978
979 call get_fields_string(line, ",", n_fields_max, n_found, i0, i1)
980
981 if (n_found < 3 .or. n_found > 4) then
982 print *, trim(line)
983 error stop "Invalid chemistry syntax"
984 end if
985
986 if (n_reactions_found >= max_num_reactions) &
987 error stop "Too many reactions, increase max_num_reactions"
988
989 n_reactions_found = n_reactions_found + 1
990 reaction(n_reactions_found) = line(i0(1):i1(1))
991 how_to_get(n_reactions_found) = line(i0(2):i1(2))
992 data_value(n_reactions_found) = line(i0(3):i1(3))
993
994 ! Fourth entry can hold a custom length unit, default is meter
995 if (n_found > 3) then
996 length_unit(n_reactions_found) = line(i0(4):i1(4))
997 else
998 length_unit(n_reactions_found) = "m"
999 end if
1000 end do
1001
1002998 continue
1003
1004 ! Close the file (so that we can re-open it for reading data)
1005 close(my_unit)
1006
1007 n_reactions = 0
1008
1009 ! Now parse the reactions
1010 do n = 1, n_reactions_found
1011 call parse_reaction(trim(reaction(n)), new_reaction, &
1012 ignored_species, keep_reaction)
1013
1014 if (keep_reaction) then
1016 else
1017 cycle
1018 end if
1019
1020 new_reaction%description = trim(reaction(n))
1021
1022 ! IMPORTANT: If you change the reactions below, do not forget to update
1023 ! documentation/chemistry.md accordingly!
1024 select case (how_to_get(n))
1025 case ("field_table")
1026 ! Reaction data should be present in the same file
1027 call read_reaction_table(filename, &
1028 trim(data_value(n)), new_reaction)
1029 new_reaction%n_coeff = 0
1030 case ("c1")
1031 new_reaction%rate_type = rate_analytic_constant
1032 new_reaction%n_coeff = 1
1033 read(data_value(n), *) new_reaction%rate_data(1)
1034 case ("c1*(Td-c2)")
1035 new_reaction%rate_type = rate_analytic_linear
1036 new_reaction%n_coeff = 2
1037 read(data_value(n), *) new_reaction%rate_data(1:2)
1038 case ("c1*exp(-(c2/(c3+Td))**2)")
1039 new_reaction%rate_type = rate_analytic_exp_v1
1040 new_reaction%n_coeff = 3
1041 read(data_value(n), *) new_reaction%rate_data(1:3)
1042 case ("c1*exp(-(Td/c2)**2)")
1043 new_reaction%rate_type = rate_analytic_exp_v2
1044 new_reaction%n_coeff = 2
1045 read(data_value(n), *) new_reaction%rate_data(1:2)
1046 case ("c1*(300/Te)**c2")
1047 new_reaction%rate_type = rate_analytic_k1
1048 new_reaction%n_coeff = 2
1049 read(data_value(n), *) new_reaction%rate_data(1:2)
1050 case ("(c1*(kB_eV*Te+c2)**2-c3)*c4")
1051 new_reaction%rate_type = rate_analytic_k3
1052 new_reaction%n_coeff = 4
1053 read(data_value(n), *) new_reaction%rate_data(1:4)
1054 case ("c1*(Tg/300)**c2*exp(-c3/Tg)")
1055 new_reaction%rate_type = rate_analytic_k4
1056 new_reaction%n_coeff = 3
1057 read(data_value(n), *) new_reaction%rate_data(1:3)
1058 case ("c1*exp(-c2/Tg)")
1059 new_reaction%rate_type = rate_analytic_k5
1060 new_reaction%n_coeff = 2
1061 read(data_value(n), *) new_reaction%rate_data(1:2)
1062 case ("c1*Tg**c2")
1063 new_reaction%rate_type = rate_analytic_k6
1064 new_reaction%n_coeff = 2
1065 read(data_value(n), *) new_reaction%rate_data(1:2)
1066 case ("c1*(Tg/c2)**c3")
1067 new_reaction%rate_type = rate_analytic_k7
1068 new_reaction%n_coeff = 3
1069 read(data_value(n), *) new_reaction%rate_data(1:3)
1070 case ("c1*(300/Tg)**c2")
1071 new_reaction%rate_type = rate_analytic_k8
1072 new_reaction%n_coeff = 2
1073 read(data_value(n), *) new_reaction%rate_data(1:2)
1074 case ("c1*exp(-c2*Tg)")
1075 new_reaction%rate_type = rate_analytic_k9
1076 new_reaction%n_coeff = 2
1077 read(data_value(n), *) new_reaction%rate_data(1:2)
1078 case ("10**(c1+c2*(Tg-300))")
1079 new_reaction%rate_type = rate_analytic_k10
1080 new_reaction%n_coeff = 2
1081 read(data_value(n), *) new_reaction%rate_data(1:2)
1082 case ("c1*(300/Tg)**c2*exp(-c3/Tg)")
1083 new_reaction%rate_type = rate_analytic_k11
1084 new_reaction%n_coeff = 3
1085 read(data_value(n), *) new_reaction%rate_data(1:3)
1086 case ("c1*Tg**c2*exp(-c3/Tg)")
1087 new_reaction%rate_type = rate_analytic_k12
1088 new_reaction%n_coeff = 3
1089 read(data_value(n), *) new_reaction%rate_data(1:3)
1090 case ("c1*exp(-(c2/(c3+Td))**c4)")
1091 new_reaction%rate_type = rate_analytic_k13
1092 new_reaction%n_coeff = 4
1093 read(data_value(n), *) new_reaction%rate_data(1:4)
1094 case ("c1*exp(-(Td/c2)**c3)")
1095 new_reaction%rate_type = rate_analytic_k14
1096 new_reaction%n_coeff = 3
1097 read(data_value(n), *) new_reaction%rate_data(1:3)
1098 case ("c1*exp(-(c2/(kb*(Tg+Td/c3)))**c4)")
1099 new_reaction%rate_type = rate_analytic_k15
1100 new_reaction%n_coeff = 4
1101 read(data_value(n), *) new_reaction%rate_data(1:4)
1102 case ("c1*(300/Te)**c2*exp(-c3/Tg)*exp(c4*(Te-Tg)/(Te*Tg))")
1103 new_reaction%rate_type = rate_analytic_k16
1104 new_reaction%n_coeff = 4
1105 read(data_value(n), *) new_reaction%rate_data(1:4)
1106 case default
1107 print *, "Unknown rate type: ", trim(how_to_get(n))
1108 print *, "For reaction: ", trim(reaction(n))
1109 print *, "In file: ", trim(filename)
1110 print *, "See documentation/chemistry.md"
1111 if (how_to_get(n) /= "field_table" .and. &
1112 index(how_to_get(n), "c1") == 0) then
1113 print *, "You probably use the old reaction format"
1114 print *, "Try to use tools/chemistry_update_reactions.sh"
1115 print *, "See also the chemistry documentation"
1116 end if
1117 error stop "Unknown chemical reaction"
1118 end select
1119
1120 ! Correct for length unit in the rate function (e.g. [k] = cm3/s)
1121 select case (length_unit(n))
1122 case ("m")
1123 continue
1124 case ("cm")
1125 ! 1 cm^3 = 1e-6 m^3
1126 new_reaction%rate_factor = new_reaction%rate_factor * &
1127 1e-6_dp**(new_reaction%n_species_in-1)
1128 case default
1129 print *, "For reaction: ", trim(reaction(n))
1130 print *, "Invalid length unit: ", trim(length_unit(n))
1131 error stop
1132 end select
1133
1134 reactions(n_reactions) = new_reaction
1135 end do
1136
1137 if (n_reactions > 0) read_success = .true.
1138 return
1139
1140 ! Error messages
1141999 error stop "read_reactions: no closing dashes for reaction list"
1142 end subroutine read_reactions
1143
1144 !> Read a reaction table
1145 subroutine read_reaction_table(filename, dataname, rdata)
1147 character(len=*), intent(in) :: filename
1148 character(len=*), intent(in) :: dataname
1149 type(reaction_t), intent(inout) :: rdata
1150
1151 rdata%rate_type = rate_tabulated_field
1152 call table_from_file(filename, dataname, rdata%x_data, rdata%y_data)
1153 end subroutine read_reaction_table
1154
1155 !> Parse a reaction and store it
1156 subroutine parse_reaction(reaction_text, reaction, ignored_species, &
1157 keep_reaction)
1158 use m_gas
1159 character(len=*), intent(in) :: reaction_text
1160 type(reaction_t), intent(out) :: reaction
1161 character(len=comp_len), intent(in) :: ignored_species(:)
1162 logical, intent(out) :: keep_reaction
1163 integer, parameter :: max_components = 100
1164 character(len=comp_len) :: component
1165 integer :: i, ix, n, n_found, multiplicity
1166 integer :: n_in, n_out
1167 integer :: i0(max_components)
1168 integer :: i1(max_components)
1169 integer :: ix_in(max_components)
1170 integer :: ix_out(max_components)
1171 integer :: multiplicity_out(max_components)
1172 logical :: left_side, is_gas_species
1173 real(dp) :: rfactor
1174
1175 call get_fields_string(reaction_text, " ", max_components, n_found, i0, i1)
1176
1177 left_side = .true.
1178 keep_reaction = .true.
1179 n_in = 0
1180 n_out = 0
1181 rfactor = 1.0_dp
1182 reaction%n_species_in = 0
1183
1184 do n = 1, n_found
1185 component = reaction_text(i0(n):i1(n))
1186
1187 if (component == "+") cycle
1188
1189 if (component == "->") then
1190 left_side = .false.
1191 cycle
1192 end if
1193
1194 ! Assume we have a multiplicity less than 10
1195 if (lge(component(1:1), '1') .and. lle(component(1:1), '9')) then
1196 read(component(1:1), *) multiplicity
1197 component = component(2:)
1198 else
1199 multiplicity = 1
1200 end if
1201
1202 if (left_side) then
1203 reaction%n_species_in = reaction%n_species_in + multiplicity
1204 end if
1205
1206 ! If the gas density is constant, remove gas species from the reaction
1207 if (gas_constant_density) then
1208 ix = gas_index(component)
1209 if (ix /= -1) then
1210 ! Multiply reaction by constant density
1211 if (left_side) rfactor = rfactor * gas_densities(ix)
1212 cycle
1213 end if
1214
1215 if (component == "M") then
1216 ! Assume this stands for 'any molecule'
1217 if (left_side) rfactor = rfactor * gas_number_density
1218 cycle
1219 end if
1220 end if
1221
1222 ! Handle ignored species
1223 if (findloc(ignored_species, component, dim=1) > 0) then
1224 is_gas_species = (gas_index(component) > 0 .or. component == "M")
1225
1226 if (left_side .and. .not. is_gas_species) then
1227 ! Ignore the whole reaction, since this species will not be
1228 ! produced (and will thus have zero density)
1229 keep_reaction = .false.
1230 return
1231 else
1232 ! Ignore the production of this species, but keep the reaction
1233 cycle
1234 end if
1235 end if
1236
1237 ix = species_index(component)
1238 if (ix == -1) then
1239 if (n_species >= max_num_species) &
1240 error stop "Too many species, increase max_num_species"
1241 n_species = n_species + 1
1242 ix = n_species
1243 species_list(ix) = trim(component)
1244 end if
1245
1246 if (left_side) then
1247 do i = 1, multiplicity
1248 n_in = n_in + 1
1249 ix_in(n_in) = ix
1250 end do
1251 else
1252 ! Check if species is already present in right-hand side
1253 do i = 1, n_out
1254 if (ix == ix_out(i)) exit
1255 end do
1256
1257 if (i <= n_out) then
1258 multiplicity_out(i) = multiplicity_out(i) + multiplicity
1259 else
1260 ! If not already present, add the species
1261 n_out = n_out + 1
1262 ix_out(n_out) = ix
1263 multiplicity_out(n_out) = multiplicity
1264 end if
1265 end if
1266 end do
1267
1268 reaction%ix_in = ix_in(1:n_in)
1269 reaction%ix_out = ix_out(1:n_out)
1270 reaction%multiplicity_out = multiplicity_out(1:n_out)
1271 reaction%rate_factor = rfactor
1272
1273 if (n_in == 0) then
1274 print *, "Error in the following reaction:"
1275 print *, trim(reaction_text)
1276 error stop "No input species"
1277 end if
1278 end subroutine parse_reaction
1279
1280 !> Find index of a species, return -1 if not found
1281 elemental integer function species_index(name)
1282 character(len=*), intent(in) :: name
1283 do species_index = 1, n_species
1284 if (species_list(species_index) == name) exit
1285 end do
1287 end function species_index
1288
1289 !> Routine to find the indices of entries in a string
1290 subroutine get_fields_string(line, delims, n_max, n_found, ixs_start, ixs_end)
1291 !> The line from which we want to read
1292 character(len=*), intent(in) :: line
1293 !> A string with delimiters. For example delims = " ,'"""//char(9)
1294 character(len=*), intent(in) :: delims
1295 !> Maximum number of entries to read in
1296 integer, intent(in) :: n_max
1297 !> Number of entries found
1298 integer, intent(inout) :: n_found
1299 !> On return, ix_start(i) holds the starting point of entry i
1300 integer, intent(inout) :: ixs_start(n_max)
1301 !> On return, ix_end(i) holds the end point of entry i
1302 integer, intent(inout) :: ixs_end(n_max)
1303
1304 integer :: ix, ix_prev
1305
1306 ix_prev = 0
1307 n_found = 0
1308
1309 do while (n_found < n_max)
1310
1311 ! Find the starting point of the next entry (a non-delimiter value)
1312 ix = verify(line(ix_prev+1:), delims)
1313 if (ix == 0) exit
1314
1315 n_found = n_found + 1
1316 ixs_start(n_found) = ix_prev + ix ! This is the absolute position in 'line'
1317
1318 ! Get the end point of the current entry (next delimiter index minus one)
1319 ix = scan(line(ixs_start(n_found)+1:), delims) - 1
1320
1321 if (ix == -1) then ! If there is no last delimiter,
1322 ixs_end(n_found) = len(line) ! the end of the line is the endpoint
1323 else
1324 ixs_end(n_found) = ixs_start(n_found) + ix
1325 end if
1326
1327 ix_prev = ixs_end(n_found) ! We continue to search from here
1328 end do
1329
1330 end subroutine get_fields_string
1331
1332 !> Replace text in string, inspired by
1333 !> http://fortranwiki.org/fortran/show/String_Functions
1334 pure function string_replace(string, text, replacement) result(outs)
1335 character(len=*), intent(in) :: string
1336 character(len=*), intent(in) :: text
1337 character(len=*), intent(in) :: replacement
1338 character(len=:), allocatable :: outs
1339 integer, parameter :: buffer_space = 256
1340 character(len=(len(string)+buffer_space)) :: buffer
1341 integer :: i, nt, nr, len_outs
1342
1343 buffer = string
1344 nt = len_trim(text)
1345 nr = len_trim(replacement)
1346
1347 do
1348 i = index(buffer, text(:nt))
1349 if (i == 0) exit
1350 buffer = buffer(:i-1) // replacement(:nr) // trim(buffer(i+nt:))
1351 end do
1352
1353 len_outs = len_trim(buffer)
1354 allocate(character(len=len_outs) :: outs)
1355 outs = buffer(1:len_outs)
1356 end function string_replace
1357
1358 !> An inefficient routine to replace *^+- characters in a string
1359 subroutine to_simple_ascii(text, simple, charge)
1360 character(len=*), intent(in) :: text
1361 character(len=*), intent(inout) :: simple
1362 integer, intent(out) :: charge
1363 integer :: n
1364 logical :: in_brackets = .false.
1365
1366 charge = 0
1367 simple = ""
1368
1369 do n = 1, len_trim(text)
1370 select case (text(n:n))
1371 case ('(')
1372 in_brackets = .true.
1373 simple = trim(simple) // "_"
1374 case (')')
1375 in_brackets = .false.
1376 case ('*')
1377 simple = trim(simple) // "_star"
1378 case ('+')
1379 if (.not. in_brackets) then
1380 charge = charge + 1
1381 end if
1382 simple = trim(simple) // "_plus"
1383 case ('-')
1384 if (.not. in_brackets) then
1385 charge = charge - 1
1386 end if
1387 simple = trim(simple) // "_min"
1388 case ('^')
1389 simple = trim(simple) // "_hat"
1390 case ("'")
1391 simple = trim(simple) // "p"
1392 case default
1393 simple = trim(simple) // text(n:n)
1394 end select
1395 end do
1396
1397 ! Handle some species separately
1398 if (simple == "e" .or. simple == "stoch_e") charge = -1
1399 end subroutine to_simple_ascii
1400
1401end module m_chemistry
Module for handling chemical reactions.
character(len=20), dimension(*), parameter, public reaction_names
integer, parameter rate_tabulated_energy
Reaction with a field-dependent reaction rate.
subroutine, public read_reactions(filename, read_success)
Read reactions from a file.
subroutine, public chemistry_write_summary(fname)
Write a summary of the reactions (TODO) and the ionization and attachment coefficients (if working at...
integer, dimension(max_num_species), public, protected species_charge
Charge of the species.
integer, parameter, public attachment_reaction
Identifier for attachment reactions.
integer, parameter, public ionization_reaction
Identifier for ionization reactions.
subroutine, public get_rates(fields, rates, n_cells, energy_ev)
Compute reaction rates.
integer, parameter, public detachment_reaction
Identifier for detachment reactions.
integer, parameter, public general_reaction
Identifier for general reactions (not of any particular type)
subroutine, public chemistry_initialize(tree, cfg)
Initialize module and load chemical reactions.
integer, dimension(:), allocatable, public, protected charged_species_itree
List with indices of charged species.
integer, parameter, public recombination_reaction
Identifier for recombination reactions.
integer, public, protected n_stochastic_species
Number of stochastic species.
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)
subroutine, public chemistry_convert_stochastic_species(tree, rng)
Convert stochastic species into regular species, using random numbers.
subroutine, public chemistry_get_breakdown_field(field_td, min_growth_rate)
Get the breakdown field in Townsend.
character(len=comp_len), dimension(max_num_species), public, protected species_list
List of the species.
integer, public, protected n_species
Number of species present.
integer, public, protected n_plasma_species
Number of plasma species present.
elemental integer function, public species_index(name)
Find index of a species, return -1 if not found.
integer, dimension(:), allocatable, public, protected charged_species_charge
List with charges of charged species.
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 to set the time step.
Definition m_dt.f90:2
integer, public, protected time_integrator
Which time integrator is used.
Definition m_dt.f90:49
Module that stores parameters related to the gas.
Definition m_gas.f90:2
character(len=comp_len), dimension(:), allocatable, public, protected gas_components
Definition m_gas.f90:30
elemental integer function, public gas_index(name)
Find index of a gas component, return -1 if not found.
Definition m_gas.f90:285
real(dp), public, protected gas_temperature
Definition m_gas.f90:21
real(dp), parameter, public townsend_to_si
Definition m_gas.f90:42
real(dp), dimension(:), allocatable, public, protected gas_densities
Definition m_gas.f90:27
logical, public, protected gas_constant_density
Whether the gas has a constant density.
Definition m_gas.f90:12
real(dp), public, protected gas_number_density
Definition m_gas.f90:33
Module to set the type of model.
Definition m_model.f90:2
logical, public, protected model_has_energy_equation
Whether the model has an energy equation.
Definition m_model.f90:20
Module with settings and routines for tabulated data.
subroutine, public table_set_column(tbl, i_col, x, y, max_err)
Interpolate data and store in lookup table.
integer, public, protected table_xspacing
X-spacing for lookup table.
integer, public, protected table_size
How large lookup tables should be.
Module that provides routines for reading in arbritrary transport data.
real(dp), public, protected td_max_ev
Maximal energy (eV) in input data (automatically updated)
type(lt_t), public, protected td_tbl
integer, parameter, public td_diffusion
Electron diffusion constant.
integer, parameter, public td_eta
Attachment coefficient.
integer, public, protected td_energy_ev
Electron energy in eV (used with chemistry)
integer, parameter, public td_alpha
Ionization coefficient.
integer, parameter, public td_mobility
Electron mobility.
Module with basic types.
Definition m_types.f90:2
Module that contains physical and numerical constants.
real(dp), parameter uc_boltzmann_const
real(dp), parameter uc_elec_volt