afivo-streamer 1.1
1D/2D/3D streamer simulations with AMR
Loading...
Searching...
No Matches
streamer.f90
Go to the documentation of this file.
1!> Program to perform streamer simulations with AMR
2program streamer
3#include "../afivo/src/cpp_macros.h"
4 use iso_fortran_env, only: error_unit
5 use m_config
6 use m_af_all
7 use m_streamer
8 use m_field
10 use m_refine
11 use m_photoi
12 use m_chemistry
13 use m_gas
14 use m_coupling
15 use m_fluid
16 use m_dt
17 use m_types
19 use m_output
20 use m_dielectric
22 use m_model
23 use m_analysis
24 use omp_lib
25
26 implicit none
27
28 integer, parameter :: int8 = selected_int_kind(18)
29 integer, parameter :: max_attemps_per_time_step = 10
30 integer, parameter :: datfile_version = 30
31 integer(int8) :: t_start, t_current, count_rate
32 real(dp) :: wc_time = 0.0_dp, inv_count_rate
33 real(dp) :: time_last_print, time_last_output
34 integer :: it_last_output
35 integer :: i, it, n, coord_type, box_bytes
36 integer :: n_steps_rejected
37 integer, allocatable :: ref_links(:, :)
38 logical :: write_out
39 real(dp) :: time, dt, dt_lim, photoi_prev_time
40 real(dp) :: dt_gas_lim, dt_lim_step
41 real(dp) :: fraction_steps_rejected
42 real(dp) :: memory_limit_gb
43 type(af_t) :: tree_copy ! Used when reading a tree from a file
44 type(ref_info_t) :: ref_info ! Contains info about refinement changes
45 integer :: output_cnt = 0 ! Number of output files written
46 character(len=string_len) :: restart_from_file = undefined_str
47 real(dp) :: max_field
48 type(af_loc_t) :: loc_field, loc_field_t0
49 real(dp) :: pos_emax(ndim), pos_emax_t0(ndim)
50 real(dp) :: breakdown_field_td, current_output_dt
51 real(dp) :: time_until_next_pulse
52 real(dp) :: field_energy, field_energy_prev
53 real(dp) :: tmp, field_energy_prev_time
54 logical :: step_accepted, start_of_new_pulse
55
56 ! To keep track of the computational cost of different parts
57 real(dp) :: t1, t2, t3
58
59 !> The configuration for the simulation
60 type(cfg_t) :: cfg
61 !> This contains the full grid information
62 type(af_t) :: tree
63
64 call print_program_name()
65
66 ! Parse command line configuration files and options
67 call cfg_update_from_arguments(cfg)
68
69 call cfg_add_get(cfg, "restart_from_file", restart_from_file, &
70 "If set, restart simulation from a previous .dat file")
71
72 memory_limit_gb = 4.0_dp**(ndim-1) ! 1, 4, 16 GB for 1D, 2D, 3D
73 call cfg_add_get(cfg, "memory_limit_GB", memory_limit_gb, &
74 "Memory limit (GB)")
75
76 call initialize_modules(cfg, tree, mg, restart_from_file /= undefined_str)
77
78 call cfg_write(cfg, trim(output_name) // "_out.cfg", custom_first=.true.)
79
80 call chemistry_get_breakdown_field(breakdown_field_td, 1.0e3_dp)
81 write(*, '(A,E12.4)') " Estimated breakdown field (Td): ", breakdown_field_td
82
83 ! Specify default methods for all the variables
84 do i = 1, size(all_densities)
85 call af_set_cc_methods(tree, all_densities(i), &
86 bc_species, af_gc_interp_lim, st_prolongation_method)
87 end do
88
89 if (.not. gas_constant_density) then
90 if (gas_dynamics) then
91 ! Let the gas density evolve in time
92 call af_set_cc_methods(tree, i_gas_dens, &
93 af_bc_neumann_zero, af_gc_interp, st_prolongation_method)
94 else
95 ! The gas density is specified by a function
96 call af_set_cc_methods(tree, i_gas_dens, &
97 funcval=set_gas_density_from_user_function)
98 end if
99 end if
100
101 do i = 1, tree%n_var_cell
102 if (tree%cc_write_output(i) .and. .not. &
103 (tree%has_cc_method(i) .or. i == i_phi)) then
104 call af_set_cc_methods(tree, i, af_bc_neumann_zero, &
105 af_gc_interp, st_prolongation_method)
106 end if
107 end do
108
109 ! These values are overwritten when restarting
110 it = 0
111 time = 0.0_dp ! Simulation time (all times are in s)
112 global_time = time
113 photoi_prev_time = time ! Time of last photoionization computation
114 dt = global_dt
115 n_steps_rejected = 0
116 fraction_steps_rejected = 0.0_dp
117 pos_emax_t0 = 0.0_dp ! Initial streamer position
118
119 ! Initialize the tree (which contains all the mesh information)
120 if (restart_from_file /= undefined_str) then
121 tree_copy = tree ! Store the settings
122 call af_read_tree(tree, restart_from_file, read_sim_data)
123
124 ! Restore some of the settings
125 tree%cc_write_output = tree_copy%cc_write_output
126 tree%cc_write_binary = tree_copy%cc_write_binary
127 tree%fc_write_binary = tree_copy%fc_write_binary
128
129 box_bytes = af_box_bytes(tree%n_cell, tree%n_var_cell, tree%n_var_face)
130 tree%box_limit = nint(memory_limit_gb * 2.0_dp**30 / box_bytes)
131
132 if (tree%n_cell /= st_box_size) &
133 error stop "restart_from_file: incompatible box size"
134
135 if (tree%n_var_cell /= tree_copy%n_var_cell) then
136 write(error_unit, *) "n_var_cell here:", tree_copy%n_var_cell
137 write(error_unit, *) "n_var_cell file:", tree%n_var_cell
138 error stop "restart_from_file: incompatible variable list"
139 end if
140
141 if (st_use_dielectric) error stop "Restarting not support with dielectric"
142
143 ! @todo more consistency checks
144
145 ! This routine always needs to be called when using multigrid
146 call mg_init(tree, mg)
147 else
148 if (st_cylindrical) then
149 coord_type = af_cyl
150 else
151 coord_type = af_xyz
152 end if
153
154 call af_init(tree, st_box_size, st_domain_origin + st_domain_len, &
155 st_coarse_grid_size, periodic=st_periodic, coord=coord_type, &
156 r_min=st_domain_origin, mem_limit_gb=memory_limit_gb)
157
158 ! Set up the initial conditions
159 call set_initial_conditions(tree, mg)
160
161 ! Write initial output
162 output_cnt = 0 ! Number of output files written
163 call output_write(tree, output_cnt, 0.0_dp, write_sim_data)
164 end if
165
166 print *, "Simulation output: ", trim(output_name)
167 print *, "Number of threads: ", af_get_max_threads()
168 call af_print_info(tree)
169 call af_stencil_print_info(tree)
170
171 ! Initial wall clock time
172 call system_clock(t_start, count_rate)
173 inv_count_rate = 1.0_dp / count_rate
174 time_last_print = -1e10_dp
175 time_last_output = time
176 it_last_output = it
177
178 call field_compute_energy(tree, field_energy_prev)
179 field_energy_prev_time = time
180
181 do
182 it = it + 1
183 if (time >= st_end_time) exit
184
185 if (associated(user_generic_method)) then
186 call user_generic_method(tree, time)
187 end if
188
189 ! Initialize starting position of streamer
191 call af_tree_max_cc(tree, i_electric_fld, max_field, loc_field_t0)
192 pos_emax_t0 = af_r_loc(tree, loc_field_t0)
193 end if
194
195 ! Check if streamer length exceeds the defined maximal streamer length
197 call af_tree_max_cc(tree, i_electric_fld, max_field, loc_field)
198 pos_emax = af_r_loc(tree, loc_field)
199
200 if (norm2(pos_emax_t0 - pos_emax) >= st_end_streamer_length) then
201 print *, "Streamer reached its desired length"
202 exit
203 end if
204 end if
205
206 ! Update wall clock time
207 call system_clock(t_current)
208 wc_time = (t_current - t_start) * inv_count_rate
209
210 ! Every ST_print_status_interval, print some info about progress
211 if (wc_time - time_last_print > output_status_delay) then
212 call output_status(tree, time, wc_time, it, dt)
213 time_last_print = wc_time
214 end if
215
216 time_until_next_pulse = field_pulse_period - modulo(time, field_pulse_period)
217
218 if (abs(current_voltage) > 0.0_dp .or. &
219 time_until_next_pulse < refine_prepulse_time) then
220 current_output_dt = output_dt
222 else
223 current_output_dt = output_dt * output_dt_factor_pulse_off
225 end if
226
227 ! Every output_dt or output_dit, write output
228 write_out = (time + dt >= time_last_output + current_output_dt .or. &
229 it >= it_last_output + output_dit)
230
231 if (write_out) then
232 ! Ensure that dt is non-negative, even when current_output_dt changes
233 dt = min(dt, max(0.0_dp, time_last_output + current_output_dt - time))
234 end if
235
236 ! Make sure to capture the start of the next pulse
237 start_of_new_pulse = (dt >= time_until_next_pulse)
238 if (start_of_new_pulse) then
239 dt = max(time_until_next_pulse, dt_min)
240 end if
241
242 if (photoi_enabled .and. mod(it, photoi_per_steps) == 0) then
243 t1 = omp_get_wtime()
244 call photoi_set_src(tree, time - photoi_prev_time)
245 t2 = omp_get_wtime()
247 photoi_prev_time = time
248 end if
249
250 if (st_use_electrode) then
251 call set_electrode_densities(tree)
252 end if
253
254 ! Advance over dt, but make a copy first so that we can try again if dt was too large
255 dt_lim = huge_real
256 step_accepted = .false.
257 do n = 1, max_attemps_per_time_step
258 t1 = omp_get_wtime()
259 call copy_current_state()
260 t2 = omp_get_wtime()
262
263 call af_advance(tree, dt, dt_lim_step, time, all_densities, &
265
266 ! dt_lim is the minimum over all steps taken (including rejected ones)
267 dt_lim = min(dt_lim, dt_lim_step)
268
269 ! Check if dt was small enough for the new state
270 step_accepted = (dt <= dt_lim_step)
271
272 if (step_accepted) then
273 exit
274 else
275 n_steps_rejected = n_steps_rejected + 1
276 write (*, "(I0,A,I0,A,2E12.4,A,F6.2)") it, " Step rejected (#", &
277 n_steps_rejected, ") (dt, dt_lim) = ", dt, dt_lim, &
278 " frac", fraction_steps_rejected
279 call output_status(tree, time, wc_time, it, dt)
280
281 ! Go back to previous state and try with a smaller dt
282 dt = dt_safety_factor * dt_lim_step
283 time = global_time
284 write_out = .false. ! Since we advance less far in time
285 call restore_previous_state()
286 end if
287 end do
288
289 ! The approximate fraction of rejected steps, over the last ~100
290 fraction_steps_rejected = 0.99_dp * fraction_steps_rejected
291 if (n > 1) fraction_steps_rejected = fraction_steps_rejected + 0.01_dp
292
293 if (n == max_attemps_per_time_step+1) &
294 error stop "All time steps were rejected"
295
296 ! Update global variable based on current space-integrated data
298 sum(st_current_rates(1:n_reactions, :), dim=2) * dt
300 sum(st_current_jdote(1, :)) * dt
301
302 ! Estimate electric current according to Sato's equation V*I = sum(J.E),
303 ! where J includes both the conduction current and the displacement
304 ! current, see 10.1088/0022-3727/32/5/005.
305 ! The latter is computed through the field energy, which contains
306 ! some noise, so the current is only updated every N iterations.
307 if (mod(it, current_update_per_steps) == 0) then
308 call field_compute_energy(tree, field_energy)
309
310 ! Time derivative of field energy
311 tmp = (field_energy - field_energy_prev)/(time - field_energy_prev_time)
312 field_energy_prev = field_energy
313 field_energy_prev_time = time
314
315 ! Add J.E term
316 if (abs(current_voltage) > 0.0_dp) then
319 else
322 end if
323 end if
324
325 ! Make sure field is available for latest time state
326 t1 = omp_get_wtime()
327 call field_compute(tree, mg, 0, time, .true.)
328 t2 = omp_get_wtime()
329 wc_time_field = wc_time_field + t2 - t1
330
331 if (gas_dynamics) then
332 call coupling_add_fluid_source(tree, dt)
333
334 ! Go back to time at beginning of step
335 time = global_time
336
337 call af_advance(tree, dt, dt_gas_lim, time, &
340 else
341 dt_gas_lim = dt_max
342 end if
343
344 ! Do not increase time step when many steps are rejected. Here we use a
345 ! pretty arbitrary threshold of 0.1
347 if (fraction_steps_rejected > 0.1_dp) tmp = 1.0_dp
348
349 dt = min(tmp * global_dt, dt_safety_factor * min(dt_lim, dt_gas_lim))
350
351 if (start_of_new_pulse) then
352 ! Start a new pulse with a small time step
353 dt = dt_min
354 if (associated(user_new_pulse_conditions)) then
355 call af_loop_box(tree, user_new_pulse_conditions)
356 end if
357 end if
358
359 ! dt is modified when writing output, global_dt not
360 global_dt = dt
361 global_time = time
362
363 if (global_dt < dt_min) then
364 write(error_unit, "(A,E12.4,A)") " Time step (dt =", global_dt, &
365 ") getting too small"
366 write(error_unit, *) "See the documentation on time integration"
367 call output_status(tree, time, wc_time, it, dt)
368 write_out = .true.
369 end if
370
371 t1 = omp_get_wtime()
372 if (write_out) then
373 output_cnt = output_cnt + 1
374 time_last_output = global_time
375 it_last_output = it
376 call output_write(tree, output_cnt, wc_time, write_sim_data)
377 if (st_use_dielectric .and. surface_output) then
378 call surface_write_output(tree, diel, [i_photon_flux, i_surf_dens], &
379 ["photon_flux", "surf_dens "], output_name, output_cnt)
380 end if
381 end if
382 t2 = omp_get_wtime()
384
385 if (global_dt < dt_min) error stop "dt too small"
386
388 if (axisymmetric_is_branching(tree)) then
389 error stop "Branching detected, abort"
390 end if
391 end if
392
393 if (mod(it, refine_per_steps) == 0) then
394 ! Restrict species, for the ghost cells near refinement boundaries
395 call af_restrict_tree(tree, all_densities)
396 call af_gc_tree(tree, all_densities)
397
398 if (gas_dynamics) then
399 call af_restrict_tree(tree, gas_vars)
400 call af_gc_tree(tree, gas_vars)
401 end if
402
403 if (st_use_dielectric) then
404 ! Make sure there are no refinement jumps across the dielectric
405 call surface_get_refinement_links(diel, ref_links)
406 call af_adjust_refinement(tree, refine_routine, ref_info, &
407 refine_buffer_width, ref_links)
408 call surface_update_after_refinement(tree, diel, ref_info)
409 else
410 call af_adjust_refinement(tree, refine_routine, ref_info, &
412 end if
413
414 if (ref_info%n_add > 0 .or. ref_info%n_rm > 0) then
415 ! Compute the field on the new mesh
416 call field_compute(tree, mg, 0, time, .true.)
417
418 ! Compute photoionization on new mesh
419 if (photoi_enabled) then
420 call photoi_set_src(tree, time - photoi_prev_time)
421 photoi_prev_time = time
422 end if
423 end if
424 end if
425
426 t3 = omp_get_wtime()
428 end do
429
430 call output_status(tree, time, wc_time, it, dt)
431
432 write(*, "(A)") "Computational cost breakdown (%)"
433 write(*, "(7(A10))") "flux", "source", "copy", "field", "output", &
434 "refine", "photoi"
435 write(*, "(7(F10.2))") 1e2*wc_time_flux/wc_time, 1e2*wc_time_source/wc_time, &
436 1e2*wc_time_copy_state/wc_time, 1e2*wc_time_field/wc_time, &
437 1e2*wc_time_output/wc_time, 1e2*wc_time_refine/wc_time, &
438 1e2*wc_time_photoi/wc_time
439
440contains
441
442 subroutine initialize_modules(cfg, tree, mg, restart)
443 use m_user
444 use m_table_data
446
447 type(cfg_t), intent(inout) :: cfg
448 type(af_t), intent(inout) :: tree
449 type(mg_t), intent(inout) :: mg
450 logical, intent(in) :: restart
451
452 call model_initialize(cfg)
453 call user_initialize(cfg, tree)
454 call dt_initialize(cfg)
456
457 call table_data_initialize(cfg)
458 call gas_initialize(tree, cfg)
460 call chemistry_initialize(tree, cfg)
461 call st_initialize(tree, cfg, ndim)
462 call photoi_initialize(tree, cfg)
463 call refine_initialize(cfg)
464 call field_initialize(tree, cfg, mg)
465 call init_cond_initialize(tree, cfg)
466 call output_initialize(tree, cfg)
467 call dielectric_initialize(tree, cfg)
468
469 call output_initial_summary(tree)
470
471 end subroutine initialize_modules
472
473 subroutine set_initial_conditions(tree, mg)
474 type(af_t), intent(inout) :: tree
475 type(mg_t), intent(inout) :: mg
476 integer :: n, lvl, i, id, n_surface_variables
477
478 ! Refine up to maximal grid spacing
479 do lvl = 1, af_max_lvl-1
480 if (all(af_lvl_dr(tree, lvl) <= refine_max_dx)) exit
481 end do
482 call af_refine_up_to_lvl(tree, lvl)
483
484 ! Set initial conditions
485 call af_loop_box(tree, init_cond_set_box)
486 if (associated(user_initial_conditions)) then
487 call af_loop_box(tree, user_initial_conditions)
488 else if (st_use_dielectric) then
489 error stop "use_dielectric requires user_initial_conditions to be set"
490 end if
491
492 ! This placement is so that users can set epsilon before the coarse grid
493 ! solver is constructed
494 call mg_init(tree, mg)
495
496 if (st_use_dielectric) then
497 ! To store the photon flux (single state), surface charge (multiple
498 ! states) and a copy of the surface charge
499 n_surface_variables = af_advance_num_steps(time_integrator) + 2
500 call surface_initialize(tree, i_eps, diel, n_surface_variables)
501 end if
502
503 do n = 1, 100
504 call field_compute(tree, mg, 0, time, .false.)
505
506 if (st_use_dielectric) then
507 ! Make sure there are no refinement jumps across the dielectric
508 call surface_get_refinement_links(diel, ref_links)
509 call af_adjust_refinement(tree, refine_routine, ref_info, &
510 refine_buffer_width, ref_links)
511 call surface_update_after_refinement(tree, diel, ref_info)
512 else
513 call af_adjust_refinement(tree, refine_routine, ref_info, &
515 end if
516
517 ! Set initial conditions on newly added boxes
518 do lvl = 1, size(ref_info%lvls)
519 !$omp parallel do private(id)
520 do i = 1, size(ref_info%lvls(lvl)%add)
521 id = ref_info%lvls(lvl)%add(i)
522 call init_cond_set_box(tree%boxes(id))
523 if (associated(user_initial_conditions)) &
524 call user_initial_conditions(tree%boxes(id))
525 end do
526 !$omp end parallel do
527 end do
528
529 if (ref_info%n_add == 0) exit
530 end do
531
532 end subroutine set_initial_conditions
533
534 subroutine write_sim_data(my_unit)
535 integer, intent(in) :: my_unit
536
537 write(my_unit) datfile_version
538
539 write(my_unit) it
540 write(my_unit) output_cnt
541 write(my_unit) time
542 write(my_unit) global_time
543 write(my_unit) photoi_prev_time
544 write(my_unit) global_dt
545
546 write(my_unit) st_global_rates
547 write(my_unit) st_global_jdote
548 write(my_unit) fraction_steps_rejected
549 end subroutine write_sim_data
550
551 subroutine read_sim_data(my_unit)
552 integer, intent(in) :: my_unit
553 integer :: version
554
555 read(my_unit) version
556 if (version /= datfile_version) error stop "Different datfile version"
557
558 read(my_unit) it
559 read(my_unit) output_cnt
560 read(my_unit) time
561 read(my_unit) global_time
562 read(my_unit) photoi_prev_time
563 read(my_unit) global_dt
564
565 read(my_unit) st_global_rates
566 read(my_unit) st_global_jdote
567 read(my_unit) fraction_steps_rejected
568
569 dt = global_dt
570 end subroutine read_sim_data
571
572 subroutine print_program_name()
573 print *, " __ _ _____ _ "
574 print *, " /\ / _(_) / ____| | "
575 print *, " / \ | |_ ___ _____ _____| (___ | |_ _ __ ___ __ _ _ __ ___ ___ _ __ "
576 print *, " / /\ \ | _| \ \ / / _ \______\___ \| __| '__/ _ \/ _` | '_ ` _ \ / _ \ '__|"
577 print *, " / ____ \| | | |\ V / (_) | ____) | |_| | | __/ (_| | | | | | | __/ | "
578 print *, " /_/ \_\_| |_| \_/ \___/ |_____/ \__|_| \___|\__,_|_| |_| |_|\___|_| "
579 print *, " "
580 end subroutine print_program_name
581
582 !> Set species boundary conditions at the electrode
583 subroutine set_electrode_densities(tree)
584 type(af_t), intent(inout) :: tree
585
586 call af_loop_box(tree, electrode_species_bc)
587 end subroutine set_electrode_densities
588
589 !> Set species boundary conditions at the electrode
590 !> @todo Set Neumann boundary conditions in the actual flux computation
591 subroutine electrode_species_bc(box)
592 type(box_t), intent(inout) :: box
593 real(dp) :: lsf_nb(2*NDIM), dens_nb(2*NDIM)
594 integer :: nc, IJK
595
596 if (iand(box%tag, mg_lsf_box) == 0) return
597
598 nc = box%n_cell
599 do kji_do(1, nc)
600 if (box%cc(ijk, i_lsf) < 0) then
601 ! Set all species densities to zero
602 box%cc(ijk, all_densities) = 0.0_dp
603
604#if NDIM == 1
605 lsf_nb = [box%cc(i-1, i_lsf), &
606 box%cc(i+1, i_lsf)]
607#elif NDIM == 2
608 lsf_nb = [box%cc(i-1, j, i_lsf), &
609 box%cc(i+1, j, i_lsf), &
610 box%cc(i, j-1, i_lsf), &
611 box%cc(i, j+1, i_lsf)]
612#elif NDIM == 3
613 lsf_nb = [box%cc(i-1, j, k, i_lsf), &
614 box%cc(i+1, j, k, i_lsf), &
615 box%cc(i, j-1, k, i_lsf), &
616 box%cc(i, j+1, k, i_lsf), &
617 box%cc(i, j, k-1, i_lsf), &
618 box%cc(i, j, k+1, i_lsf)]
619#endif
620
621 if (any(lsf_nb > 0) .and. &
622 associated(bc_species, af_bc_neumann_zero)) then
623 ! At the boundary of the electrode
624#if NDIM == 1
625 dens_nb = [box%cc(i-1, i_electron), &
626 box%cc(i+1, i_electron)]
627#elif NDIM == 2
628 dens_nb = [box%cc(i-1, j, i_electron), &
629 box%cc(i+1, j, i_electron), &
630 box%cc(i, j-1, i_electron), &
631 box%cc(i, j+1, i_electron)]
632#elif NDIM == 3
633 dens_nb = [box%cc(i-1, j, k, i_electron), &
634 box%cc(i+1, j, k, i_electron), &
635 box%cc(i, j-1, k, i_electron), &
636 box%cc(i, j+1, k, i_electron), &
637 box%cc(i, j, k-1, i_electron), &
638 box%cc(i, j, k+1, i_electron)]
639#endif
640
641 ! Set electron density to average of outside neighbors
642 box%cc(ijk, i_electron) = sum(dens_nb, mask=(lsf_nb > 0)) / &
643 count(lsf_nb > 0)
644 ! Set first positive ion density for charge neutrality
645 box%cc(ijk, i_1pos_ion) = box%cc(ijk, i_electron)
646 end if
647 end if
648 end do; close_do
649 end subroutine electrode_species_bc
650
651 !> Copy current state for densities and field
652 subroutine copy_current_state()
653 integer :: n_states
654
655 n_states = af_advance_num_steps(time_integrator)
656 call af_tree_copy_ccs(tree, all_densities, all_densities+n_states)
657
658 ! Copy potential
659 call af_tree_copy_cc(tree, i_phi, i_phi+1)
660
661 if (st_use_dielectric) then
662 call surface_copy_variable(diel, i_surf_dens, i_surf_dens+n_states)
663 end if
664 end subroutine copy_current_state
665
666 !> Restore state for densities and field
667 subroutine restore_previous_state()
668 integer :: n_states
669
670 n_states = af_advance_num_steps(time_integrator)
671
672 call af_tree_copy_ccs(tree, all_densities+n_states, all_densities)
673
674 ! Copy potential and compute field again
675 call af_tree_copy_cc(tree, i_phi+1, i_phi)
676 call field_from_potential(tree, mg)
677
678 if (st_use_dielectric) then
679 call surface_copy_variable(diel, i_surf_dens+n_states, i_surf_dens )
680 end if
681 end subroutine restore_previous_state
682
683 !> A wrapper routine to set the gas density in a box by a user-defined
684 !> function
685 subroutine set_gas_density_from_user_function(box, iv)
686 type(box_t), intent(inout) :: box !< Box to fill values in
687 integer, intent(in) :: iv !< Index of variable
688 integer :: IJK, nc
689 nc = box%n_cell
690
691 do kji_do(0, nc+1)
692 box%cc(ijk, iv) = user_gas_density(box, ijk)
693 end do; close_do
694 end subroutine set_gas_density_from_user_function
695
696 logical function axisymmetric_is_branching(tree)
697 type(af_t), intent(in) :: tree
698 real(dp) :: max_field, axis_field, rmin(NDIM), rmax(NDIM)
699 type(af_loc_t) :: loc_field
700
701 ! Get location of max(E) in full domain
702 call af_tree_max_cc(tree, i_electric_fld, max_field, loc_field)
703
704 ! Compare with maximum in region near axis
705 rmin = 0.0_dp
706 rmax(1) = 1e-2_dp * st_domain_len(1)
707 rmax(2:) = st_domain_len(2:)
708 call analysis_max_var_region(tree, i_electric_fld, rmin, &
709 rmax, axis_field, loc_field)
710
711 axisymmetric_is_branching = (max_field > 1.1_dp * axis_field)
712
713 if (axisymmetric_is_branching) print *, max_field, axis_field
714 end function axisymmetric_is_branching
715
716end program streamer
Module with routines to help analyze simulations.
Definition m_analysis.f90:2
Module for handling chemical reactions.
subroutine, public chemistry_initialize(tree, cfg)
Initialize module and load chemical reactions.
subroutine, public chemistry_get_breakdown_field(field_td, min_growth_rate)
Get the breakdown field in Townsend.
integer, public, protected n_reactions
Number of reactions present.
Module for the coupling between the gas dynamics and the fluid model.
Definition m_coupling.f90:2
subroutine, public coupling_add_fluid_source(tree, dt)
Add source terms form the fluid model to the Euler equations.
subroutine, public coupling_update_gas_density(tree)
Update gas number density in the fluid model.
Module with settings and routines to handle dielectrics.
integer, parameter, public i_surf_dens
logical, public, protected surface_output
subroutine, public dielectric_initialize(tree, cfg)
type(surfaces_t), public diel
To store dielectric surface.
integer, parameter, public i_photon_flux
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
real(dp), public, protected dt_max
Definition m_dt.f90:40
real(dp), public, protected dt_safety_factor
Definition m_dt.f90:28
subroutine, public dt_initialize(cfg)
Initialize the time step module.
Definition m_dt.f90:57
real(dp), public, protected dt_min
Definition m_dt.f90:43
real(dp), public, protected dt_max_growth_factor
Maximal relative increase dt for the next iteration.
Definition m_dt.f90:46
Module to compute electric fields.
Definition m_field.f90:2
real(dp), public, protected field_pulse_period
Time of one complete voltage pulse.
Definition m_field.f90:33
subroutine, public field_compute_energy(tree, field_energy)
Compute total field energy in Joule, defined as the volume integral over 1/2 * epsilon * E^2.
Definition m_field.f90:765
real(dp), public, protected current_voltage
The current applied voltage.
Definition m_field.f90:90
subroutine, public field_initialize(tree, cfg, mg)
Initialize this module.
Definition m_field.f90:108
subroutine, public field_compute(tree, mg, s_in, time, have_guess)
Compute electric field on the tree. First perform multigrid to get electric potential,...
Definition m_field.f90:406
Fluid model module.
Definition m_fluid.f90:2
subroutine, public forward_euler(tree, dt, dt_stiff, dt_lim, time, s_deriv, n_prev, s_prev, w_prev, s_out, i_step, n_steps)
Advance fluid model using forward Euler step. If the equation is written as y' = f(y),...
Definition m_fluid.f90:23
Module that stores parameters related to the gas.
Definition m_gas.f90:2
subroutine, public gas_forward_euler(tree, dt, dt_stiff, dt_lim, time, s_deriv, n_prev, s_prev, w_prev, s_out, i_step, n_steps)
A forward-Euler method for the Euler equations.
Definition m_gas.f90:204
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
integer, dimension(n_vars_euler), public, protected gas_vars
Definition m_gas.f90:69
logical, public, protected gas_constant_density
Whether the gas has a constant density.
Definition m_gas.f90:12
subroutine, public gas_initialize(tree, cfg)
Initialize this module.
Definition m_gas.f90:101
Module to help setting up initial conditions.
subroutine, public init_cond_set_box(box)
Sets the initial condition.
subroutine, public init_cond_initialize(tree, cfg)
Set the initial conditions from the configuration.
Module to set the type of model.
Definition m_model.f90:2
subroutine, public model_initialize(cfg)
Initialize the module.
Definition m_model.f90:28
Module with methods and parameters related to writing output.
Definition m_output.f90:2
integer, public, protected output_dt_factor_pulse_off
Definition m_output.f90:94
subroutine, public output_status(tree, time, wc_time, it, dt)
Print short status message.
Definition m_output.f90:858
real(dp), public, protected output_dt
Definition m_output.f90:22
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), public, protected output_status_delay
Definition m_output.f90:91
integer, public, protected output_dit
Definition m_output.f90:25
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
Top-module for photoionization, which can make use of different methods.
Definition m_photoi.f90:2
subroutine, public photoi_set_src(tree, dt)
Sets the photoionization.
Definition m_photoi.f90:141
logical, public, protected photoi_enabled
Whether photoionization is enabled.
Definition m_photoi.f90:15
subroutine, public photoi_initialize(tree, cfg)
Initialize photoionization parameters.
Definition m_photoi.f90:65
integer, public, protected photoi_per_steps
Update photoionization every N time step.
Definition m_photoi.f90:49
Module with grid refinement settings and routines.
Definition m_refine.f90:2
integer, public, protected refine_buffer_width
Definition m_refine.f90:10
subroutine, public refine_initialize(cfg)
Initialize the grid refinement options.
Definition m_refine.f90:93
real(dp), public current_electrode_dx
Definition m_refine.f90:47
real(dp), public refine_electrode_dx
Definition m_refine.f90:43
real(dp), public, protected refine_prepulse_time
Definition m_refine.f90:54
integer, public, protected refine_per_steps
Definition m_refine.f90:13
procedure(af_subr_ref), pointer, public refine_routine
Definition m_refine.f90:83
real(dp), public, protected electrode_derefine_factor
Definition m_refine.f90:51
real(dp), public, protected refine_max_dx
Definition m_refine.f90:19
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), public wc_time_refine
procedure(af_subr_prolong), pointer, public, protected st_prolongation_method
Method used to prolong (interpolate) densities.
real(dp), public wc_time_output
integer, public, protected i_eps
Index can be set to include a dielectric.
real(dp), dimension(:), allocatable, public st_global_rates
Global sum of reaction rates.
integer, public, protected current_update_per_steps
Per how many iterations the electric current is computed.
integer, public, protected st_initial_streamer_pos_steps_wait
Wait n steps before initializing streamer begin position.
real(dp), public wc_time_field
real(dp), public st_global_jdote_current
Electric current through electrodes due to J.E.
real(dp), public global_dt
Global time step.
type(mg_t), public mg
Multigrid option structure.
integer, dimension(ndim), public, protected st_coarse_grid_size
Size of the coarse grid.
subroutine, public st_initialize(tree, cfg, ndim)
Create the configuration file with default values.
integer, public, protected st_box_size
The size of the boxes that we use to construct our mesh.
real(dp), dimension(:, :), allocatable, public st_current_jdote
Current sum of J.E per thread.
logical, dimension(ndim), public, protected st_periodic
Whether the domain is periodic (per dimension)
real(dp), public global_time
Global time.
procedure(af_subr_bc), pointer, public, protected bc_species
Boundary condition for the plasma species.
logical, public, protected st_use_electrode
Whether to include an electrode.
real(dp), public wc_time_source
integer, public, protected i_lsf
Index can be set to include an electrode.
logical, public, protected st_use_dielectric
Whether a dielectric is used.
integer, public, protected i_electron
Index of electron density.
integer, public, protected i_1pos_ion
Index of first positive ion species.
logical, public, protected st_abort_axisymmetric_if_branching
Abort axisymmetric simulations if there is branching.
integer, public, protected i_phi
Index of electrical potential.
real(dp), public wc_time_flux
real(dp), public st_global_jdote
Global sum of J.E.
integer, public, protected i_electric_fld
Index of electric field norm.
real(dp), public wc_time_copy_state
real(dp), public, protected st_end_streamer_length
Streamer length at which the simulation will stop.
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.
real(dp), public wc_time_photoi
logical, public, protected st_use_end_streamer_length
Whether streamer length is used as a simulation stopping.
real(dp), dimension(:, :), allocatable, public st_current_rates
Current sum of reaction rates per thread.
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_data_initialize(cfg)
Initialize this module.
Module that provides routines for reading in arbritrary transport data.
subroutine, public transport_data_initialize(cfg)
Initialize the transport coefficients.
Module with basic types.
Definition m_types.f90:2
real(dp), parameter huge_real
Huge number.
Definition m_types.f90:16
character(len= *), parameter undefined_str
Undefined string.
Definition m_types.f90:10
Module that contains physical and numerical constants.
This module contains all the methods that users can customize.
procedure(generic_subr), pointer user_generic_method
Generic procedure that is called every time step.
procedure(af_subr), pointer user_initial_conditions
If defined, call this routine after setting initial conditions.
procedure(af_subr), pointer user_new_pulse_conditions
If defined, call this routine after a new voltage pulse starts.
Template for user code, this one simply uses the default routines.
Definition m_user.f90:2
subroutine, public user_initialize(cfg, tree)
Definition m_user.f90:16
program streamer
Program to perform streamer simulations with AMR.
Definition streamer.f90:2
subroutine initialize_modules(cfg, tree, mg, restart)
Definition streamer.f90:443