afivo-streamer 1.1
1D/2D/3D streamer simulations with AMR
Loading...
Searching...
No Matches
m_gas.f90
Go to the documentation of this file.
1!> Module that stores parameters related to the gas
2module m_gas
3#include "../afivo/src/cpp_macros.h"
4 use m_types
5 use m_af_types
7
8 implicit none
9 private
10
11 !> Whether the gas has a constant density
12 logical, public, protected :: gas_constant_density = .true.
13
14 !> Whether the gas dynamics are simulated
15 logical, public, protected :: gas_dynamics = .false.
16
17 ! Pressure of the gas in bar
18 real(dp), public, protected :: gas_pressure = 1.0_dp
19
20 ! Gas temperature in Kelvin
21 real(dp), public, protected :: gas_temperature = 300.0_dp
22
23 ! List of gas fractions (the last one is 1.0 for the total density)
24 real(dp), allocatable, public, protected :: gas_fractions(:)
25
26 ! List of gas densities (the last one is the total density)
27 real(dp), allocatable, public, protected :: gas_densities(:)
28
29 ! List of gas components (the last one is M, the total density)
30 character(len=comp_len), allocatable, public, protected :: gas_components(:)
31
32 ! Gas number density (1/m3)
33 real(dp), public, protected :: gas_number_density
34
35 ! Inverse gas number density (1/m3)
36 real(dp), public, protected :: gas_inverse_number_density
37
38 ! Convert V/m to Townsend
39 real(dp), parameter, public :: si_to_townsend = 1e21_dp
40
41 ! Convert Townsend to V/m
42 real(dp), parameter, public :: townsend_to_si = 1e-21_dp
43
44 ! Index of the gas number density
45 integer, public, protected :: i_gas_dens = -1
46
47 ! Gas mean molecular weight (kg)
48 real(dp), public, protected :: gas_molecular_weight = 28.8_dp * uc_atomic_mass
49
50 !> Vibration-Translation relaxation time (s)
51 real(dp), public, protected :: gas_vt_time = 20e-6_dp
52
53 ! Joule heating efficiency (between 0.0 and 1.0)
54 real(dp), public, protected :: gas_heating_efficiency = 1.0_dp
55
56 ! Fraction of gas heating that occurs via V-T relaxation
57 real(dp), public, protected :: gas_fraction_slow_heating = 0.0_dp
58
59 ! Factor for the EHD force term (should be 1 by default)
60 real(dp), public, protected :: gas_ehd_factor = 1.0_dp
61
62 ! Ratio of heat capacities (polytropic index)
63 real(dp), public, protected :: gas_euler_gamma = 1.4_dp
64
65 ! Number of variables for the Euler equations
66 integer, parameter, public :: n_vars_euler = 2+ndim
67
68 ! Indices of the Euler variables
69 integer, public, protected :: gas_vars(n_vars_euler) = -1
70 ! Indices of the primiteve Euler variables
71 integer, public, protected :: gas_prim_vars(n_vars_euler+1) = -1
72
73 ! Indices of the Euler fluxes
74 integer, public, protected :: gas_fluxes(n_vars_euler) = -1
75
76 ! Index of slow heating term
77 integer, public, protected :: i_vibration_energy = -1
78
79 ! Names of the Euler variables
80 character(len=name_len), public, protected :: gas_var_names(n_vars_euler)
81
82 ! Indices defining the order of the gas dynamics variables
83 integer, parameter, public :: i_rho = 1
84#if NDIM == 1
85 integer, parameter, public :: i_mom(ndim) = [2]
86#elif NDIM == 2
87 integer, parameter, public :: i_mom(ndim) = [2, 3]
88#elif NDIM == 3
89 integer, parameter, public :: i_mom(ndim) = [2, 3, 4]
90#endif
91 integer, parameter, public :: i_e = i_mom(ndim) + 1
92
93 public :: gas_initialize
94 public :: gas_index
95 public :: gas_forward_euler
96
97contains
98
99 !> Initialize this module
100 subroutine gas_initialize(tree, cfg)
101 use m_config
104 use m_dt
105 type(af_t), intent(inout) :: tree
106 type(cfg_t), intent(inout) :: cfg
107 integer :: n
108
109 call cfg_add_get(cfg, "gas%dynamics", gas_dynamics, &
110 "Whether the gas dynamics are simulated")
111
112 if (gas_dynamics) then
113 gas_var_names(i_rho) = "gas_rho"
114 gas_var_names(i_mom(1)) = "gas_mom_x"
115#if NDIM > 1
116 gas_var_names(i_mom(2)) = "gas_mom_y"
117#endif
118#if NDIM == 3
119 gas_var_names(i_mom(3)) = "gas_mom_z"
120#endif
121 gas_var_names(i_e) = "gas_e"
122
123 gas_constant_density = .false.
124 call af_add_cc_variable(tree, "M", ix=i_gas_dens)
125
126 do n = 1, n_vars_euler
127 call af_add_cc_variable(tree, gas_var_names(n), ix=gas_vars(n), &
128 n_copies=af_advance_num_steps(time_integrator))
129 call af_add_fc_variable(tree, "flux", ix=gas_fluxes(n))
130
131 if (tree%coord_t == af_cyl .and. n == i_mom(1)) then
132 call af_set_cc_methods(tree, gas_vars(n), bc_radial_momentum)
133 else
134 call af_set_cc_methods(tree, gas_vars(n), af_bc_neumann_zero)
135 end if
136 end do
137 call af_add_cc_variable(tree, "u", ix=gas_prim_vars(i_mom(1)))
138#if NDIM > 1
139 call af_add_cc_variable(tree, "v", ix=gas_prim_vars(i_mom(2)))
140#endif
141#if NDIM == 3
142 call af_add_cc_variable(tree, "w", ix=gas_prim_vars(i_mom(3)))
143#endif
144 call af_add_cc_variable(tree, "pressure", ix=gas_prim_vars(i_e))
145 call af_add_cc_variable(tree, "temperature", ix=gas_prim_vars(i_e+1))
146 else if (associated(user_gas_density)) then
147 gas_constant_density = .false.
148 call af_add_cc_variable(tree, "M", ix=i_gas_dens)
149 else
150 gas_constant_density = .true.
151 end if
152
153 call cfg_add_get(cfg, "gas%pressure", gas_pressure, &
154 "The gas pressure (bar)")
155 call cfg_add_get(cfg, "gas%temperature", gas_temperature, &
156 "The gas temperature (Kelvin)")
157 !> @todo compute gas molecular weight from gas composition
158 call cfg_add_get(cfg, "gas%molecular_weight", gas_molecular_weight, &
159 "Gas mean molecular weight (kg), for gas dynamics")
160 call cfg_add_get(cfg, "gas%heating_efficiency", gas_heating_efficiency, &
161 "Joule heating efficiency (between 0.0 and 1.0)")
162 call cfg_add_get(cfg, "gas%fraction_slow_heating", gas_fraction_slow_heating, &
163 "Fraction of gas heating that occurs via V-T relaxation")
164 call cfg_add_get(cfg, "gas%vt_relaxation_time", gas_vt_time, &
165 "Vibration-Translation relaxation time")
166 call cfg_add_get(cfg, "gas%EHD_factor", gas_ehd_factor, &
167 "Factor for the EHD force term (should be 1 by default)")
168
169 if (gas_fraction_slow_heating > 0) then
170 call af_add_cc_variable(tree, "vibrational_energy", ix=i_vibration_energy)
171 end if
172
173 ! Ideal gas law
174 gas_number_density = 1e5_dp * gas_pressure / &
177
178 call cfg_add(cfg, "gas%components", ["N2", "O2"], &
179 "Gas component names", .true.)
180 call cfg_add(cfg, "gas%fractions", [0.8_dp, 0.2_dp], &
181 "Gas component fractions", .true.)
182
183 call cfg_get_size(cfg, "gas%components", n)
184 allocate(gas_components(n+1))
185 allocate(gas_fractions(n+1))
186 call cfg_get(cfg, "gas%components", gas_components(1:n))
187 call cfg_get(cfg, "gas%fractions", gas_fractions(1:n))
188
189 gas_components(n+1) = "M"
190 gas_fractions(n+1) = 1.0_dp
191
192 if (any(gas_fractions < 0.0_dp)) &
193 error stop "gas%fractions has negative value"
194 if (abs(sum(gas_fractions(1:n)) - 1.0_dp) > 1e-4_dp) &
195 error stop "gas%fractions not normalized"
196
198
199 end subroutine gas_initialize
200
201 !> A forward-Euler method for the Euler equations
202 subroutine gas_forward_euler(tree, dt, dt_stiff, dt_lim, time, s_deriv, n_prev, &
203 s_prev, w_prev, s_out, i_step, n_steps)
204 use m_af_flux_schemes
205 use m_af_limiters
206 type(af_t), intent(inout) :: tree
207 real(dp), intent(in) :: dt !< Time step
208 real(dp), intent(in) :: dt_stiff !< Time step for stiff terms (IMEX)
209 real(dp), intent(inout) :: dt_lim !< Computed time step limit
210 real(dp), intent(in) :: time !< Current time
211 integer, intent(in) :: s_deriv !< State to compute derivatives from
212 integer, intent(in) :: n_prev !< Number of previous states
213 integer, intent(in) :: s_prev(n_prev) !< Previous states
214 real(dp), intent(in) :: w_prev(n_prev) !< Weights of previous states
215 integer, intent(in) :: s_out !< Output state
216 integer, intent(in) :: i_step !< Step of the integrator
217 integer, intent(in) :: n_steps !< Total number of steps
218 real(dp) :: dt_dummy(0)
219
220 call flux_generic_tree(tree, n_vars_euler, gas_vars, s_deriv, &
221 gas_fluxes, dt_lim, max_wavespeed, get_fluxes, &
222 flux_dummy_modify, flux_dummy_line_modify, to_primitive, &
223 to_conservative, af_limiter_vanleer_t)
224 if (tree%coord_t == af_cyl) then
225 call flux_update_densities(tree, dt, n_vars_euler, gas_vars, n_vars_euler, &
226 gas_vars, gas_fluxes, s_deriv, n_prev, s_prev, w_prev, s_out, &
227 add_geometric_source, 0, dt_dummy)
228 else
229 call flux_update_densities(tree, dt, n_vars_euler, gas_vars, n_vars_euler, &
230 gas_vars, gas_fluxes, s_deriv, n_prev, s_prev, w_prev, s_out, &
231 flux_dummy_source, 0, dt_dummy)
232 end if
233 end subroutine gas_forward_euler
234
235
236 !> Add geometric source term for axisymmetric simulations
237 subroutine add_geometric_source(box, dt, n_vars, i_cc, s_deriv, s_out, &
238 n_dt, dt_lim, mask)
239 type(box_t), intent(inout) :: box
240 real(dp), intent(in) :: dt
241 integer, intent(in) :: n_vars
242 integer, intent(in) :: i_cc(n_vars)
243 integer, intent(in) :: s_deriv
244 integer, intent(in) :: s_out
245 logical, intent(in) :: mask(dtimes(box%n_cell))
246 integer, intent(in) :: n_dt
247 real(dp), intent(inout) :: dt_lim(n_dt)
248
249#if NDIM == 2
250 real(dp) :: pressure(dtimes(box%n_cell))
251 real(dp) :: inv_radius
252 integer :: nc, i
253
254 nc = box%n_cell
255 pressure = get_pressure(box, s_deriv)
256
257 do i = 1, nc
258 inv_radius = 1/af_cyl_radius_cc(box, i)
259 where (mask(i, :))
260 box%cc(i, 1:nc, i_cc(i_mom(1))+s_out) = &
261 box%cc(i, 1:nc, i_cc(i_mom(1))+s_out) + dt * &
262 pressure(i, :) * inv_radius
263 end where
264 end do
265#endif
266 end subroutine add_geometric_source
267
268#if NDIM == 2
269 pure function get_pressure(box, s_in) result(pressure)
270 type(box_t), intent(in) :: box
271 integer, intent(in) :: s_in
272 real(dp) :: pressure(dtimes(box%n_cell))
273 integer :: nc
274
275 nc = box%n_cell
276 pressure = (gas_euler_gamma-1.0_dp) * (&
277 box%cc(dtimes(1:nc), gas_vars(i_e)+s_in) - 0.5_dp * &
278 sum(box%cc(dtimes(1:nc), gas_vars(i_mom)+s_in)**2, dim=ndim+1) / &
279 box%cc(dtimes(1:nc), gas_vars(i_rho)+s_in))
280 end function get_pressure
281#endif
282
283 !> Find index of a gas component, return -1 if not found
284 elemental integer function gas_index(name)
285 character(len=*), intent(in) :: name
286 do gas_index = 1, size(gas_components)
287 if (gas_components(gas_index) == name) exit
288 end do
289 if (gas_index == size(gas_components)+1) gas_index = -1
290 end function gas_index
291
292 subroutine to_primitive(n_values, n_vars, u)
293 integer, intent(in) :: n_values, n_vars
294 real(dp), intent(inout) :: u(n_values, n_vars)
295 integer :: idim
296
297 do idim = 1, ndim
298 u(:, i_mom(idim)) = u(:, i_mom(idim))/u(:, i_rho)
299 end do
300
301 u(:, i_e) = (gas_euler_gamma-1.0_dp) * (u(:, i_e) - &
302 0.5_dp*u(:, i_rho)* sum(u(:, i_mom(:))**2, dim=2))
303 end subroutine to_primitive
304
305 subroutine to_conservative(n_values, n_vars, u)
306 integer, intent(in) :: n_values, n_vars
307 real(dp), intent(inout) :: u(n_values, n_vars)
308 real(dp) :: kin_en(n_values)
309 real(dp) :: inv_fac
310 integer :: i
311
312 ! Compute kinetic energy (0.5 * rho * velocity^2)
313 kin_en = 0.5_dp * u(:, i_rho) * sum(u(:, i_mom(:))**2, dim=2)
314
315 ! Compute energy from pressure and kinetic energy
316 inv_fac = 1/(gas_euler_gamma - 1.0_dp)
317 u(:, i_e) = u(:, i_e) * inv_fac + kin_en
318
319 ! Compute momentum from density and velocity components
320 do i = 1, ndim
321 u(:, i_mom(i)) = u(:, i_rho) * u(:, i_mom(i))
322 end do
323 end subroutine to_conservative
324
325 subroutine max_wavespeed(n_values, n_var, flux_dim, u, w)
326 integer, intent(in) :: n_values !< Number of cell faces
327 integer, intent(in) :: n_var !< Number of variables
328 integer, intent(in) :: flux_dim !< In which dimension fluxes are computed
329 real(dp), intent(in) :: u(n_values, n_var) !< Primitive variables
330 real(dp), intent(out) :: w(n_values) !< Maximum speed
331 real(dp) :: sound_speeds(n_values)
332
333 sound_speeds = sqrt(gas_euler_gamma * u(:, i_e) / u(:, i_rho))
334 w = sound_speeds + abs(u(:, i_mom(flux_dim)))
335 end subroutine max_wavespeed
336
337 subroutine get_fluxes(n_values, n_var, flux_dim, u, flux, box, line_ix, s_deriv)
338 integer, intent(in) :: n_values !< Number of cell faces
339 integer, intent(in) :: n_var !< Number of variables
340 integer, intent(in) :: flux_dim !< In which dimension fluxes are computed
341 real(dp), intent(in) :: u(n_values, n_var)
342 real(dp), intent(out) :: flux(n_values, n_var)
343 type(box_t), intent(in) :: box
344 integer, intent(in) :: line_ix(ndim-1)
345 integer, intent(in) :: s_deriv !< State to compute derivatives from
346 real(dp) :: e(n_values), inv_fac
347 integer :: i
348
349 ! Compute left and right flux for conservative variables from the primitive
350 ! reconstructed values.
351
352 ! Density flux
353 flux(:, i_rho) = u(:, i_rho) * u(:, i_mom(flux_dim))
354
355 ! Momentum flux
356 do i = 1, ndim
357 flux(:, i_mom(i)) = u(:, i_rho) * &
358 u(:, i_mom(i)) * u(:, i_mom(flux_dim))
359 end do
360
361 ! Add pressure term
362 flux(:, i_mom(flux_dim)) = flux(:, i_mom(flux_dim)) + u(:, i_e)
363
364 ! Compute energy
365 inv_fac = 1/(gas_euler_gamma-1.0_dp)
366 e = u(:, i_e) * inv_fac + 0.5_dp * u(:, i_rho) * &
367 sum(u(:, i_mom(:))**2, dim=2)
368
369 ! Energy flux
370 flux(:, i_e) = u(:, i_mom(flux_dim)) * (e + u(:, i_e))
371
372 end subroutine get_fluxes
373
374 !> Boundary condition for a radial momentum flux (in axisymmetric coordinates)
375 subroutine bc_radial_momentum(box, nb, iv, coords, bc_val, bc_type)
376 type(box_t), intent(in) :: box
377 integer, intent(in) :: nb
378 integer, intent(in) :: iv
379 real(dp), intent(in) :: coords(ndim, box%n_cell**(ndim-1))
380 real(dp), intent(out) :: bc_val(box%n_cell**(ndim-1))
381 integer, intent(out) :: bc_type
382
383 if (nb == af_neighb_lowx) then
384 ! This will ensure the radial momentum is zero on the axis (by having
385 ! ghost values with opposite sign)
386 bc_type = af_bc_dirichlet
387 bc_val = 0.0_dp
388 else
389 bc_type = af_bc_neumann
390 bc_val = 0.0_dp
391 end if
392 end subroutine bc_radial_momentum
393
394end module m_gas
Module to set the time step.
Definition m_dt.f90:2
integer, public, protected time_integrator
Which time integrator is used.
Definition m_dt.f90:49
Module that stores parameters related to the gas.
Definition m_gas.f90:2
character(len=comp_len), dimension(:), allocatable, public, protected gas_components
Definition m_gas.f90:30
real(dp), public, protected gas_molecular_weight
Definition m_gas.f90:48
real(dp), public, protected gas_euler_gamma
Definition m_gas.f90:63
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
integer, dimension(n_vars_euler+1), public, protected gas_prim_vars
Definition m_gas.f90:71
elemental integer function, public gas_index(name)
Find index of a gas component, return -1 if not found.
Definition m_gas.f90:285
real(dp), dimension(:), allocatable, public, protected gas_fractions
Definition m_gas.f90:24
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), public, protected gas_temperature
Definition m_gas.f90:21
integer, dimension(ndim), parameter, public i_mom
Definition m_gas.f90:85
real(dp), public, protected gas_heating_efficiency
Definition m_gas.f90:54
integer, dimension(n_vars_euler), public, protected gas_fluxes
Definition m_gas.f90:74
real(dp), parameter, public townsend_to_si
Definition m_gas.f90:42
real(dp), parameter, public si_to_townsend
Definition m_gas.f90:39
real(dp), public, protected gas_pressure
Definition m_gas.f90:18
real(dp), dimension(:), allocatable, public, protected gas_densities
Definition m_gas.f90:27
integer, dimension(n_vars_euler), public, protected gas_vars
Definition m_gas.f90:69
real(dp), public, protected gas_fraction_slow_heating
Definition m_gas.f90:57
integer, parameter, public i_e
Definition m_gas.f90:91
logical, public, protected gas_constant_density
Whether the gas has a constant density.
Definition m_gas.f90:12
integer, public, protected i_vibration_energy
Definition m_gas.f90:77
integer, parameter, public i_rho
Definition m_gas.f90:83
real(dp), public, protected gas_vt_time
Vibration-Translation relaxation time (s)
Definition m_gas.f90:51
character(len=name_len), dimension(n_vars_euler), public, protected gas_var_names
Definition m_gas.f90:80
integer, parameter, public n_vars_euler
Definition m_gas.f90:66
real(dp), public, protected gas_ehd_factor
Definition m_gas.f90:60
subroutine, public gas_initialize(tree, cfg)
Initialize this module.
Definition m_gas.f90:101
real(dp), public, protected gas_number_density
Definition m_gas.f90:33
real(dp), public, protected gas_inverse_number_density
Definition m_gas.f90:36
Module with basic types.
Definition m_types.f90:2
Module that contains physical and numerical constants.
real(dp), parameter uc_boltzmann_const
real(dp), parameter uc_atomic_mass
This module contains all the methods that users can customize.
procedure(gas_dens_func), pointer user_gas_density
To set a user-defined gas number density.