afivo-streamer 1.1
1D/2D/3D streamer simulations with AMR
Loading...
Searching...
No Matches
m_output.f90
Go to the documentation of this file.
1!> Module with methods and parameters related to writing output
2module m_output
3#include "../afivo/src/cpp_macros.h"
4 use m_af_all
5 use m_types
6 use m_streamer
7 use m_gas
8 use m_lookup_table
10 use m_field
11
12 implicit none
13 private
14
15 ! Name for the output files
16 character(len=string_len), public, protected :: output_name = "output/my_sim"
17
18 ! If defined, only output these variables
19 character(len=af_nlen), allocatable :: output_only(:)
20
21 ! Time between writing output
22 real(dp), public, protected :: output_dt = 1.0e-10_dp
23
24 ! Write to a log file for regression testing
25 logical, protected :: output_regression_test = .false.
26
27 ! Write output along a line
28 logical, public, protected :: lineout_write = .false.
29
30 ! Write Silo output
31 logical, public, protected :: silo_write = .true.
32
33 ! Write Silo output every N outputs
34 integer, public, protected :: silo_per_outputs = 1
35
36 ! Write binary output files (to resume later)
37 logical, public, protected :: datfile_write = .false.
38
39 ! Write binary output files every N outputs
40 integer, public, protected :: datfile_per_outputs = 1
41
42 ! Use this many points for lineout data
43 integer, public, protected :: lineout_npoints = 500
44
45 ! Relative position of line minimum coordinate
46 real(dp), public, protected :: lineout_rmin(3) = 0.0_dp
47
48 ! Relative position of line maximum coordinate
49 real(dp), public, protected :: lineout_rmax(3) = 1.0_dp
50
51 ! Which variable to include in lineout
52 integer, allocatable, public, protected :: lineout_ivar(:)
53
54 ! Write integral over cross-section data output
55 logical, public, protected :: cross_write = .false.
56
57 ! Integrate up to this r value
58 real(dp), public, protected :: cross_rmax = 2.0e-3_dp
59
60 ! Use this many points for cross-section data
61 integer, public, protected :: cross_npoints = 500
62
63 ! Write uniform output in a plane
64 logical, public, protected :: plane_write = .false.
65
66 ! Which variable to include in plane
67 integer, allocatable, public, protected :: plane_ivar(:)
68
69 ! Use this many points for plane data
70 integer, public, protected :: plane_npixels(2) = [64, 64]
71
72 ! Relative position of plane minimum coordinate
73 real(dp), public, protected :: plane_rmin(ndim) = 0.0_dp
74
75 ! Relative position of plane maximum coordinate
76 real(dp), public, protected :: plane_rmax(ndim) = 1.0_dp
77
78 ! Output electric field maxima and their locations
79 logical, public, protected :: field_maxima_write = .false.
80
81 ! Threshold value (V/m) for electric field maxima
82 real(dp), public, protected :: field_maxima_threshold = 0.0_dp
83
84 ! Minimal distance (m) between electric field maxima
85 real(dp), public, protected :: field_maxima_distance = 0.0_dp
86
87 ! Print status every this many seconds
88 real(dp), public, protected :: output_status_delay = 60.0_dp
89
90 ! To reduce output when the voltage is off
91 integer, public, protected :: output_dt_factor_pulse_off = 1
92
93 ! Density threshold for detecting plasma regions
94 real(dp) :: density_threshold = 1e18_dp
95
96 ! Number of extra variables to add to output
97 integer :: n_extra_vars = 0
98
99 ! Maximum refinement level for output
100 integer :: output_max_lvl = 100
101
102 ! Names of extra variables to add to output
103 character(len=af_nlen) :: output_extra_vars(100)
104
105 ! Output the electron energy in eV from the local field approximation
106 logical :: output_electron_energy = .false.
107
108 ! Output the conductivity of the plasma
109 logical :: output_conductivity = .false.
110
111 ! Output the electron conduction current
112 logical :: output_electron_current = .false.
113
114 ! Table with energy in eV vs electric field
115 type(lt_t) :: ev_vs_fld
116
117 ! Public methods
118 public :: output_initialize
119 public :: output_initial_summary
120 public :: output_write
121 public :: output_log
122 public :: output_status
123
124contains
125
126 subroutine output_initialize(tree, cfg)
127 use m_config
128 use m_table_data
129 type(af_t), intent(inout) :: tree
130 type(cfg_t), intent(inout) :: cfg
131 character(len=name_len), allocatable :: varname(:)
132 character(len=af_nlen) :: empty_names(0)
133 integer :: n, i
134 character(len=string_len) :: td_file
135 real(dp), allocatable :: x_data(:), y_data(:)
136
137 !< [main_output_settings]
138 call cfg_add_get(cfg, "output%name", output_name, &
139 "Name for the output files (e.g. output/my_sim)")
140 call cfg_add_get(cfg, "output%dt", output_dt, &
141 "The timestep for writing output (s)")
142 call cfg_add_get(cfg, "silo_write", silo_write, &
143 "Write silo output")
144 call cfg_add_get(cfg, "silo%per_outputs", silo_per_outputs, &
145 "Write silo output files every N outputs")
146 call cfg_add_get(cfg, "datfile%write", datfile_write, &
147 "Write binary output files (to resume later)")
148 call cfg_add_get(cfg, "datfile%per_outputs", datfile_per_outputs, &
149 "Write binary output files every N outputs")
150 call cfg_add_get(cfg, "output%electron_energy", output_electron_energy, &
151 "Show the electron energy in eV from the local field approximation")
152 call cfg_add_get(cfg, "output%conductivity", output_conductivity, &
153 "Output the conductivity of the plasma")
154 !< [main_output_settings]
155
156 call check_path_writable(output_name)
157
158 call cfg_add_get(cfg, "output%electron_current", output_electron_current, &
159 "Output the electron conduction current (can also be computed in &
160 &Visit as -gradient(phi) * sigma)")
161 call cfg_add_get(cfg, "lineout%write", lineout_write, &
162 "Write output along a line")
163
164 if (lineout_write) then
165 call cfg_add(cfg, "lineout%varname", ["e"], &
166 "Names of variable to write in lineout", dynamic_size=.true.)
167 call cfg_get_size(cfg, "lineout%varname", n)
168 allocate(varname(n))
169 call cfg_get(cfg, "lineout%varname", varname)
170
171 allocate(lineout_ivar(n))
172 do i = 1, n
173 lineout_ivar(i) = af_find_cc_variable(tree, trim(varname(i)))
174 end do
175 deallocate(varname)
176
177 call cfg_add_get(cfg, "lineout%npoints", lineout_npoints, &
178 "Use this many points for lineout data")
179 call cfg_add_get(cfg, "lineout%rmin", lineout_rmin(1:ndim), &
180 "Relative position of line minimum coordinate")
181 call cfg_add_get(cfg, "lineout%rmax", lineout_rmax(1:ndim), &
182 "Relative position of line maximum coordinate")
183 end if
184
185 call cfg_add_get(cfg, "output%status_delay", output_status_delay, &
186 "Print status every this many seconds")
187 call cfg_add_get(cfg, "output%max_lvl", output_max_lvl, &
188 "Maximum refinement level for output, to reduce file size")
189 call cfg_add_get(cfg, "output%dt_factor_pulse_off", &
191 "Reduce output frequency with this factor when the voltage is off")
192 call cfg_add_get(cfg, "output%regression_test", output_regression_test, &
193 "Write to a log file for regression testing")
194 call cfg_add_get(cfg, "output%density_threshold", density_threshold, &
195 "Electron density threshold for detecting plasma regions &
196 &(1/m3, will be scaled by gas density)")
197 call cfg_add(cfg, "output%only", empty_names, &
198 "If defined, only output these variables", .true.)
199
200 call cfg_get_size(cfg, "output%only", n)
201 allocate(output_only(n))
202 call cfg_get(cfg, "output%only", output_only)
203
204 if (size(output_only) > 0) then
205 tree%cc_write_output(:) = .false.
206 do n = 1, size(output_only)
207 i = af_find_cc_variable(tree, trim(output_only(n)))
208 tree%cc_write_output(i) = .true.
209 end do
210 end if
211
212 call cfg_add_get(cfg, "cross%write", cross_write, &
213 "Write integral over cross-section data output")
214 call cfg_add_get(cfg, "cross%rmax", cross_rmax, &
215 "Integrate up to this r value")
216 call cfg_add_get(cfg, "cross%npoints", cross_npoints, &
217 "Use this many points for cross-section data")
218
219 call cfg_add_get(cfg, "plane%write", plane_write, &
220 "Write uniform output in a plane")
221
222 if (plane_write) then
223 call cfg_add(cfg, "plane%varname", ["e"], &
224 "Names of variable to write in a plane", dynamic_size=.true.)
225
226 call cfg_get_size(cfg, "plane%varname", n)
227 allocate(varname(n))
228 call cfg_get(cfg, "plane%varname", varname)
229
230 allocate(plane_ivar(n))
231 do i = 1, n
232 plane_ivar(i) = af_find_cc_variable(tree, trim(varname(i)))
233 end do
234 deallocate(varname)
235
236 call cfg_add_get(cfg, "plane%npixels", plane_npixels, &
237 "Use this many pixels for plane data")
238 call cfg_add_get(cfg, "plane%rmin", plane_rmin(1:ndim), &
239 "Relative position of plane minimum coordinate")
240 call cfg_add_get(cfg, "plane%rmax", plane_rmax(1:ndim), &
241 "Relative position of plane maximum coordinate")
242 end if
243
244 call cfg_add_get(cfg, "field_maxima%write", field_maxima_write, &
245 "Output electric field maxima and their locations")
246 call cfg_add_get(cfg, "field_maxima%threshold", field_maxima_threshold, &
247 "Threshold value (V/m) for electric field maxima")
248 call cfg_add_get(cfg, "field_maxima%distance", field_maxima_distance, &
249 "Minimal distance (m) between electric field maxima")
250
251 if (output_electron_energy) then
252 n_extra_vars = n_extra_vars + 1
253 output_extra_vars(n_extra_vars) = "eV"
254
255 ! Create a lookup table for the model coefficients
256 ev_vs_fld = lt_create(table_min_townsend, table_max_townsend, &
258 call cfg_get(cfg, "input_data%file", td_file)
259
260 ! Read table with E/N vs electron energy (eV)
261 call table_from_file(td_file, "Mean energy (eV)", x_data, y_data)
262 call table_set_column(ev_vs_fld, 1, x_data, y_data)
263 end if
264
265 if (output_conductivity) then
266 n_extra_vars = n_extra_vars + 1
267 output_extra_vars(n_extra_vars) = "sigma"
268 end if
269
270 if (output_electron_current) then
271 do i = 1, ndim
272 n_extra_vars = n_extra_vars + 1
273 write(output_extra_vars(n_extra_vars), "(A,I0)") "Je_", i
274 end do
275 end if
276
277 call cfg_add(cfg, "output%write_source", empty_names, &
278 "Write chemistry source terms of these species to output", &
279 dynamic_size=.true.)
280 call cfg_get_size(cfg, "output%write_source", n)
281 allocate(varname(n))
282 call cfg_get(cfg, "output%write_source", varname)
283
284 do i = 1, n
285 n_extra_vars = n_extra_vars + 1
286 output_extra_vars(n_extra_vars) = "src_" // trim(varname(i))
287 end do
288 deallocate(varname)
289
290 end subroutine output_initialize
291
292 !> Write a summary of the model and parameters used
293 subroutine output_initial_summary(tree)
294 use m_chemistry
295 type(af_t), intent(in) :: tree
296 character(len=string_len) :: fname
297
298 fname = trim(output_name) // "_summary.txt"
299 call chemistry_write_summary(trim(fname))
300 call output_stoichiometric_matrix(trim(output_name)//"_stoich_matrix.txt")
301 call output_chemical_species(trim(output_name)//"_species.txt")
302 call output_chemical_reactions(trim(output_name)//"_reactions.txt")
303 call output_chemical_rates(trim(output_name)//"_rates.txt", .true.)
304 call output_chemical_amounts(tree, trim(output_name)//"_amounts.txt", .true.)
305 end subroutine output_initial_summary
306
307 subroutine output_write(tree, output_cnt, wc_time, write_sim_data)
308 use m_photoi
309 use m_field
311 use m_analysis
312 use m_chemistry
313 type(af_t), intent(inout) :: tree
314 integer, intent(in) :: output_cnt
315 real(dp), intent(in) :: wc_time
316 interface
317 subroutine write_sim_data(my_unit)
318 integer, intent(in) :: my_unit
319 end subroutine write_sim_data
320 end interface
321 character(len=string_len) :: fname
322
323 if (compute_power_density) then
324 call af_loop_box(tree, set_power_density_box)
325 end if
326
327 if (gas_dynamics) then
328 call af_loop_box(tree, set_gas_primitives_box)
329 end if
330
331 if (silo_write .and. &
332 modulo(output_cnt, silo_per_outputs) == 0) then
333 call field_set_rhs(tree, 0)
334
335 ! Ensure valid ghost cells for density-based variables
336 call af_restrict_tree(tree, all_densities)
337 call af_gc_tree(tree, all_densities)
338
339 call af_restrict_tree(tree, [i_rhs])
340 call af_gc_tree(tree, [i_rhs])
341
342 write(fname, "(A,I6.6)") trim(output_name) // "_", output_cnt
343 if (n_extra_vars > 0) then
344 call af_write_silo(tree, fname, output_cnt, global_time, &
345 add_vars=add_variables, &
346 add_names=output_extra_vars(1:n_extra_vars), &
347 max_lvl=output_max_lvl)
348 else
349 call af_write_silo(tree, fname, output_cnt, global_time, &
350 max_lvl=output_max_lvl)
351 end if
352 end if
353
354 if (datfile_write .and. &
355 modulo(output_cnt, datfile_per_outputs) == 0) then
356 call af_write_tree(tree, fname, write_sim_data)
357 end if
358
359 write(fname, "(A,I6.6)") trim(output_name) // "_log.txt"
360 if (associated(user_write_log)) then
361 call user_write_log(tree, fname, output_cnt)
362 else
363 call output_log(tree, fname, output_cnt, wc_time)
364 end if
365
366 call output_chemical_rates(trim(output_name)//"_rates.txt", .false.)
367 call output_chemical_amounts(tree, trim(output_name)//"_amounts.txt", .false.)
368
369 if (output_regression_test) then
370 write(fname, "(A,I6.6)") trim(output_name) // "_rtest.log"
371 call output_regression_log(tree, fname, output_cnt, wc_time)
372 end if
373
374 if (field_maxima_write) then
375 write(fname, "(A,I6.6,A)") trim(output_name) // &
376 "_Emax_", output_cnt, ".txt"
377 call output_fld_maxima(tree, fname)
378 end if
379
380#if NDIM > 1
381 if (plane_write) then
382 write(fname, "(A,I6.6)") trim(output_name) // &
383 "_plane_", output_cnt
384 call af_write_plane(tree, fname, plane_ivar, &
388 end if
389#endif
390
391 if (lineout_write) then
392 write(fname, "(A,I6.6)") trim(output_name) // &
393 "_line_", output_cnt
394 call af_write_line(tree, trim(fname), lineout_ivar, &
395 r_min = lineout_rmin(1:ndim) * st_domain_len + st_domain_origin, &
396 r_max = lineout_rmax(1:ndim) * st_domain_len + st_domain_origin, &
397 n_points=lineout_npoints)
398 end if
399
400#if NDIM == 2
401 if (st_cylindrical) then
402 if(cross_write) then
403 write(fname, "(A,I6.6)") trim(output_name) // &
404 "_cross_", output_cnt
405 call output_cross(tree, fname)
406 end if
407 end if
408#endif
409
410 end subroutine output_write
411
412 subroutine add_variables(box, new_vars, n_var)
413 use m_gas
415 use m_chemistry
416 type(box_t), intent(in) :: box
417 integer, intent(in) :: n_var
418 real(dp) :: new_vars(dtimes(0:box%n_cell+1), n_var)
419 integer :: n, nc, n_cells, i_species, idim
420 character(len=name_len) :: species_name
421 real(dp) :: n_inv(dtimes(0:box%n_cell+1))
422 real(dp) :: td(dtimes(0:box%n_cell+1))
423 real(dp) :: sigma(dtimes(1:box%n_cell))
424 real(dp) :: e_vector(dtimes(1:box%n_cell), ndim)
425 real(dp) :: dens((box%n_cell+2)**ndim, n_species)
426 real(dp) :: rates((box%n_cell+2)**ndim, n_reactions)
427 real(dp) :: derivs((box%n_cell+2)**ndim, n_species)
428 logical :: have_derivs
429
430 if (.not. gas_constant_density) then
431 n_inv = 1/box%cc(dtimes(:), i_gas_dens)
432 else
433 n_inv = 1/gas_number_density
434 end if
435
436 td = si_to_townsend * box%cc(dtimes(:), i_electric_fld) * n_inv
437
438 have_derivs = .false.
439 nc = box%n_cell
440 n_cells = (nc+2)**ndim
441
442 do n = 1, n_var
443 select case (output_extra_vars(n))
444 case ("eV")
445 ! Add electron energy in eV
446 new_vars(dtimes(:), n) = lt_get_col(ev_vs_fld, 1, td)
447 case ("sigma")
448 ! Add plasma conductivity (e mu n_e)
449 new_vars(dtimes(:), n) = lt_get_col(td_tbl, td_mobility, td) * &
450 n_inv * box%cc(dtimes(:), i_electron) * uc_elem_charge
451 case ("Je_1")
452 ! Add electron current density (e mu n_e E_vector)
453 !
454 ! TODO: we could also add diffusive fluxes (or re-use the electron
455 ! flux computation)
456 e_vector = field_get_e_vector(box)
457 sigma = lt_get_col(td_tbl, td_mobility, &
458 td(dtimes(1:nc))) * n_inv * box%cc(dtimes(1:nc), i_electron) * &
460 do idim = 1, ndim
461 new_vars(dtimes(1:nc), n+idim-1) = sigma * e_vector(dtimes(:), idim)
462 end do
463 case ("Je_2")
464 continue ! already set above
465 case ("Je_3")
466 continue ! already set above
467 case default
468 ! Assume temporal production of some species is added, prefixed by "src_"
469
470 if (.not. have_derivs) then
471 call get_rates(pack(td, .true.), rates, n_cells)
472
473 dens(:, n_gas_species+1:n_species) = reshape(&
474 box%cc(dtimes(:), species_itree(n_gas_species+1:n_species)), &
475 [n_cells, n_plasma_species])
476
477 call get_derivatives(dens, rates, derivs, n_cells)
478 have_derivs = .true.
479 end if
480
481 ! Trim "src_" from the name
482 species_name = trim(output_extra_vars(n)(5:))
483 i_species = species_index(species_name)
484
485 if (i_species == -1) then
486 print *, "No species corresponding to ", species_name
487 error stop "Error adding time derivative to output"
488 else
489 new_vars(dtimes(:), n) = reshape(derivs(:, i_species), &
490 [dtimes(nc+2)])
491 end if
492 end select
493 end do
494 end subroutine add_variables
495
496 subroutine output_log(tree, filename, out_cnt, wc_time)
497 use m_field, only: current_voltage
499 use m_chemistry
500 use m_analysis
501 use m_dielectric
502 use m_dt
503 type(af_t), intent(in) :: tree
504 character(len=*), intent(in) :: filename
505 integer, intent(in) :: out_cnt !< Output number
506 real(dp), intent(in) :: wc_time !< Wallclock time
507 character(len=50), save :: fmt
508 integer :: my_unit, n
509 real(dp) :: velocity, dt
510 real(dp), save :: prev_pos(ndim) = 0
511 real(dp) :: sum_elec, sum_pos_ion
512 real(dp) :: max_elec, max_field, max_er, min_er
513 real(dp) :: sum_elem_charge, tmp, ne_zminmax(2)
514 real(dp) :: elecdens_threshold, max_field_tip
515 real(dp) :: r0(ndim), r1(ndim), r_tip(ndim)
516 type(af_loc_t) :: loc_elec, loc_field, loc_er, loc_tip
517 integer :: i, n_reals, n_user_vars
518 character(len=name_len) :: var_names(user_max_log_vars)
519 real(dp) :: var_values(user_max_log_vars)
520 logical, save :: first_time = .true.
521
522 if (associated(user_log_variables)) then
523 ! Set default names for the user variables
524 do i = 1, user_max_log_vars
525 write(var_names, "(A,I0)") "uservar_", i
526 end do
527 call user_log_variables(tree, n_user_vars, var_names, var_values)
528 else
529 n_user_vars = 0
530 end if
531
532 call af_tree_sum_cc(tree, i_electron, sum_elec)
533 call af_tree_sum_cc(tree, i_1pos_ion, sum_pos_ion)
534 call af_tree_max_cc(tree, i_electron, max_elec, loc_elec)
535 call af_tree_max_cc(tree, i_electric_fld, max_field, loc_field)
536 call af_tree_max_fc(tree, 1, electric_fld, max_er, loc_er)
537 call af_tree_min_fc(tree, 1, electric_fld, min_er)
538
539 ! Scale threshold with gas number density
540 elecdens_threshold = density_threshold * &
541 (gas_number_density/2.414e25_dp)**2
542 call analysis_zmin_zmax_threshold(tree, i_electron, elecdens_threshold, &
543 [st_domain_len(ndim), 0.0_dp], ne_zminmax)
544
545 ! Assume streamer tip is located at the farthest plasma density (above a
546 ! threshold) away from the electrode boundaries
549
550 if (ne_zminmax(1) - st_domain_origin(ndim) < &
551 st_domain_origin(ndim) + st_domain_len(ndim) - ne_zminmax(2)) then
552 r0(ndim) = ne_zminmax(2) - 0.02_dp * st_domain_len(ndim)
553 r1(ndim) = ne_zminmax(2) + 0.02_dp * st_domain_len(ndim)
554 else
555 r0(ndim) = ne_zminmax(1) - 0.02_dp * st_domain_len(ndim)
556 r1(ndim) = ne_zminmax(1) + 0.02_dp * st_domain_len(ndim)
557 end if
558
559 call analysis_max_var_region(tree, i_electric_fld, r0, r1, &
560 max_field_tip, loc_tip)
561
562 if (loc_tip%id > 0) then
563 r_tip = af_r_loc(tree, loc_tip)
564 else
565 r_tip = 0.0_dp
566 end if
567
568 sum_elem_charge = 0
569 do n = n_gas_species+1, n_species
570 if (species_charge(n) /= 0) then
571 call af_tree_sum_cc(tree, species_itree(n), tmp)
572 sum_elem_charge = sum_elem_charge + tmp * species_charge(n)
573 end if
574 end do
575
576 if (st_use_dielectric) then
577 call surface_get_integral(tree, diel, i_surf_dens, tmp)
578 sum_elem_charge = sum_elem_charge + tmp
579 end if
580
581 dt = global_dt
582
583 if (first_time) then
584 first_time = .false.
585 open(newunit=my_unit, file=trim(filename), action="write")
586#if NDIM == 1
587 write(my_unit, "(A)", advance="no") "it time dt v sum(n_e) sum(n_i) &
588 &sum(charge) sum(J.E) max(E) x max(n_e) x voltage current_J.E &
589 &current_displ ne_zmin ne_zmax &
590 &max(Etip) x wc_time n_cells min(dx) dt_cfl dt_diff dt_drt dt_chem &
591 &highest(lvl)"
592#elif NDIM == 2
593 write(my_unit, "(A)", advance="no") "it time dt v sum(n_e) sum(n_i) &
594 &sum(charge) sum(J.E) max(E) x y max(n_e) x y max(E_r) x y min(E_r) &
595 &voltage current_J.E current_displ &
596 &ne_zmin ne_zmax max(Etip) x y wc_time n_cells min(dx) &
597 &dt_cfl dt_diff dt_drt dt_chem highest(lvl)"
598#elif NDIM == 3
599 write(my_unit, "(A)", advance="no") "it time dt v sum(n_e) sum(n_i) &
600 &sum(charge) sum(J.E) max(E) x y z max(n_e) x y z voltage &
601 &current_J.E current_displ &
602 &ne_zmin ne_zmax max(Etip) x y z wc_time n_cells min(dx) &
603 &dt_cfl dt_diff dt_drt dt_chem highest(lvl)"
604#endif
605 if (associated(user_log_variables)) then
606 do i = 1, n_user_vars
607 write(my_unit, "(A)", advance="no") " "//trim(var_names(i))
608 end do
609 end if
610 write(my_unit, *) ""
611 close(my_unit)
612
613 ! Start with velocity zero
614 prev_pos = af_r_loc(tree, loc_field)
615 end if
616
617#if NDIM == 1
618 n_reals = 19
619#elif NDIM == 2
620 n_reals = 26
621#elif NDIM == 3
622 n_reals = 25
623#endif
624
625 if (associated(user_log_variables)) then
626 write(fmt, "(A,I0,A,I0,A)") "(I6,", n_reals, "E20.8e3,I12,5E20.8e3,I3,", &
627 n_user_vars, "E20.8e3)"
628 else
629 write(fmt, "(A,I0,A)") "(I6,", n_reals, "E20.8e3,I12,5E20.8e3,I3)"
630 end if
631
632 velocity = norm2(af_r_loc(tree, loc_field) - prev_pos) / output_dt
633 prev_pos = af_r_loc(tree, loc_field)
634
635 open(newunit=my_unit, file=trim(filename), action="write", &
636 position="append")
637#if NDIM == 1
638 write(my_unit, fmt) out_cnt, global_time, dt, velocity, sum_elec, &
639 sum_pos_ion, sum_elem_charge, st_global_jdote, &
640 max_field, af_r_loc(tree, loc_field), max_elec, &
641 af_r_loc(tree, loc_elec), current_voltage, st_global_jdote_current, &
642 st_global_displ_current, ne_zminmax, &
643 max_field_tip, r_tip, &
644 wc_time, af_num_cells_used(tree), &
645 af_min_dr(tree), dt_limits, &
646 tree%highest_lvl, var_values(1:n_user_vars)
647#elif NDIM == 2
648 write(my_unit, fmt) out_cnt, global_time, dt, velocity, sum_elec, &
649 sum_pos_ion, sum_elem_charge, st_global_jdote, &
650 max_field, af_r_loc(tree, loc_field), max_elec, &
651 af_r_loc(tree, loc_elec), max_er, af_r_loc(tree, loc_er), min_er, &
653 st_global_displ_current, ne_zminmax, max_field_tip, r_tip, &
654 wc_time, af_num_cells_used(tree), af_min_dr(tree), &
655 dt_limits, tree%highest_lvl, &
656 var_values(1:n_user_vars)
657#elif NDIM == 3
658 write(my_unit, fmt) out_cnt, global_time, dt, velocity, sum_elec, &
659 sum_pos_ion, sum_elem_charge, st_global_jdote, &
660 max_field, af_r_loc(tree, loc_field), max_elec, &
661 af_r_loc(tree, loc_elec), current_voltage, st_global_jdote_current, &
662 st_global_displ_current, ne_zminmax, &
663 max_field_tip, r_tip, &
664 wc_time, af_num_cells_used(tree), &
665 af_min_dr(tree), dt_limits, &
666 tree%highest_lvl, var_values(1:n_user_vars)
667#endif
668 close(my_unit)
669
670 end subroutine output_log
671
672 !> Write a net stoichiometric matrix to a file
673 subroutine output_stoichiometric_matrix(filename)
674 use m_chemistry
675 character(len=*), intent(in) :: filename
676 integer :: my_unit, m, i, ix
677 integer, allocatable :: stoich(:,:)
678
679 allocate(stoich(n_reactions, n_species))
680 stoich(:, :) = 0
681
682 do m = 1, n_reactions
683 ! Subtract consumed species
684 do i = 1, size(reactions(m)%ix_in)
685 ix = reactions(m)%ix_in(i)
686 stoich(m, ix) = stoich(m, ix) - 1
687 end do
688
689 ! Add produced species
690 do i = 1, size(reactions(m)%ix_out)
691 ix = reactions(m)%ix_out(i)
692 stoich(m, ix) = stoich(m, ix) + &
693 reactions(m)%multiplicity_out(i)
694 end do
695 end do
696
697 open(newunit=my_unit, file=trim(filename), action="write")
698 do i = 1, n_species
699 write(my_unit, *) stoich(:, i)
700 end do
701 write(my_unit, *) ""
702 close(my_unit)
703 end subroutine output_stoichiometric_matrix
704
705 !> Write a list of chemical species
706 subroutine output_chemical_species(filename)
707 use m_chemistry
708 character(len=*), intent(in) :: filename
709 integer :: my_unit, i
710
711 open(newunit=my_unit, file=trim(filename), action="write")
712 do i = 1, n_species
713 write(my_unit, "(A)") species_list(i)
714 end do
715 write(my_unit, "(A)") ""
716 close(my_unit)
717 end subroutine output_chemical_species
718
719 !> Write a list of chemical reactions
720 subroutine output_chemical_reactions(filename)
721 use m_chemistry
722 character(len=*), intent(in) :: filename
723 integer :: my_unit, i
724
725 open(newunit=my_unit, file=trim(filename), action="write")
726 do i = 1, n_reactions
727 write(my_unit, "(A)") reactions(i)%description
728 end do
729 write(my_unit, "(A)") ""
730 close(my_unit)
731 end subroutine output_chemical_reactions
732
733 !> Write space-integrated and time-integrated reaction rates
734 subroutine output_chemical_rates(filename, first_time)
735 use m_chemistry
736 character(len=*), intent(in) :: filename
737 logical, intent(in) :: first_time
738 integer :: my_unit, iostate
739
740 if (first_time) then
741 ! Clear the file
742 open(newunit=my_unit, file=trim(filename), iostat=iostate)
743 if (iostate == 0) close(my_unit, status='delete')
744 else
745 open(newunit=my_unit, file=trim(filename), action="write", &
746 position="append")
747 write(my_unit, *) global_time, st_global_rates
748 close(my_unit)
749 end if
750 end subroutine output_chemical_rates
751
752 !> Write space-integrated species densities
753 subroutine output_chemical_amounts(tree, filename, first_time)
754 use m_chemistry
755 type(af_t), intent(in) :: tree
756 character(len=*), intent(in) :: filename
757 logical, intent(in) :: first_time
758 integer :: my_unit, n, iostate
759 real(dp) :: sum_dens(n_species)
760
761 if (first_time) then
762 ! Clear the file
763 open(newunit=my_unit, file=trim(filename), iostat=iostate)
764 if (iostate == 0) close(my_unit, status='delete')
765 else
766 do n = 1, n_species
767 if (species_itree(n) > 0) then
768 call af_tree_sum_cc(tree, species_itree(n), sum_dens(n))
769 else
770 sum_dens(n) = 0.0_dp ! A neutral gas specie
771 end if
772 end do
773
774 open(newunit=my_unit, file=trim(filename), action="write", &
775 position="append")
776 write(my_unit, *) global_time, sum_dens
777 close(my_unit)
778 end if
779
780 end subroutine output_chemical_amounts
781
782 !> Write statistics to a file that can be used for regression testing
783 subroutine output_regression_log(tree, filename, out_cnt, wc_time)
784 use m_chemistry
785 type(af_t), intent(in) :: tree
786 character(len=*), intent(in) :: filename
787 integer, intent(in) :: out_cnt !< Output number
788 real(dp), intent(in) :: wc_time !< Wallclock time
789 character(len=30) :: fmt
790 integer :: my_unit, n
791 real(dp) :: vol
792 real(dp) :: sum_dens(n_species)
793 real(dp) :: sum_dens_sq(n_species)
794 real(dp) :: max_dens(n_species)
795
796 vol = af_total_volume(tree)
797 do n = 1, n_species
798 if (species_itree(n) > 0) then
799 call af_tree_sum_cc(tree, species_itree(n), sum_dens(n))
800 call af_tree_sum_cc(tree, species_itree(n), sum_dens_sq(n), power=2)
801 call af_tree_max_cc(tree, species_itree(n), max_dens(n))
802 else
803 ! A neutral gas specie
804 sum_dens(n) = 0.0_dp
805 sum_dens_sq(n) = 0.0_dp
806 max_dens(n) = 0.0_dp
807 end if
808 end do
809
810 if (out_cnt == 0) then
811 open(newunit=my_unit, file=trim(filename), action="write")
812 write(my_unit, "(A)", advance="no") "it time dt"
813 do n = 1, n_species
814 write(my_unit, "(A)", advance="no") &
815 " sum(" // trim(species_list(n)) // ")"
816 end do
817 do n = 1, n_species
818 write(my_unit, "(A)", advance="no") &
819 " sum(" // trim(species_list(n)) // "^2)"
820 end do
821 do n = 1, n_species
822 write(my_unit, "(A)", advance="no") &
823 " max(" // trim(species_list(n)) // ")"
824 end do
825 write(my_unit, "(A)") ""
826 close(my_unit)
827 end if
828
829 write(fmt, "(A,I0,A)") "(I0,", 3+3*n_species, "E20.8e3)"
830
831 open(newunit=my_unit, file=trim(filename), action="write", &
832 position="append")
833 write(my_unit, fmt) out_cnt, global_time, global_dt, &
834 sum_dens/vol, sum_dens_sq/vol, max_dens
835 close(my_unit)
836
837 end subroutine output_regression_log
838
839 subroutine check_path_writable(pathname)
840 character(len=*), intent(in) :: pathname
841 integer :: my_unit, iostate
842 open(newunit=my_unit, file=trim(pathname)//"_DUMMY", iostat=iostate)
843 if (iostate /= 0) then
844 print *, "Output name: " // trim(pathname) // '_...'
845 error stop "Directory not writable (does it exist?)"
846 else
847 close(my_unit, status='delete')
848 end if
849 end subroutine check_path_writable
850
851 !> Print short status message
852 subroutine output_status(tree, time, wc_time, it, dt)
853 use m_dt
854 type(af_t), intent(in) :: tree
855 real(dp), intent(in) :: time
856 real(dp), intent(in) :: wc_time
857 integer, intent(in) :: it
858 real(dp), intent(in) :: dt
859 write(*, "(F7.2,A,I0,A,E10.3,A,E10.3,A,E10.3,A,E10.3)") &
860 100 * time / st_end_time, "% it: ", it, &
861 " t:", time, " dt:", dt, " wc:", wc_time, &
862 " ncell:", real(af_num_cells_used(tree), dp)
863
864 ! This line prints the different time step restrictions
865 write(*, "(A,4E10.3,A)") " dt: ", &
866 dt_limits, " (cfl drt chem other)"
867 end subroutine output_status
868
869 subroutine output_fld_maxima(tree, filename)
870 use m_analysis
871 type(af_t), intent(in) :: tree
872 character(len=*), intent(in) :: filename
873 integer :: my_unit, i, n, n_found
874 integer, parameter :: n_max = 1000
875 real(dp) :: coord_val(ndim+1, n_max), d
876
877 ! Get locations of the maxima
879 n_max, coord_val, n_found)
880
881 if (n_found > n_max) then
882 print *, "output_fld_maxima warning: n_found > n_max"
883 print *, "n_found = ", n_found, "n_max = ", n_max
884 n_found = n_max
885 end if
886
887 ! Merge maxima that are too close
888 do n = n_found, 1, -1
889 do i = 1, n-1
890 d = norm2(coord_val(1:ndim, i) - coord_val(1:ndim, n))
891 if (d < field_maxima_distance) then
892 ! Replace the smaller value by the one at the end of the list, and
893 ! shorten the list by one item
894 if (coord_val(ndim+1, i) < coord_val(ndim+1, n)) then
895 coord_val(:, i) = coord_val(:, n)
896 end if
897 coord_val(:, n) = coord_val(:, n_found)
898 n_found = n_found - 1
899 exit
900 end if
901 end do
902 end do
903
904 open(newunit=my_unit, file=trim(filename), action="write")
905 do n = 1, n_found
906 if (coord_val(ndim+1, n) > field_maxima_threshold) then
907 write(my_unit, *) coord_val(:, n)
908 end if
909 end do
910 close(my_unit)
911
912 end subroutine output_fld_maxima
913
914#if NDIM == 2
915 subroutine output_cross(tree, filename)
916 use m_af_all
917 use m_analysis
918 use m_streamer
919 type(af_t), intent(in) :: tree
920 character(len=*), intent(inout) :: filename
921
922 integer :: my_unit
923 integer :: i
924 real(dp) :: z, elec_dens, charge_dens, current_dens
925
926 open(newunit=my_unit, file=trim(filename)//".txt", action="write")
927 write(my_unit, '(A)') "z elec_dens charge_dens current_dens"
928
929 do i = 1, cross_npoints
930 z = i * st_domain_len(2) / (cross_npoints + 1)
931 call analysis_get_cross(tree, cross_rmax, z, elec_dens, &
932 charge_dens, current_dens)
933 write(my_unit, *) z, elec_dens, charge_dens, current_dens
934 end do
935
936 close(my_unit)
937 end subroutine output_cross
938#endif
939
940 subroutine set_power_density_box(box)
941 type(box_t), intent(inout) :: box
942 integer :: ijk, nc
943 real(dp) :: j_dot_e
944
945 nc = box%n_cell
946 do kji_do(1, nc)
947 ! Compute inner product flux * field over the cell faces
948 j_dot_e = 0.5_dp * sum(box%fc(ijk, :, flux_elec) * box%fc(ijk, :, electric_fld))
949#if NDIM == 1
950 j_dot_e = j_dot_e + 0.5_dp * (&
951 box%fc(i+1, 1, flux_elec) * box%fc(i+1, 1, electric_fld))
952#elif NDIM == 2
953 j_dot_e = j_dot_e + 0.5_dp * (&
954 box%fc(i+1, j, 1, flux_elec) * box%fc(i+1, j, 1, electric_fld) + &
955 box%fc(i, j+1, 2, flux_elec) * box%fc(i, j+1, 2, electric_fld))
956#elif NDIM == 3
957 j_dot_e = j_dot_e + 0.5_dp * (&
958 box%fc(i+1, j, k, 1, flux_elec) * box%fc(i+1, j, k, 1, electric_fld) + &
959 box%fc(i, j+1, k, 2, flux_elec) * box%fc(i, j+1, k, 2, electric_fld) + &
960 box%fc(i, j, k+1, 3, flux_elec) * box%fc(i, j, k+1, 3, electric_fld))
961#endif
962
963 box%cc(ijk, i_power_density) = j_dot_e * uc_elec_charge
964 end do; close_do
965 end subroutine set_power_density_box
966
967 subroutine set_gas_primitives_box(box)
968 type(box_t), intent(inout) :: box
969 integer :: ijk, nc, idim
970
971 nc = box%n_cell
972 do kji_do(1, nc)
973 do idim = 1, ndim
974 ! Compute velocity components
975 box%cc(ijk, gas_prim_vars(i_mom(idim))) = &
976 box%cc(ijk, gas_vars(i_mom(idim))) / box%cc(ijk, gas_vars(i_rho))
977 end do
978
979 ! Compute the pressure
980 box%cc(ijk, gas_prim_vars(i_e)) = (gas_euler_gamma-1.0_dp) * (box%cc(ijk,gas_vars( i_e)) - &
981 0.5_dp*box%cc(ijk,gas_vars( i_rho))* sum(box%cc(ijk, gas_vars(i_mom(:)))**2))
982 ! Compute the temperature (T = P/(n*kB), n=gas number density)
983 box%cc(ijk, gas_prim_vars(i_e+1)) = box%cc(ijk, gas_prim_vars(i_e))/ &
984 (box%cc(ijk, i_gas_dens)* uc_boltzmann_const)
985
986 end do; close_do
987 end subroutine set_gas_primitives_box
988
989end module m_output
Module with routines to help analyze simulations.
Definition m_analysis.f90:2
subroutine, public analysis_max_var_region(tree, iv, r0, r1, max_value, loc)
Find maximal value for boxes that are (at least partially) in the rectangle from r0 to r1.
subroutine, public analysis_get_cross(tree, rmax, z, elec_dens, charge_dens, current_dens)
Get integrated quantities of an axisymmetric streamer at a z-coordinate.
subroutine, public analysis_zmin_zmax_threshold(tree, iv, threshold, limits, z_minmax)
Find minimum and maximum z coordinate where a variable exceeds a threshold.
subroutine, public analysis_get_maxima(tree, iv, threshold, n_max, coord_val, n_found)
Find at most n_max maxima of a variable. Maxima are determined by looking at the direct neighbors.
Module for handling chemical reactions.
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.
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)
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.
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.
integer, parameter, public i_surf_dens
type(surfaces_t), public diel
To store dielectric surface.
Module to set the time step.
Definition m_dt.f90:2
real(dp), dimension(dt_num_cond), public dt_limits
Definition m_dt.f90:13
Module to compute electric fields.
Definition m_field.f90:2
subroutine, public field_set_rhs(tree, s_in)
Definition m_field.f90:364
real(dp), public, protected current_voltage
The current applied voltage.
Definition m_field.f90:90
Module that stores parameters related to the gas.
Definition m_gas.f90:2
logical, public, protected gas_dynamics
Whether the gas dynamics are simulated.
Definition m_gas.f90:15
integer, public, protected i_gas_dens
Definition m_gas.f90:45
real(dp), parameter, public si_to_townsend
Definition m_gas.f90:39
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 with methods and parameters related to writing output.
Definition m_output.f90:2
real(dp), public, protected field_maxima_distance
Definition m_output.f90:85
real(dp), dimension(3), public, protected lineout_rmin
Definition m_output.f90:46
integer, public, protected cross_npoints
Definition m_output.f90:61
integer, public, protected output_dt_factor_pulse_off
Definition m_output.f90:91
real(dp), public, protected cross_rmax
Definition m_output.f90:58
logical, public, protected plane_write
Definition m_output.f90:64
logical, public, protected datfile_write
Definition m_output.f90:37
subroutine, public output_status(tree, time, wc_time, it, dt)
Print short status message.
Definition m_output.f90:853
logical, public, protected field_maxima_write
Definition m_output.f90:79
real(dp), public, protected output_dt
Definition m_output.f90:22
integer, public, protected datfile_per_outputs
Definition m_output.f90:40
integer, public, protected silo_per_outputs
Definition m_output.f90:34
subroutine, public output_initial_summary(tree)
Write a summary of the model and parameters used.
Definition m_output.f90:294
character(len=string_len), public, protected output_name
Definition m_output.f90:16
real(dp), dimension(ndim), public, protected plane_rmax
Definition m_output.f90:76
integer, public, protected lineout_npoints
Definition m_output.f90:43
integer, dimension(:), allocatable, public, protected plane_ivar
Definition m_output.f90:67
real(dp), public, protected output_status_delay
Definition m_output.f90:88
logical, public, protected silo_write
Definition m_output.f90:31
logical, public, protected cross_write
Definition m_output.f90:55
subroutine, public output_log(tree, filename, out_cnt, wc_time)
Definition m_output.f90:497
logical, public, protected lineout_write
Definition m_output.f90:28
integer, dimension(:), allocatable, public, protected lineout_ivar
Definition m_output.f90:52
real(dp), dimension(3), public, protected lineout_rmax
Definition m_output.f90:49
integer, dimension(2), public, protected plane_npixels
Definition m_output.f90:70
real(dp), dimension(ndim), public, protected plane_rmin
Definition m_output.f90:73
subroutine, public output_initialize(tree, cfg)
Definition m_output.f90:127
subroutine, public output_write(tree, output_cnt, wc_time, write_sim_data)
Definition m_output.f90:308
real(dp), public, protected field_maxima_threshold
Definition m_output.f90:82
Top-module for photoionization, which can make use of different methods.
Definition m_photoi.f90:2
This module contains several pre-defined variables like:
Definition m_streamer.f90:6
logical, public, protected st_cylindrical
Whether cylindrical coordinates are used.
real(dp), dimension(:), allocatable, public st_global_rates
Global sum of reaction rates.
real(dp), public st_global_jdote_current
Electric current through electrodes due to J.E.
real(dp), public global_dt
Global time step.
real(dp), public global_time
Global time.
logical, public, protected st_use_dielectric
Whether a dielectric is used.
integer, public, protected i_electron
Index of electron density.
integer, public, protected electric_fld
Index of electric field vector.
integer, public, protected i_rhs
Index of source term Poisson.
integer, public, protected i_1pos_ion
Index of first positive ion species.
real(dp), public st_global_jdote
Global sum of J.E.
integer, public, protected i_electric_fld
Index of electric field norm.
real(dp), dimension(ndim), public, protected st_domain_len
Domain length per dimension.
real(dp), public, protected st_end_time
End time of the simulation.
real(dp), dimension(ndim), public, protected st_domain_origin
Origin of domain.
integer, dimension(:), allocatable, public, protected all_densities
Index of all densities that evolve in time.
logical, public, protected compute_power_density
Include deposited power density in output.
real(dp), public st_global_displ_current
Electric current through electrodes due to displacement current.
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.
real(dp), public, protected table_max_townsend
Maximum field (Td) for lookup tables.
integer, public, protected table_xspacing
X-spacing for lookup table.
integer, public, protected table_size
How large lookup tables should be.
subroutine, public table_from_file(file_name, data_name, x_data, y_data)
Routine to read in tabulated data from a file.
real(dp), public, protected table_min_townsend
Minimum field (Td) for lookup tables.
Module that provides routines for reading in arbritrary transport data.
type(lt_t), public, protected td_tbl
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_elem_charge
real(dp), parameter uc_elec_charge
This module contains all the methods that users can customize.
integer, parameter user_max_log_vars
procedure(log_subr), pointer user_write_log
To write a custom log file.
procedure(log_vars), pointer user_log_variables
To add entries to the log file.