Peano
Loading...
Searching...
No Matches
thunderstorm.py
Go to the documentation of this file.
1import sys
2import os
3
4import exahype2
5
6sys.path.insert(0, os.path.abspath(".."))
7
8from EulerPDE import get_flux, get_max_eigenvalue
9from EulerFWave import fWave
10
11from DiffusionPDE import stencil
12import GravityWaves
13
14"""
15This application is based on:
16"Breaking of thunderstorm-generated gravity waves as a source of short-period ducted waves at mesopause altitudes", Snively and Pasko, 2003, doi:10.1029/2003GL018436
17"""
18
19if __name__ == "__main__":
20 parser = exahype2.ArgumentParser(
21 "ExaHyPE 2 Gravity Waves Thunderstorm Argument Parser"
22 )
23 parser.set_defaults(
24 min_depth=5,
25 degrees_of_freedom=16,
26 end_time=3, # end time given in Snively and Pasko: 16000
27 )
28 parser.add_argument(
29 "-csv",
30 "--csv-file-path",
31 default="./thunderstorm_model.csv",
32 type=str,
33 help="Path to CSV file for use as initial conditions for this project.",
34 )
35 args = parser.parse_args()
36
37 # adapt to [1_200e3, 180e3, 1_200e3] for reduced model
38 size = [1_200e3, 220e3, 1_200e3]
39
40
43
44 atmosphere_initial_conditions = (
46 csv_file_path=args.csv_file_path,
47 initialize_density=True,
48 initialize_hydrogen=False,
49 )
50 + """
51 // momenta are in m/s * kg/m^3 = kg/(m^2 * s)
52 Q[Shortcuts::rhou] = HORIZONTAL_WINDSPEED * Q[Shortcuts::rho];
53 Q[Shortcuts::rhov] = 0.0;
54 #if DIMENSIONS == 3
55 Q[Shortcuts::rhow] = 0.0;
56 #endif
57
58 const auto M = Q[Shortcuts::O2] + Q[Shortcuts::O] + Q[Shortcuts::N2];
59 const auto iM = 1.0 / M;
60 const auto meanMolecularMass = (MOLAR_MASS_NITROGEN * Q[Shortcuts::N2] + MOLAR_MASS_MOLECULAR_OXYGEN * Q[Shortcuts::O2] + MOLAR_MASS_ATOMIC_OXYGEN * Q[Shortcuts::O]) * iM; // in kg/mol
61 const auto pressure = Q[Shortcuts::rho] * UNIVERSAL_GAS_CONSTANT * Q[Shortcuts::T] / meanMolecularMass;
62 #if !defined(WITH_GPU)
63 assertion5(pressure > 0.0, M, meanMolecularMass, Q[Shortcuts::rho], Q[Shortcuts::T], pressure);
64 #endif
65
66 const auto gamma = (1.4 * (M - Q[Shortcuts::O]) + 1.67 * Q[Shortcuts::O]) * iM;
67
68 Q[Shortcuts::E] = pressure / (gamma - 1) + 0.5 * Q[Shortcuts::rho] * (HORIZONTAL_WINDSPEED * HORIZONTAL_WINDSPEED);
69
70 Q[Shortcuts::backgroundDensity] = Q[Shortcuts::rho];
71 Q[Shortcuts::backgroundPressure] = pressure;"""
72 )
73
74 atmosphere_boundary_conditions = """
75 // normal == 0 => left
76 // normal == 1 => bottom
77 // normal == 2 => right
78 // normal == 3 => top
79
80 if (normal == 0 || normal == 2) {
81 // outflow at sides
82 for (int unknown = 0; unknown < NumberOfUnknowns + NumberOfAuxiliaryVariables; ++unknown) {
83 Qoutside[unknown] = Qinside[unknown];
84 }
85 } else if (normal == 1 || normal == 3) {
86 const auto M = Qinside[Shortcuts::O2] + Qinside[Shortcuts::O] + Qinside[Shortcuts::N2];
87 const auto iM = 1.0 / M;
88 const auto meanMolecularMass = (MOLAR_MASS_NITROGEN * Qinside[Shortcuts::N2] + MOLAR_MASS_MOLECULAR_OXYGEN * Qinside[Shortcuts::O2] + MOLAR_MASS_ATOMIC_OXYGEN * Qinside[Shortcuts::O]) * iM; // in kg/mol
89 const auto pressure = Qinside[Shortcuts::rho] * UNIVERSAL_GAS_CONSTANT * Qinside[Shortcuts::T] / meanMolecularMass;
90 #if !defined(WITH_GPU)
91 assertion5(pressure > 0.0, M, meanMolecularMass, Qinside[Shortcuts::rho], Qinside[Shortcuts::T], pressure);
92 #endif
93
94 const auto gamma = (1.4 * (M - Qinside[Shortcuts::O]) + 1.67 * Qinside[Shortcuts::O]) * iM;
95
96 // positive dy upwards, negative dz downwards
97 const auto dy = normal == 3 ? h(1) : -h(1);
98
99 const auto specificGasConstant = UNIVERSAL_GAS_CONSTANT / meanMolecularMass;
100 const auto scaleHeight = specificGasConstant * Qinside[Shortcuts::T] / GRAVITY;
101 const auto exponentialDistribution = tarch::la::pow(tarch::la::E, -dy / scaleHeight);
102
103 const auto densityOutside = Qinside[Shortcuts::rho] * exponentialDistribution;
104 const auto pressureOutside = pressure * exponentialDistribution;
105
106 Qoutside[Shortcuts::rho] = densityOutside;
107 const auto u = Qinside[Shortcuts::rhou] / Qinside[Shortcuts::rho];
108 const auto v = Qinside[Shortcuts::rhov] / Qinside[Shortcuts::rho];
109 #if DIMENSIONS == 3
110 const auto w = Qinside[Shortcuts::rhow] / Qinside[Shortcuts::rho];
111 const auto vSq = u * u + v * v + w * w;
112 #else
113 const auto vSq = u * u + v * v;
114 #endif
115
116 Qoutside[Shortcuts::E] = pressureOutside / (gamma - 1) + 0.5 * densityOutside * vSq;
117
118 if (normal == 1) {
119 // Reflective at bottom to keep atmosphere
120 // from escaping the domain through the Earth's surface
121 Qoutside[Shortcuts::rhou] = u * densityOutside;
122 Qoutside[Shortcuts::rhov] = -v * densityOutside;
123 #if DIMENSIONS == 3
124 Qoutside[Shortcuts::rhow] = w * densityOutside;
125 #endif
126 } else {
127 // Outflow at top
128 Qoutside[Shortcuts::rhou] = u * densityOutside;
129 Qoutside[Shortcuts::rhov] = v * densityOutside;
130 #if DIMENSIONS == 3
131 Qoutside[Shortcuts::rhow] = w * densityOutside;
132 #endif
133 }
134
135 for (int auxiliaryVariable = 0; auxiliaryVariable < NumberOfAuxiliaryVariables; ++auxiliaryVariable) {
136 Qoutside[NumberOfUnknowns + auxiliaryVariable] = Qinside[NumberOfUnknowns + auxiliaryVariable];
137 }
138
139 Qoutside[Shortcuts::backgroundDensity] = densityOutside;
140 Qoutside[Shortcuts::backgroundPressure] = pressureOutside;
141 }"""
142
143 max_h = 1.1 * min(size) / (3.0**args.min_depth)
144 min_h = max_h * 3.0 ** (-args.amr_levels)
145
146 atmosphere_unknowns = {
147 "rho": 1,
148 "rhou": 1,
149 "rhov": 1,
150 "E": 1,
151 }
152
153 if args.dimensions == 3:
154 atmosphere_unknowns["rhow"] = 1
155
156 atmosphere_auxiliary_variables = {
157 "T": 1,
158 "O": 1,
159 "O2": 1,
160 "N2": 1,
161 "backgroundDensity": 1,
162 "backgroundPressure": 1,
163 }
164
165 atmosphere_solver = exahype2.solvers.fv.godunov.GlobalAdaptiveTimeStep(
166 name="FVSolver",
167 patch_size=args.degrees_of_freedom,
168 unknowns=atmosphere_unknowns,
169 auxiliary_variables=atmosphere_auxiliary_variables,
170 min_volume_h=min_h,
171 max_volume_h=max_h,
172 time_step_relaxation=0.5,
173 )
174
175 atmosphere_solver.set_implementation(
176 initial_conditions=atmosphere_initial_conditions,
177 boundary_conditions=atmosphere_boundary_conditions,
178 flux=get_flux(),
179 riemann_solver=fWave,
180 max_eigenvalue=get_max_eigenvalue(),
181 )
182
183 atmosphere_solver.add_user_solver_includes('#include "tarch/reader/CSVReader.h"\n')
184
185 atmosphere_solver.set_implementation(
186 source_term="""for(int unknown = 0; unknown < NumberOfUnknowns; ++unknown) {
187 S[unknown] = 0.0;
188 }
189
190 applications::exahype2::GravityWaves::Thunderstorm::ForcingTerms::scenario(Q, x, h, t, S);"""
191 )
192
193 atmosphere_solver.add_user_solver_includes('#include "ForcingTerms.h"')
194 atmosphere_solver.add_solver_constants(GravityWaves.CSV_CONSTANTS)
195
196
199
200 diffusion_unknowns = {
201 "u": 1,
202 "v": 1,
203 }
204
205 if args.dimensions == 3:
206 diffusion_unknowns["w"] = 1
207
208 diffusion_unknowns["T"] = 1
209
210 diffusion_auxiliary_variables = {
211 "nu": 1,
212 "kappa": 1,
213 "rho": 1,
214 "E": 1,
215 "O": 1,
216 "O2": 1,
217 "N2": 1,
218 "backgroundU": 1,
219 "backgroundV": 1,
220 }
221
222 if args.dimensions == 3:
223 diffusion_auxiliary_variables["backgroundW"] = 1
224 diffusion_auxiliary_variables["backgroundT"] = 1
225
226 diffusion_initial_conditions = (
228 csv_file_path=args.csv_file_path,
229 initialize_density=True,
230 initialize_hydrogen=False,
231 )
232 + """
233 // "Doppler ducting of short-period gravity waves by midlatitude tidal wind structure", Snively et al., 2007
234 // exponential function between (110km, 100 m^2/s) and (150km, 5000 m^2/s)
235 // 5000 m^2/s implied in Thermosphere Ducted Gravity Waves but fully stated in Doppler Ducting
236 auto viscosityFunction = [](const double altitude) {
237 if(tarch::la::smaller(altitude, 110'000.0)) {
238 return 100.0;
239 }
240
241 return 0.00212732 * tarch::la::pow(tarch::la::E, 0.0000978006 * altitude);
242 };
243
244 Q[Shortcuts::u] = HORIZONTAL_WINDSPEED;
245 Q[Shortcuts::backgroundU] = Q[Shortcuts::u];
246
247 Q[Shortcuts::v] = 0.0;
248 Q[Shortcuts::backgroundV] = 0.0;
249
250 #if DIMENSIONS == 3
251 Q[Shortcuts::w] = 0.0;
252 Q[Shortcuts::backgroundW] = 0.0;
253 #endif
254
255 Q[Shortcuts::nu] = viscosityFunction(x(1));
256 Q[Shortcuts::kappa] = 1.5 * Q[Shortcuts::nu];
257
258 const auto M = Q[Shortcuts::O2] + Q[Shortcuts::O] + Q[Shortcuts::N2];
259 const auto iM = 1.0 / M;
260 const auto meanMolecularMass = (MOLAR_MASS_NITROGEN * Q[Shortcuts::N2] + MOLAR_MASS_MOLECULAR_OXYGEN * Q[Shortcuts::O2] + MOLAR_MASS_ATOMIC_OXYGEN * Q[Shortcuts::O]) * iM; // in kg/mol
261 const auto pressure = Q[Shortcuts::rho] * UNIVERSAL_GAS_CONSTANT * Q[Shortcuts::T] / meanMolecularMass;
262 const auto gamma = (1.4 * (M - Q[Shortcuts::O]) + 1.67 * Q[Shortcuts::O]) * iM;
263
264 Q[Shortcuts::E] = pressure / (gamma - 1) + 0.5 * Q[Shortcuts::rho] * (HORIZONTAL_WINDSPEED * HORIZONTAL_WINDSPEED);
265 Q[Shortcuts::backgroundT] = Q[Shortcuts::T];"""
266 )
267
268 diffusion_boundary_conditions = """
269 // normal == 0 => left
270 // normal == 1 => bottom
271 // normal == 2 => right
272 // normal == 3 => top
273
274 for (int unknown = 0; unknown < NumberOfUnknowns + NumberOfAuxiliaryVariables; ++unknown) {
275 Qoutside0[unknown] = Qinside[unknown];
276 }
277
278 if (normal == 1) {
279 // Reflective at bottom to keep atmosphere
280 // from escaping the domain through the Earth's surface
281 Qoutside0[Shortcuts::u] = Qinside[Shortcuts::u];
282 Qoutside0[Shortcuts::v] = -Qinside[Shortcuts::v];
283 #if DIMENSIONS == 3
284 Qoutside0[Shortcuts::w] = Qinside[Shortcuts::w];
285 #endif
286 }"""
287
288 diffusion_solver = exahype2.solvers.fd.GlobalAdaptiveTimeStep(
289 name="FDSolver",
290 patch_size=args.degrees_of_freedom,
291 unknowns=diffusion_unknowns,
292 auxiliary_variables=diffusion_auxiliary_variables,
293 min_h=min_h,
294 max_h=max_h,
295 time_step_relaxation=0.5,
296 order=1,
297 solvers_before_in_coupling=[atmosphere_solver],
298 )
299
300 diffusion_solver.set_implementation(
301 initial_conditions=diffusion_initial_conditions,
302 boundary_conditions=diffusion_boundary_conditions,
303 max_eigenvalue="return 1.0;",
304 solver=stencil,
305 )
306
307 diffusion_solver.add_user_solver_includes('#include "tarch/reader/CSVReader.h"\n')
308 diffusion_solver.add_solver_constants(GravityWaves.CSV_CONSTANTS)
309
310
313
315 atmospheric_solver=atmosphere_solver,
316 diffusion_solver=diffusion_solver,
317 chemical_solver=None,
318 size=size[: args.dimensions],
319 arguments=args,
320 )
321
322 project.output.makefile.add_cpp_file("ForcingTerms.cpph")
323
324 project.output.makefile.add_CXX_flag(f"""-DDENSITY_THRESHOLD=1E-9""")
325 project.output.makefile.add_CXX_flag(f"""-DPRESSURE_THRESHOLD=1E-9""")
326 project.output.makefile.add_CXX_flag(f"""-DTEMPERATURE_THRESHOLD=1E-9""")
327 project.output.makefile.add_CXX_flag(f"""-DVELOCITY_THRESHOLD=1E-9""")
328 project.output.makefile.add_CXX_flag(f"""-DHORIZONTAL_WINDSPEED=0.0""")
329
330 project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)
get_CSV_initializer(csv_file_path, bool initialize_density, bool initialize_hydrogen)
create_peano_project(atmospheric_solver, diffusion_solver, chemical_solver, size, arguments)