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