Peano
Loading...
Searching...
No Matches
navier_stokes.py
Go to the documentation of this file.
1# This file is part of the ExaHyPE2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3
4from .equation import Equation
5
6from exahype2.solvers.PDETerms import PDETerms
7
8
11 self,
12 dimensions,
13 use_advection=True,
14 use_background_state=True,
15 use_gravity=True,
16 use_viscosity=True,
17 gamma=1.4,
18 cv=1.0,
19 gas_constant=287.058,
20 reference_viscosity=0.1,
21 Pr=0.71,
22 molecular_diffusion_coeff=0.0,
23 q0=0.0,
24 ):
25 self.dimensionsdimensions = dimensions
26
28 "rho": 1,
29 "j": dimensions,
30 "E": 1,
31 }
32 if use_advection:
33 self.num_unknownsnum_unknowns["Z"] = 1
34
36 key: value
37 for key, value in {
38 "height": 1 if use_gravity else 0,
39 "backgroundstate": 2 if use_background_state else 0,
40 }.items()
41 if value > 0
42 }
43
44 self._use_advection = use_advection
45 self._use_background_state = use_background_state
46 self._use_gravity = use_gravity
47 self._use_viscosity = use_viscosity
48
49 self._gamma = gamma # ratio of specific heats
50 self._cv = cv # heat capacity at constant volume
51 self._gas_constant = gas_constant # specific gas constant of a fluid
52 self._reference_viscosity = reference_viscosity
53 self._Pr = Pr # Prandtl number
54 self._molecular_diffusion_coeff = molecular_diffusion_coeff
55 self._q0 = q0 # q0 not explained in the old version, merely marked with ???
56
58
60 return (
61 """
62 auto chemicalEnergy = q0 * Z;
63 """
64 + (
65 """
66 // add gravity Potential to the chemical energy for simplicity,
67 // but it is technically a separate term
68 chemicalEnergy += rho * gravity * height;
69"""
70 if self._use_gravity
71 else ""
72 )
73 + """
74
75 const auto jDot = j[0]*j[0]+j[1]*j[1]
76#if DIMENSIONS==3
77 + j[2]*j[2]
78#endif
79 ;
80
81 const auto pressure = (gamma-1) * (E - 0.5 * (1.0/rho) * jDot - chemicalEnergy);
82"""
83 )
84
85 def evaluate_energy(self):
86 return (
87 """
88 auto chemicalEnergy = q0 * Z;
89 """
90 + (
91 """
92 // add gravity Potential to the chemical energy for simplicity,
93 // but it is technically a separate term
94 chemicalEnergy += rho * gravity * height;
95"""
96 if self._use_gravity
97 else ""
98 )
99 + """
100
101 const auto jDot = j[0]*j[0]+j[1]*j[1]
102#if DIMENSIONS==3
103 + j[2]*j[2]
104#endif
105 ;
106 const auto E = pressure/(gamma - 1) + 0.5 * (jDot/rho) + chemicalEnergy;
107"""
108 )
109
110 # these get used both here and in the Riemann solver, but need
111 # different factors in their sum (i.e lambda + a * lambda_diff)
112 # in an effort to minimize code duplication we therefore define
113 # both of these once and just refer to them
115 return (
116 """
117 constexpr auto gamma = """
118 + str(self._gamma)
119 + """;
120 constexpr auto gravity = 9.81;
121 constexpr auto q0 = """
122 + str(self._q0)
123 + """;
124 constexpr auto referenceViscosity = """
125 + str(self._reference_viscosity)
126 + """;
127 constexpr auto Pr = """
128 + str(self._Pr)
129 + """; //Prandtl number
130 constexpr auto molecularDiffusionCoeff = """
132 + """;
133
134 auto maxEval = [&](
135 const {{SOLUTION_STORAGE_PRECISION}}* const NOALIAS Q,
136 const int normal
137 ){
138 const auto j = &Q[Shortcuts::j];
139 const auto E = Q[Shortcuts::E];
140 const auto rho = Q[Shortcuts::rho];
141 const auto height = """
142 + ("Q[Shortcuts::height]" if self._use_gravity else "0.0")
143 + """;
144 const auto Z = """
145 + ("Q[Shortcuts::Z];" if self._use_advection else "0.0;")
146 + self.evaluate_pressure()
147 + """
148 const auto u_n = Q[Shortcuts::j+normal]/rho;
149 const auto c = std::sqrt(gamma * (pressure/rho));
150 double lambda = std::fmax(
151 std::abs(u_n-c), std::abs(u_n+c)
152 );
153 return lambda;
154 };
155
156 auto diffusiveEval = [&](
157 const {{SOLUTION_STORAGE_PRECISION}}* const NOALIAS Q,
158 const int normal
159 ){
160 double smaxDiffusive = molecularDiffusionCoeff;
161 smaxDiffusive = std::fmax(
162 smaxDiffusive, (4./3.) * (referenceViscosity/Q[Shortcuts::rho])
163 );
164 return std::fmax(
165 smaxDiffusive, (gamma * referenceViscosity) / (Pr * Q[Shortcuts::rho])
166 );
167
168 };
169"""
170 )
171
172 def eigenvalues(self):
173 return (
175 + """
176 return maxEval(Q, normal)
177 + diffusiveEval(Q, normal)*2.0/(h[normal]*PNPM);
178"""
179 )
180
181 def flux(self):
182 # define parameters that will be used in the flux
183 flux_init = (
184 """ const auto rho = Q[Shortcuts::rho];
185 const auto j = &Q[Shortcuts::j];
186 const auto E = Q[Shortcuts::E];
187 const auto Z = """
188 + ("Q[Shortcuts::Z]" if self._use_advection else "0.0")
189 + """;
190 const auto height = """
191 + ("Q[Shortcuts::height]" if self._use_gravity else "0.0")
192 + """;
193 const auto backgroundPressure = """
194 + (
195 "Q[Shortcuts::backgroundstate+1]"
197 else "0.0"
198 )
199 + """;
200
201 constexpr auto gravity = 9.81;
202 constexpr auto gamma = """
203 + str(self._gamma)
204 + """;
205 constexpr auto gasConstant = """
206 + str(self._gas_constant)
207 + """;
208 constexpr auto cv = """
209 + str(self._cv)
210 + """;
211 constexpr auto q0 = """
212 + str(self._q0)
213 + """;
214 const auto invRho = 1./rho; """
215 + self.evaluate_pressure()
216 )
217
218 # part of the flux which corresponds to the Euler equations
219 flux_euler = """
220 F[Shortcuts::rho] = j[normal];
221 F[Shortcuts::j+0] = invRho * j[normal] * j[0];
222 F[Shortcuts::j+1] = invRho * j[normal] * j[1];
223#if DIMENSIONS==3
224 F[Shortcuts::j+2] = invRho * j[normal] * j[2];
225#endif
226 F[Shortcuts::E] = invRho * j[normal] * (E+pressure);
227
228 F[Shortcuts::j+normal] += (pressure-backgroundPressure);
229"""
230 flux_viscous = (
231 """
232 // TODO Support different visc. models?
233 constexpr auto referenceViscosity = """
234 + str(self._reference_viscosity)
235 + """;
236 constexpr auto mu = referenceViscosity;
237 constexpr auto Pr = """
238 + str(self._Pr)
239 + """; //Prandtl number
240 constexpr auto kappa = 1./Pr * referenceViscosity * gamma * cv;
241
242 const auto uu = invRho * Q[Shortcuts::j];
243 const auto uux = invRho * (deltaQ[0][Shortcuts::j+0] - uu * deltaQ[0][Shortcuts::rho]);
244 const auto uuy = invRho * (deltaQ[1][Shortcuts::j+0] - uu * deltaQ[1][Shortcuts::rho]);
245
246 const auto vv = invRho * Q[Shortcuts::j+1];
247 const auto vvx = invRho * (deltaQ[0][Shortcuts::j+1] - vv * deltaQ[0][Shortcuts::rho]);
248 const auto vvy = invRho * (deltaQ[1][Shortcuts::j+1] - vv * deltaQ[1][Shortcuts::rho]);
249
250#if DIMENSIONS == 2
251 const auto divV23 = 2./3. * (uux + vvy);
252#else
253 const auto ww = invRho * Q[Shortcuts::j+2];
254 const auto uuz = invRho * (deltaQ[2][Shortcuts::j+0] - uu * deltaQ[2][Shortcuts::rho]);
255 const auto vvz = invRho * (deltaQ[2][Shortcuts::j+1] - vv * deltaQ[2][Shortcuts::rho]);
256 const auto wwx = invRho * (deltaQ[2][Shortcuts::j+2] - ww * deltaQ[0][Shortcuts::rho]);
257 const auto wwy = invRho * (deltaQ[2][Shortcuts::j+2] - ww * deltaQ[1][Shortcuts::rho]);
258 const auto wwz = invRho * (deltaQ[2][Shortcuts::j+2] - ww * deltaQ[2][Shortcuts::rho]);
259 const auto divV23 = 2./3. * (uux + vvy + wwz);
260#endif
261
262 double Fj[DIMENSIONS];
263 switch(normal){
264 case 0:
265 Fj[0] = mu * (2 * uux - divV23);
266 Fj[1] = mu * (uuy + vvx);
267#if DIMENSIONS == 3
268 Fj[2] = mu * (uuz + wwx);
269#endif
270 break;
271 case 1:
272 Fj[0] = mu * (uuy + vvx);
273 Fj[1] = mu * (2 * vvy - divV23);
274#if DIMENSIONS == 3
275 Fj[2] = mu * (vvz + wwy);
276 break;
277 case 2:
278 Fj[0] = mu * (uuz + wwx);
279 Fj[1] = mu * (vvz + wwy);
280 Fj[2] = mu * (2 * wwz - divV23);
281#endif
282 }
283
284 F[Shortcuts::j+0] -= Fj[0];
285 F[Shortcuts::j+1] -= Fj[1];
286 F[Shortcuts::E] -= Fj[0]*uu + Fj[1]*vv;
287#if DIMENSIONS == 3
288 F[Shortcuts::j+2] -= Fj[2];
289 F[Shortcuts::E] -= Fj[2] *ww;
290#endif
291"""
292 )
293 flux_heat = (
294 """
295 const auto invCv = 1/cv;
296 const auto invRho2 = invRho * invRho;
297 const auto invRho3 = invRho2 * invRho;
298
299#if DIMENSIONS == 2
300 const auto dTdW1 = -1 * Q[Shortcuts::E] * invRho2 +
301 invRho3 * (Q[Shortcuts::j]*Q[Shortcuts::j] + Q[Shortcuts::j+1] * Q[Shortcuts::j+1]);
302#else
303 const auto dTdW1 = -1 * Q[Shortcuts::E] * invRho2 +
304 invRho3 * (Q[Shortcuts::j]*Q[Shortcuts::j] + Q[Shortcuts::j+1] * Q[Shortcuts::j+1] + Q[Shortcuts::j+2] * Q[Shortcuts::j+2]);
305#endif
306 const auto dTdW2 = -1 * Q[Shortcuts::j] * invRho2;
307 const auto dTdW3 = -1 * Q[Shortcuts::j+1] * invRho2;
308#if DIMENSIONS == 3
309 const auto dTdW4 = -1 * Q[Shortcuts::j+2] * invRho2;
310#endif
311
312 auto T = invCv * (dTdW1 * deltaQ[normal][Shortcuts::rho] +
313 dTdW2 * deltaQ[normal][Shortcuts::j+0] +
314 dTdW3 * deltaQ[normal][Shortcuts::j+1] +
315#if DIMENSIONS == 3
316 dTdW4 * deltaQ[normal][Shortcuts::j+2] +
317#endif
318 invRho * deltaQ[normal][Shortcuts::E]);
319 """
320 + (
321 """
322 // Including the gravitational potential in the pressure
323 // implies that we need to change the derivative of the temperature as well,
324 // but only in z direction
325 // Gravity happens in y direction in 2D and in z direction in 3D
326 if(normal==DIMENSIONS-1){
327 T -= (gravity * (gamma - 1)) / gasConstant;
328 } """
329 if self._use_gravity
330 else ""
331 )
332 + """
333 // Heat flux
334 F[Shortcuts::E] -= kappa * T;
335"""
336 )
337
338 flux_advection = (
339 """
340 // Euler flux
341 F[Shortcuts::Z] = j[normal] * Z * invRho;
342 // Contribution to the Heat flux
343 const auto factor = invCv * q0 * invRho2;
344 T = -factor *
345 (Q[Shortcuts::rho] * deltaQ[normal][Shortcuts::Z] -
346 Q[Shortcuts::Z] * deltaQ[normal][Shortcuts::rho]);
347 F[Shortcuts::E] -= kappa * T;
348
349 // Molecular diffusion for Advection-Reaction-Diffusion part.
350 constexpr auto molecularDiffusionCoeff = """
352 + """;
353 F[Shortcuts::rho] -= molecularDiffusionCoeff * deltaQ[normal][Shortcuts::rho];
354 F[Shorcuts::Z] -= molecularDiffusionCoeff * deltaQ[normal][Shortcuts::Z];
355"""
356 )
357
358 return (
359 flux_init
360 + flux_euler
361 + (flux_viscous if self._use_viscosity else "")
362 + flux_heat
363 + (flux_advection if self._use_advection else "")
364 )
365
366 def source(self):
367 # If both terms are 0, no need for a source
368 if not (self._use_gravity or self._use_background_state):
369 return PDETerms.None_Implementation
370 return (
371 """
372 std::fill_n(S, NumberOfUnknowns, 0.0);
373
374 auto rhoPertubation = Q[Shortcuts::rho];
375"""
376 + (
377 """
378 auto backgroundRho = Q[Shortcuts::backgroundstate+0];
379 rhoPertubation -= backgroundRho;
380"""
382 else ""
383 )
384 + """
385 constexpr auto gravity = 9.81;
386 //vertical direction is either y or z
387 S[Shortcuts::j+DIMENSIONS-1] = -1 * rhoPertubation * gravity;
388"""
389 + ("" if self._use_gravity else "")
390 )
391
392 def riemann_solver(self):
393 return (
394 """
395 constexpr int strideQ = NumberOfUnknowns + NumberOfAuxiliaryVariables;
396 {{CORRECTOR_COMPUTATION_PRECISION}} QavL[strideQ] __attribute__((aligned(16))) = {0.0};
397 {{CORRECTOR_COMPUTATION_PRECISION}} QavR[strideQ] __attribute__((aligned(16))) = {0.0};
398
399#if DIMENSIONS==3
400 constexpr int faceSize = (Order+1)*(Order+1);
401#else
402 constexpr int faceSize = (Order+1);
403#endif
404
405 for (int xy = 0; xy < faceSize; xy++) {
406 for (int n = 0; n < strideQ; n++) {
407 QavL[n] += kernels::{{SOLVER_NAME}}::Quadrature<{{CORRECTOR_COMPUTATION_PRECISION}}>::weights2[xy] * QL[xy * strideQ + n];
408 QavR[n] += kernels::{{SOLVER_NAME}}::Quadrature<{{CORRECTOR_COMPUTATION_PRECISION}}>::weights2[xy] * QR[xy * strideQ + n];
409 }
410 }
411
412 """
413 + self.eigenvalue_functions()
414 + """
415
416 // Compute max eigenvalue over face
417 auto smax = std::fmax(
418 maxEval(&QavL[0], direction),
419 maxEval(&QavR[0], direction)
420 );
421
422 // Add viscous eigenvalue term to max eigenvalue
423 const auto factor = 2 * (2 * Order + 1) / (h[direction] * std::sqrt(0.5 * std::numbers::pi));
424 smax += factor*std::fmax(
425 diffusiveEval(&QavL[0], direction), diffusiveEval(&QavR[0], direction)
426 );
427
428 for (int xy = 0; xy < faceSize; xy++) {
429 for (int n = 0; n < NumberOfUnknowns; n++) {
430 FL[xy*NumberOfUnknowns+n] = 0.5 * (FL[xy * NumberOfUnknowns + n] + FR[xy * NumberOfUnknowns + n]
431 + smax * (QL[xy * strideQ + n] - QR[xy * strideQ + n]) );
432 }
433 }
434 std::copy_n(FL, faceSize*NumberOfUnknowns, FR);
435"""
436 )
__init__(self, dimensions, use_advection=True, use_background_state=True, use_gravity=True, use_viscosity=True, gamma=1.4, cv=1.0, gas_constant=287.058, reference_viscosity=0.1, Pr=0.71, molecular_diffusion_coeff=0.0, q0=0.0)