Peano
Loading...
Searching...
No Matches
AugmentedStateRiemannSolver.cpph
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
5 constexpr int NEWTON_ITER = 1;
6 constexpr double NEWTON_TOL = 1e-5;
7 constexpr double CONVERGENCE_TOL = 1e-5;
8 constexpr double ZERO_TOL = 1e-5;
9
12 DrySingleRarefaction, // 1st wave family: contact discontinuity; 2nd wave family: rarefaction.
13 SingleRarefactionDry, // 1st wave family: rarefaction; 2nd wave family: contact discontinuity.
14 ShockShock, // 1st wave family: shock; 2nd wave family: shock.
15 ShockRarefaction, // 1st wave family: shock; 2nd wave family: rarefaction.
16 RarefactionShock, // 1st wave family: rarefaction; 2nd wave family: shock.
17 RarefactionRarefaction // 1st wave family: rarefaction; 2nd wave family: rarefaction.
18 };
19
20 template <class T>
21 static inline GPU_CALLABLE_METHOD RiemannStructure
22 determineRiemannStructure(const T& hLeft, const T& hRight, const T& uLeft, const T& uRight) {
23 // Determine min and max values
24 const T hMin = std::fmin(hLeft, hRight);
25 const T hMax = std::fmax(hLeft, hRight);
26
27 const T uDiff = uRight - uLeft;
28
29 // Left state is dry, thus the Riemann-problem consists of only
30 // one rarefaction arising from the right characteristic field.
31 if (hLeft <= hThreshold) {
33 }
34 // Right state is dry, thus the Riemann-problem consists of only
35 // one rarefaction arising from the left characteristic field.
36 if (hRight <= hThreshold) {
38 }
39
40 const T phiMin = uDiff + 2.0 * (std::sqrt(g * hMin) - std::sqrt(g * hMax));
41 const T phiMax = uDiff + (hMax - hMin) * (std::sqrt(0.5 * g * (hMax + hMin) / (hMax * hMin)));
42
43 if (phiMin > 0.0) {
45 } else if (phiMax <= 0.0) {
46 return ShockShock;
47 } else {
48 if (hLeft > hRight) {
49 return RarefactionShock;
50 } else {
51 return ShockRarefaction;
52 }
53 }
54 }
55
56 // Solve the linear equation by utilising Cramers rule similar to the Clawpack implementation
57 template <class T>
58 static inline GPU_CALLABLE_METHOD void solveLinearEquation(const T matrix[3][3], const T b[3], T x[3]) {
59 T determinantOne = matrix[0][0] * (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1]);
60 T determinantTwo = matrix[0][1] * (matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0]);
61 T determinantThree = matrix[0][2] * (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]);
62 T determinant = determinantOne - determinantTwo + determinantThree;
63 T A[3][3];
64 for (int k = 0; k < 3; ++k) {
65 for (int mw = 0; mw < 3; ++mw) {
66 A[0][mw] = matrix[0][mw];
67 A[1][mw] = matrix[1][mw];
68 A[2][mw] = matrix[2][mw];
69 }
70 A[0][k] = b[0];
71 A[1][k] = b[1];
72 A[2][k] = b[2];
73 determinantOne = A[0][0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]);
74 determinantTwo = A[0][1] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]);
75 determinantThree = A[0][2] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]);
76 x[k] = (determinantOne - determinantTwo + determinantThree) / determinant;
77 }
78 }
79
80 // This function computes the estimation of the middle state height and speeds according
81 template <class T>
82 static inline GPU_CALLABLE_METHOD T computeMiddleState(
83 const T& hLeft,
84 const T& hRight,
85 const T& uLeft,
86 const T& uRight,
87 RiemannStructure& riemannStructure,
88 T* middleSpeeds
89 ) {
90 T uMiddle = 0;
91 T hMiddle = 0;
92 T hMin = std::min(hLeft, hRight);
93 T hMax = std::max(hLeft, hRight);
94 T uDiff = uRight - uLeft;
95
96 riemannStructure = determineRiemannStructure(hLeft, hRight, uLeft, uRight);
97
98 // Dry state Riemann-problem, containing only a single rarefaction
99 if (hLeft <= hThreshold or hRight <= hThreshold) {
100 hMiddle = 0;
101 uMiddle = 0;
102 middleSpeeds[0] = uRight + uLeft - 2.0 * std::sqrt(g * hRight) + 2.0 * std::sqrt(g * hLeft);
103 middleSpeeds[1] = uRight + uLeft - 2.0 * std::sqrt(g * hRight) + 2.0 * std::sqrt(g * hLeft);
104 return 0; // For dry state problems, the middle state height is zero
105 }
106
107 // const T phiMin = uDiff + 2.0 * (std::sqrt(g * hMin) - std::sqrt(g * hMax));
108 const T phiMax = uDiff + (hMax - hMin) * (std::sqrt(0.5 * g * (hMax + hMin) / (hMax * hMin)));
109
110 // Variables needed for the Newton iteration
111 T hZero = 0;
112 T gLeft = 0;
113 T gRight = 0;
114 T phiZero = 0;
115 T phiZeroDerivative = 0;
116 T slope = 0;
117
118 if (riemannStructure == RarefactionRarefaction) {
119 hMiddle = std::max(0.0, uLeft - uRight + 2.0 * (std::sqrt(g * hLeft) + std::sqrt(g * hRight)));
120 hMiddle = 1.0 / (16.0 * g) * hMiddle * hMiddle;
121 if (hMiddle > 0) {
122 uMiddle = uLeft + 2.0 * (std::sqrt(g * hLeft) - std::sqrt(g * hMiddle));
123 } else if (hMiddle < 0) {
124 uMiddle = -(uLeft + 2.0 * (std::sqrt(g * hLeft) - std::sqrt(g * hMiddle)));
125 }
126 middleSpeeds[0] = uLeft + 2.0 * std::sqrt(g * hLeft) - 3.0 * std::sqrt(g * hMiddle);
127 middleSpeeds[1] = uRight - 2.0 * std::sqrt(g * hRight) + 3.0 * std::sqrt(g * hMiddle);
128 return hMiddle;
129 } else if (riemannStructure == ShockShock) {
130 /*
131 * Compute All-Shock Riemann Solution
132 * \cite[ch. 13.7]{LeVeque2002}
133 *
134 * Compute middle state h_m
135 * => Solve non-linear scalar equation by Newton's method
136 * u_l - (h_m - h_l) \sqrt{\frac{g}{2} \left( \frac{1}{h_m} + \frac{1}{h_l} \right) }
137 * = u_r + (h_m - h_r) \sqrt{\frac{g}{2} \left( \frac{1}{h_m} + \frac{1}{h_r} \right) }
138 *
139 * Therefore determine the root of phi_{ss}:
140 *\begin{equation}
141 * \begin{matrix}
142 * \phi_{ss}(h) =&& u_r - u_l +
143 * (h-h_l) \sqrt{
144 * \frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_l} \right)
145 * } \\
146 * && +(h-h_r) \sqrt{
147 * \frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_r} \right)
148 * }
149 * \end{matrix}
150 *\end{equation}
151 *
152 *\begin{equation}
153 * \begin{matrix}
154 * \phi_{ss}'(h) = &&\sqrt{\frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_l} \right) }
155 * +\sqrt{\frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_r} \right) }\\
156 * && -\frac{g}{4}
157 * \frac{h-h_l}
158 * {
159 * h^2\sqrt{\frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_l} \right) }
160 * }-
161 * \frac{g}{4}
162 * \frac{h-h_r}
163 * {
164 * h^2\sqrt{\frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_r} \right) }
165 * }
166 * \end{matrix}
167 *\end{equation}
168 */
169 hZero = hMax;
170 for (int i = 0; i < NEWTON_ITER; ++i) {
171 gLeft = std::sqrt(0.5 * g * (1 / hZero + 1 / hLeft));
172 gRight = std::sqrt(0.5 * g * (1 / hZero + 1 / hRight));
173 phiZero = uDiff + (hZero - hLeft) * gLeft + (hZero - hRight) * gRight;
174 if (std::fabs(phiZero) < NEWTON_TOL) {
175 break;
176 }
177 phiZeroDerivative = gLeft - g * (hZero - hLeft) / (4.0 * (hZero * hZero) * gLeft) + gRight
178 - g * (hZero - hRight) / (4.0 * (hZero * hZero) * gRight);
179 slope = 2.0 * std::sqrt(hZero) * phiZeroDerivative;
180 hZero = std::pow((std::sqrt(hZero) - phiZero / slope), 2);
181 }
182 hMiddle = hZero;
183 T uOneMiddle = uLeft - (hMiddle - hLeft) * std::sqrt((0.5 * g) * (1 / hMiddle + 1 / hLeft));
184 T uTwoMiddle = uRight + (hMiddle - hRight) * std::sqrt((0.5 * g) * (1 / hMiddle + 1 / hRight));
185 uMiddle = 0.5 * (uOneMiddle + uTwoMiddle);
186 middleSpeeds[0] = uOneMiddle - std::sqrt(g * hMiddle);
187 middleSpeeds[1] = uTwoMiddle + std::sqrt(g * hMiddle);
188 return hMiddle;
189 } else if (riemannStructure == ShockRarefaction or riemannStructure == RarefactionShock) {
190 /*
191 * Compute middle state h_m
192 * => Solve non-linear scalar equation by Newton's method
193 *
194 * Therefore determine the root of phi_{sr}/phi_{rs}:
195 *\begin{equation}
196 * \begin{matrix}
197 * h_{min} = \min(h_l, h_r)\\
198 * h_{max} = \max(h_l, h_r)
199 * \end{matrix}
200 *\end{equation}
201 *\begin{equation}
202 * \begin{matrix}
203 * \phi_{sr}(h) = \phi_{rs}(h) =&& u_r - u_l + (h - h_{min}) \sqrt{
204 * \frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_{min}} \right)
205 * } \\
206 * && + 2 (\sqrt{gh} - \sqrt{gh_{max}})
207 * \end{matrix}
208 *\end{equation}
209 *\begin{equation}
210 * \phi'_{sr}(h) = \phi'_{rs}(h) = \sqrt{\frac{g}{2} \left( \frac{1}{h} + \frac{1}{h_{min}} \right) }
211 * -\frac{g}{4} \frac{h-h_{min}}
212 * { h^2
213 * \sqrt{
214 * \frac{g}{2} \left(\frac{1}{h} + \frac{1}{h_{min}} \right)
215 * }
216 * }
217 * + \sqrt{\frac{g}{h}}
218 *\end{equation}
219 *
220 * This implementation does not calculate the phi derivative here
221 */
222 hZero = hMin;
223 for (int i = 0; i < NEWTON_ITER; ++i) {
224 phiZero = uDiff + 2.0 * (std::sqrt(g * hZero) - std::sqrt(g * hMax))
225 + (hZero - hMin) * std::sqrt(0.5 * g * (1 / hZero + 1 / hMin));
226 slope = (phiMax - phiZero) / (hMax - hMin);
227 hZero = hZero - phiZero / slope;
228 }
229
230 hMiddle = hZero;
231
232 if (hLeft > hRight) {
233 uMiddle = uLeft + 2.0 * std::sqrt(g * hLeft) - 2.0 * std::sqrt(g * hMiddle);
234 middleSpeeds[0] = uLeft + 2.0 * std::sqrt(g * hLeft) - 3.0 * std::sqrt(g * hMiddle);
235 middleSpeeds[1] = uLeft + 2.0 * std::sqrt(g * hLeft) - std::sqrt(g * hMiddle);
236 } else {
237 middleSpeeds[1] = uRight - 2.0 * std::sqrt(g * hRight) + 3.0 * std::sqrt(g * hMiddle);
238 middleSpeeds[0] = uRight - 2.0 * std::sqrt(g * hRight) + std::sqrt(g * hMiddle);
239 uMiddle = uRight - 2.0 * std::sqrt(g * hRight) + 2.0 * std::sqrt(g * hMiddle);
240 }
241 return hMiddle;
242 }
243 return hMiddle;
244 }
245
246 template <class T>
247 static inline GPU_CALLABLE_METHOD WetDryState determineWetDryState(
248 T& hLeft,
249 T& huLeft,
250 T& hvLeft,
251 T& uLeft,
252 T& vLeft,
253 T& bLeft,
254 T& hRight,
255 T& huRight,
256 T& hvRight,
257 T& uRight,
258 T& vRight,
259 T& bRight,
260 T& hMiddle,
261 WetDryState& wetDryState,
262 RiemannStructure& riemannStructure,
263 T* middleSpeeds
264 ) {
265 // Test for simple wet/wet case since this is most probably the
266 // most frequently executed branch.
267 if (hLeft > hThreshold and hRight > hThreshold) {
268 wetDryState = WetDryState::WetWet;
269 }
270
271 // Check for the dry/dry-case
272 else if (hLeft <= hThreshold and hRight <= hThreshold) {
273 wetDryState = WetDryState::DryDry;
274 }
275
276 // We have a shoreline: one cell dry, one cell wet
277
278 // Check for simple inundation problems
279 // (=> dry cell lies lower than the wet cell)
280 else if (hLeft <= hThreshold and hRight + bRight > bLeft) { // Dry cell on the left, wet cell on the right, simple
281 // inundation
282 wetDryState = WetDryState::DryWetInundation;
283 } else if (hRight <= hThreshold and hLeft + bLeft > bRight) { // Dry cell on the right, wet cell on the left, simple
284 // inundation
285 wetDryState = WetDryState::WetDryInundation;
286 }
287
288 // Dry cell lies higher than the wet cell
289 else if (hLeft <= hThreshold and (hRight + bRight <= bLeft)) { // Left cell dry and hRight + bRight <= bLeft
290 // Lets check if the momentum is able to overcome the difference in height
291 // => solve homogeneous Riemann-problem to determine the middle state height
292 // which would arise if there is a wall (wall-boundary-condition)
293 // \cite[ch. 6.8.2]{George2006})
294 // \cite[ch. 5.2]{George2008}
295 hMiddle = computeMiddleState(hRight, hRight, -uRight, uRight, riemannStructure, middleSpeeds);
296 if (hMiddle + bRight > bLeft) {
297 // Momentum is large enough, continue with the original values
298 // bLeft = hMiddle + bRight;
300 } else {
301 // Momentum is not large enough, use wall-boundary-values
302 hLeft = hRight;
303 uLeft = -uRight;
304 huLeft = -huRight;
305 bLeft = bRight = 0.0;
306 vLeft = vRight;
307 wetDryState = WetDryState::DryWetWall;
308 }
309 } else if (hRight <= hThreshold and (hLeft + bLeft <= bRight)) {
310 // Lets check if the momentum is able to overcome the difference in height
311 // => solve homogeneous Riemann-problem to determine the middle state height
312 // which would arise if there is a wall (wall-boundary-condition)
313 // \cite[ch. 6.8.2]{George2006})
314 // \cite[ch. 5.2]{George2008}
315 hMiddle = computeMiddleState(hLeft, hLeft, uLeft, -uLeft, riemannStructure, middleSpeeds);
316
317 if (hMiddle + bLeft > bRight) {
318 // Momentum is large enough, continue with the original values
319 // bRight = hMiddle + bLeft;
321 } else {
322 // Momentum is not large enough, use wall-boundary-values
323 hRight = hLeft;
324 uRight = -uLeft;
325 huRight = -huLeft;
326 bRight = bLeft = 0.0;
327 vRight = vLeft;
328 wetDryState = WetDryState::WetDryWall;
329 }
330 }
331
332 // Limit the effect of the source term if there is a "wall"
333 //\cite[end of ch. 5.2]{George2008}
334 //\cite[rpn2ez_fast_geo.f][LeVeque2011]
335 if (wetDryState == WetDryState::DryWetWallInundation) {
336 bLeft = hRight + bRight;
337 } else if (wetDryState == WetDryState::WetDryWallInundation) {
338 bRight = hLeft + bLeft;
339 }
340 return wetDryState;
341 }
342
343 template <class T>
344 static inline GPU_CALLABLE_METHOD void computeWaveDecomposition(
345 T& hLeft,
346 T& huLeft,
347 T& uLeft,
348 T& vLeft,
349 T& bLeft,
350 T& hRight,
351 T& huRight,
352 T& uRight,
353 T& vRight,
354 T& bRight,
355 T& hMiddle,
356 const T& sqrtGhLeft,
357 const T& sqrtGhRight,
358 const T& sqrthLeft,
359 const T& sqrthRight,
360 const T& sqrtG,
361 WetDryState& wetDryState,
362 RiemannStructure& riemannStructure,
363 T fWaves[3][3],
364 T waveSpeeds[3],
365 T* middleSpeeds
366 ) {
367 // Compute eigenvalues of the jacobian matrices in states Q_{i-1} and Q_{i} (QLeft and QRight) (char. speeds)
368 T characteristicSpeeds[2];
369 characteristicSpeeds[0] = uLeft - sqrtGhLeft;
370 characteristicSpeeds[1] = uRight + sqrtGhRight;
371
372 // Compute "Roe speeds"
373 T hRoe = 0.5 * (hRight + hLeft);
374 T uRoe = uLeft * sqrthLeft + uRight * sqrthRight; // TODO: in Clawpack the g is included?
375 uRoe /= (sqrthLeft + sqrthRight);
376
377 T roeSpeeds[2];
378 // Optimisation for dumb compilers
379 T sqrtGhRoe = std::sqrt(g * hRoe);
380 roeSpeeds[0] = uRoe - sqrtGhRoe;
381 roeSpeeds[1] = uRoe + sqrtGhRoe;
382
383 // Compute the middle state of the homogeneous Riemann-problem
384 if (wetDryState != WetDryState::WetDryWall and wetDryState != WetDryState::DryWetWall) {
385 // Case WDW and DWW was computed in
386 // determineWetDryState already
387 hMiddle = computeMiddleState(hLeft, hRight, uLeft, uRight, riemannStructure, middleSpeeds);
388 }
389
390 // Compute extended Einfeldt speeds (Einfeldt speeds + middle state speeds)
391 // \cite[ch. 5.2]{George2008}, \cite[ch. 6.8]{George2006}
392
393 /*
394 * In the paper George2008, the values lambdaStarHStarMinus and Plus are computed a bit different,
395 * but in this implementation it is based on the approximated middle speeds computed by computeMiddleState
396 * like in the two reference implementations.
397 */
398 T lambdaStarHStarMinus = middleSpeeds[0];
399 T lambdaStarHStarPlus = middleSpeeds[1];
400
401 T extEinfeldtSpeeds[2] = {0, 0};
402 if (hLeft > hThreshold and hRight > hThreshold) {
403 extEinfeldtSpeeds[0] = std::min(characteristicSpeeds[0], roeSpeeds[0]);
404 extEinfeldtSpeeds[0] = std::min(extEinfeldtSpeeds[0], lambdaStarHStarPlus);
405 extEinfeldtSpeeds[1] = std::max(characteristicSpeeds[1], roeSpeeds[1]);
406 extEinfeldtSpeeds[1] = std::max(extEinfeldtSpeeds[1], lambdaStarHStarMinus);
407 } else if (hLeft <= hThreshold) { // Ignore undefined speeds
408 extEinfeldtSpeeds[0] = std::min(roeSpeeds[0], lambdaStarHStarPlus);
409 extEinfeldtSpeeds[1] = std::max(characteristicSpeeds[1], roeSpeeds[1]);
410 } else if (hRight <= hThreshold) { // Ignore undefined speeds
411 extEinfeldtSpeeds[0] = std::min(characteristicSpeeds[0], roeSpeeds[0]);
412 extEinfeldtSpeeds[1] = std::max(roeSpeeds[1], lambdaStarHStarMinus);
413 }
414
415
416 // HLL middle state
417 // \cite[theorem 3.1]{George2006}, \cite[ch. 4.1]{George2008}
418 T hLLMiddleHeight = huLeft - huRight + extEinfeldtSpeeds[1] * hRight - extEinfeldtSpeeds[0] * hLeft;
419 hLLMiddleHeight /= (extEinfeldtSpeeds[1] - extEinfeldtSpeeds[0]);
420 hLLMiddleHeight = std::max(hLLMiddleHeight, 0.0);
421
422 // Define eigenvalues
423 T eigenValues[3];
424 eigenValues[0] = extEinfeldtSpeeds[0];
425 // eigenValues[1] --> corrector wave
426 eigenValues[2] = extEinfeldtSpeeds[1];
427
428 // Define eigenvectors
429 T eigenVectors[3][3];
430
431 // Set first and third eigenvector, based on the characteristic fields of the 2D-SWE
432 eigenVectors[0][0] = 1;
433 eigenVectors[0][2] = 1;
434
435 eigenVectors[1][0] = eigenValues[0];
436 eigenVectors[1][2] = eigenValues[2];
437
438 eigenVectors[2][0] = eigenValues[0] * eigenValues[0];
439 eigenVectors[2][2] = eigenValues[2] * eigenValues[2];
440
441 // Compute rarefaction corrector wave
442 // \cite[ch. 6.7.2]{George2006}, \cite[ch. 5.1]{George2008}
443 bool strongRarefaction = false;
444 // If the option CorrectRarefactions is enabled, the more complex rarefaction corrector wave will be utilised if
445 // some conditions are met (the 2nd wave from the paper)
446 constexpr bool CorrectRarefactions = true;
447 if constexpr (CorrectRarefactions) {
448 if ((riemannStructure == ShockRarefaction or riemannStructure == RarefactionShock
449 or riemannStructure == RarefactionRarefaction)
450 and hMiddle > hThreshold) { // Limit to ensure non-negative depth
451 // TODO: GeoClaw, riemann_aug_JCP: No explicit boundaries for "strong rarefaction" in literature?
452 T rareBarrier[2] = {0.5, 0.9};
453
454 // Lower rare barrier in the case of a transsonic rarefaction
455 if ((riemannStructure == RarefactionShock or riemannStructure == RarefactionRarefaction)
456 and eigenValues[0] * lambdaStarHStarMinus < 0.0) { // Transsonic rarefaction, first wave family
457 rareBarrier[0] = 0.2;
458 } else if ((riemannStructure == ShockRarefaction or riemannStructure == RarefactionRarefaction)
459 and eigenValues[2] * lambdaStarHStarPlus < 0.0) { // Transsonic rarefaction, second wave family
460 rareBarrier[0] = 0.2;
461 }
462
463 T sqrtGhMiddle = std::sqrt(g * hMiddle);
464 // Determine the max. rarefaction size (distance between head and tail)
465 T rareFactionSize[2];
466 rareFactionSize[0] = 3.0 * (sqrtGhLeft - sqrtGhMiddle);
467 rareFactionSize[1] = 3.0 * (sqrtGhRight - sqrtGhMiddle);
468
469 T maxRareFactionSize = std::max(rareFactionSize[0], rareFactionSize[1]);
470
471 // Set the eigenvalue of the corrector wave in the case of a "strong rarefaction"
472 if (maxRareFactionSize > rareBarrier[0] * (eigenValues[2] - eigenValues[0])
473 and maxRareFactionSize < rareBarrier[1] * (eigenValues[2] - eigenValues[0])) {
474 strongRarefaction = true;
475 if (rareFactionSize[0] > rareFactionSize[1]) {
476 eigenValues[1] = lambdaStarHStarMinus;
477 } else if (rareFactionSize[1] > rareFactionSize[0]) {
478 eigenValues[1] = lambdaStarHStarPlus;
479 } else {
480 eigenValues[1] = 0.5 * (lambdaStarHStarMinus + lambdaStarHStarPlus);
481 }
482 }
483 }
484 // Additional condition from Clawpack
485 if (hLLMiddleHeight < (std::min(hLeft, hRight) / 5.0)) {
486 strongRarefaction = false;
487 }
488 }
489
490 // Set 2nd eigenvector
491 if (not strongRarefaction) { // Simple form as there is no strong rarefaction
492 eigenValues[1] = 0.5 * (eigenValues[0] + eigenValues[2]);
493 eigenVectors[0][1] = 0.0;
494 eigenVectors[1][1] = 0.0;
495 eigenVectors[2][1] = 1.0;
496 } else { // The more complex modified form depending on the middle speed as there is a strong rarefaction
497 eigenVectors[0][1] = 1.0;
498 eigenVectors[1][1] = eigenValues[1];
499 eigenVectors[2][1] = eigenValues[1] * eigenValues[1];
500 }
501
502 // Compute the jump in state, so the delta vector needed for the wave decomposition approach
503 T rightHandSide[3];
504 rightHandSide[0] = hRight - hLeft; // Jump in height
505 rightHandSide[1] = huRight - huLeft; // Jump in momentum
506 rightHandSide[2] = (huRight * uRight + 0.5 * g * hRight * hRight) // Jump in momentum flux
507 - (huLeft * uLeft + 0.5 * g * hLeft * hLeft);
508
509 // Compute steady state wave, 0-th wave
510 // \cite[ch. 4.2.1 \& app. A]{George2008}, \cite[ch. 6.2 \& ch. 4.4]{George2006}
511 T steadyStateWave[2] = {0, 0};
512
513 T hBar = (hLeft + hRight) * 0.5;
514 // The coefficients needed for the decomposition, in the paper they are called alpha
515 T beta[3];
516
517 T eigenvalueAvgBar = 0;
518 T eigenvalueAvgTilde = 0;
519 T hAvgTilde = 0;
520
521 /*
522 * In the following case distinction, the SteadyStateWave branch tries to implement the steady state
523 * wave accounting for the source term in a manner similar to the paper, whereas SteadyStateIter takes a bit
524 * different approach like in the Clawpack implementation.
525 *
526 * It seems that SteadyStateIter with one iteration (NEWTON_ITER) works the best (multiple don't work as well)
527 * as it seems to be also a bit more stable than with SteadyStateWave.
528 */
529 constexpr bool SteadyStateWave = false;
530 if constexpr (SteadyStateWave) {
531 if (bLeft != bRight) { // There is only a source for different bathymetry values
532 eigenvalueAvgBar = (uLeft + uRight) * (uLeft + uRight) * 0.25 - g * hBar;
533 eigenvalueAvgTilde = std::max(0.0, uRight * uLeft) - g * hBar;
534
535 hAvgTilde = hBar * (eigenvalueAvgTilde / eigenvalueAvgBar);
536
537 hAvgTilde = std::max(hAvgTilde, std::min(hRight, hLeft));
538 hAvgTilde = std::min(hAvgTilde, std::max(hRight, hLeft));
539
540 steadyStateWave[0] = (g * hBar) / eigenvalueAvgBar;
541 steadyStateWave[1] = -g * hAvgTilde;
542
543 // Some limitations implemented according to either the paper George2008 or the Clawpack implementation
544 if (eigenValues[0] < 0.0 and eigenValues[2] > 0.0) { // Subsonic flow
545 if (bRight > bLeft) {
546 steadyStateWave[0] = std::max(
547 steadyStateWave[0],
548 (hLLMiddleHeight / (bRight - bLeft)) * ((eigenValues[2] - eigenValues[0]) / eigenValues[0])
549 );
550 } else if (bRight < bLeft) {
551 steadyStateWave[0] = std::max(
552 steadyStateWave[0],
553 (hLLMiddleHeight / (bRight - bLeft)) * ((eigenValues[2] - eigenValues[0]) / eigenValues[2])
554 );
555 }
556 steadyStateWave[0] = std::min(steadyStateWave[0], -1.0);
557 // TODO: Modified accordingly from Clawpack, check the following for correctness
558 // as the Clawpack version does not use the coefficient (bRight - bLeft)
559 // for the rightHandSide subtractions and thus the limits have been
560 // modified.
561 } else if ((std::fabs(eigenvalueAvgBar) <= ZERO_TOL) or (eigenvalueAvgBar * eigenvalueAvgTilde <= ZERO_TOL)
562 or (eigenvalueAvgBar * eigenValues[0] * eigenValues[2] <= ZERO_TOL)
563 or (std::min(std::fabs(eigenValues[0]), std::fabs(eigenValues[2])) < ZERO_TOL)
564 or (eigenValues[0] < 0.0 and lambdaStarHStarMinus > 0.0)
565 or (eigenValues[2] > 0.0 and lambdaStarHStarPlus < 0.0)
566 or ((uLeft + sqrt(g * hLeft)) * (uRight + sqrt(g * hRight)) < 0.0)
567 or ((uLeft - sqrt(g * hLeft)) * (uRight - sqrt(g * hRight)) < 0.0)) { // Flow near a sonic state
568 steadyStateWave[0] = 0;
569 } else if (eigenValues[0] > 0.0 and eigenValues[2] > 0.0) { // Supersonic leftwards flow
570 steadyStateWave[0] = std::min(
571 steadyStateWave[0],
572 (hLLMiddleHeight / (bRight - bLeft)) * ((eigenValues[2] - eigenValues[0]) / eigenValues[0])
573 );
574 steadyStateWave[0] = std::max(steadyStateWave[0], (-hLeft / (bRight - bLeft)));
575 } else if (eigenValues[0] < 0.0 and eigenValues[2] < 0.0) { // Supersonic rightwards flow
576 steadyStateWave[0] = std::min(steadyStateWave[0], (hRight / (bRight - bLeft)));
577 steadyStateWave[0] = std::max(
578 steadyStateWave[0],
579 (hLLMiddleHeight / (bRight - bLeft)) * ((eigenValues[2] - eigenValues[0]) / eigenValues[2])
580 );
581 }
582 } else {
583 steadyStateWave[0] = steadyStateWave[1] = 0.0; // No source term for constant bathymetry
584 }
585 // Subtract the wave from the state delta as described in George2008, here the coefficient
586 // (bRight - bLeft) is different than in the steadyStateIter case.
587 rightHandSide[0] -= (bRight - bLeft) * steadyStateWave[0];
588 // rightHandSide[1]: No source term
589 rightHandSide[2] -= (bRight - bLeft) * steadyStateWave[1];
590
591 // Solve the linear equation system to deduce the beta coefficients necessary for the computation of the flux
592 // waves
593 solveLinearEquation(eigenVectors, rightHandSide, beta);
594 }
595
596 steadyStateWave[0] = -(bRight - bLeft);
597 steadyStateWave[1] = -g * 0.5 * (hRight + hLeft) * (bRight - bLeft);
598
599 // Initialise the following variables for the iteration, implementation similar to Clawpack.
600 T hLeftIter = hLeft;
601 T hRightIter = hRight;
602 T uLeftIter = uLeft;
603 T uRightIter = uRight;
604 T huLeftIter = uLeftIter * hLeftIter;
605 T huRightIter = uRightIter * hRightIter;
606 T deltaNorm = rightHandSide[0] * rightHandSide[0] + rightHandSide[2] * rightHandSide[2];
607
608 bool sonicFlow = false;
609 constexpr bool SteadyStateIter = true;
610 if constexpr (SteadyStateIter) {
611 for (int i = 0; i < NEWTON_ITER; ++i) {
612 if (std::min(hLeftIter, hRightIter) < hThreshold and strongRarefaction) {
613 strongRarefaction = false;
614 hLeftIter = hLeft;
615 hRightIter = hRight;
616 uLeftIter = uLeft;
617 uRightIter = uRight;
618 huLeftIter = uLeftIter * hLeftIter;
619 huRightIter = uRightIter * hRightIter;
620 eigenValues[1] = 0.5 * (eigenValues[0] + eigenValues[2]);
621 eigenVectors[0][1] = 0.0;
622 eigenVectors[1][1] = 0.0;
623 eigenVectors[2][1] = 1.0;
624 }
625 // Compute some average quantities
626 hBar = std::max(0.0, 0.5 * (hLeftIter + hRightIter));
627 eigenvalueAvgBar = (uLeftIter + uRightIter) * (uLeftIter + uRightIter) * 0.25 - g * hBar; // std::pow(((uLeft +
628 // uRight) / 2), 2)
629 eigenvalueAvgTilde = std::max(0.0, uRightIter * uLeftIter) - g * hBar;
630
631 // Determine whether there is a sonic flow type
632 if ((std::fabs(eigenvalueAvgBar) <= ZERO_TOL) or (eigenvalueAvgBar * eigenvalueAvgTilde <= ZERO_TOL)
633 or (eigenvalueAvgBar * eigenValues[0] * eigenValues[2] <= ZERO_TOL)
634 or (std::min(std::fabs(eigenValues[0]), std::fabs(eigenValues[2])) < ZERO_TOL)
635 or (eigenValues[0] < 0.0 and lambdaStarHStarMinus > 0.0)
636 or (eigenValues[2] > 0.0 and lambdaStarHStarPlus < 0.0)
637 or ((uLeft + sqrt(g * hLeft)) * (uRight + sqrt(g * hRight)) < 0.0)
638 or ((uLeft - sqrt(g * hLeft)) * (uRight - sqrt(g * hRight)) < 0.0)) {
639 sonicFlow = true;
640 }
641
642 // Again several limitations for the wave according to George2008 and the Clawpack implementation to improve
643 // stability.
644 if (sonicFlow) {
645 steadyStateWave[0] = -(bRight - bLeft);
646 } else {
647 steadyStateWave[0] = (bRight - bLeft) * g * hBar / eigenvalueAvgBar;
648 }
649
650 if (eigenValues[0] < -ZERO_TOL and eigenValues[2] > ZERO_TOL) { // Subsonic
651 steadyStateWave[0] = std::min(
652 steadyStateWave[0],
653 hLLMiddleHeight * (eigenValues[2] - eigenValues[0]) / eigenValues[2]
654 );
655 steadyStateWave[0] = std::max(
656 steadyStateWave[0],
657 hLLMiddleHeight * (eigenValues[2] - eigenValues[0]) / eigenValues[0]
658 );
659 } else if (eigenValues[0] >= ZERO_TOL) { // Supersonic right
660 steadyStateWave[0] = std::min(
661 steadyStateWave[0],
662 hLLMiddleHeight * (eigenValues[2] - eigenValues[0]) / eigenValues[0]
663 );
664 steadyStateWave[0] = std::max(steadyStateWave[0], -hLeft);
665 } else if (eigenValues[2] <= -ZERO_TOL) { // Supersonic left
666 steadyStateWave[0] = std::min(steadyStateWave[0], hRight);
667 steadyStateWave[0] = std::max(
668 steadyStateWave[0],
669 hLLMiddleHeight * (eigenValues[2] - eigenValues[0]) / eigenValues[2]
670 );
671 }
672 if (sonicFlow) {
673 steadyStateWave[1] = -g * hBar * (bRight - bLeft);
674 } else {
675 steadyStateWave[1] = -(bRight - bLeft) * g * hBar * eigenvalueAvgTilde / eigenvalueAvgBar;
676 }
677
678 steadyStateWave[1] = std::min(
679 steadyStateWave[1],
680 g * std::max(-hLeftIter * (bRight - bLeft), -hRightIter * (bRight - bLeft))
681 );
682 steadyStateWave[1] = std::max(
683 steadyStateWave[1],
684 g * std::min(-hLeftIter * (bRight - bLeft), -hRightIter * (bRight - bLeft))
685 );
686
687 // subtract the wave from the state delta as described in George2008
688 rightHandSide[0] -= steadyStateWave[0];
689 // rightHandSide[1]: No source term
690 rightHandSide[2] -= steadyStateWave[1];
691
692 // Solve the linear equation system to deduce the beta coefficients necessary for the computation of the flux
693 // waves
694 solveLinearEquation(eigenVectors, rightHandSide, beta);
695
696 if (std::fabs(rightHandSide[0] * rightHandSide[0] + rightHandSide[2] * rightHandSide[2] - deltaNorm)
697 < CONVERGENCE_TOL) {
698 break;
699 }
700
701 deltaNorm = rightHandSide[0] * rightHandSide[0] + rightHandSide[2] * rightHandSide[2];
702
703 hLeftIter = hLeft;
704 hRightIter = hRight;
705 uLeftIter = uLeft;
706 uRightIter = uRight;
707 huLeftIter = uLeftIter * hLeftIter;
708 huRightIter = uRightIter * hRightIter;
709
710 // TODO: This is again from Clawpack, but as using only one iteration works it is to be evaluated whether
711 // the following code works in the correct way.
712 for (int mw = 0; mw < 3; ++mw) {
713 if (eigenValues[mw] < 0.0) {
714 hLeftIter = hLeftIter + beta[mw] * eigenVectors[0][mw];
715 huLeftIter = huLeftIter + beta[mw] * eigenVectors[1][mw];
716 }
717 }
718 for (int mw = 2; mw > 0; --mw) {
719 if (eigenValues[mw] > 0.0) {
720 hRightIter = hRightIter - beta[mw] * eigenVectors[0][mw];
721 huRightIter = huRightIter - beta[mw] * eigenVectors[1][mw];
722 }
723 }
724
725 if (hLeftIter > hThreshold) {
726 uLeftIter = huLeftIter / hLeftIter;
727 } else {
728 hLeftIter = std::max(hLeftIter, 0.0);
729 uLeftIter = 0.0;
730 }
731 if (hRightIter > hThreshold) {
732 uRightIter = huRightIter / hRightIter;
733 } else {
734 hRightIter = std::max(hRightIter, 0.0);
735 uRightIter = 0.0;
736 }
737 }
738 }
739
740 // Compute the flux waves as described in George2008, with an extension from Clawpack to also account for
741 // the updates of the flux of the transverse components.
742 if (wetDryState == WetDryState::WetDryWall) { // Zero ghost updates (wall boundary)
743 // Care about the left going wave (0) only
744 fWaves[0][0] = beta[0] * eigenVectors[1][0];
745 fWaves[1][0] = beta[0] * eigenVectors[2][0];
746 fWaves[2][0] = beta[0] * eigenVectors[1][0];
747 fWaves[2][0] = fWaves[2][0] * vLeft;
748
749 // Set the rest to zero
750 fWaves[0][1] = fWaves[1][1] = fWaves[2][1] = 0.0;
751 fWaves[0][2] = fWaves[1][2] = fWaves[2][2] = 0.0;
752
753 waveSpeeds[0] = eigenValues[0];
754 waveSpeeds[1] = waveSpeeds[2] = 0.0;
755
756 // assert(eigenValues[0] < ZERO_TOL);
757 } else if (wetDryState == WetDryState::DryWetWall) { // Zero ghost updates (wall boundary)
758 // Care about the right going wave (2) only
759 fWaves[0][2] = beta[2] * eigenVectors[1][2];
760 fWaves[1][2] = beta[2] * eigenVectors[2][2];
761 fWaves[2][2] = beta[2] * eigenVectors[1][2];
762 fWaves[2][2] = fWaves[2][2] * vRight;
763
764 // Set the rest to zero
765 fWaves[0][0] = fWaves[1][0] = fWaves[2][0] = 0.0;
766 fWaves[0][1] = fWaves[1][1] = fWaves[2][1] = 0.0;
767
768 waveSpeeds[2] = eigenValues[2];
769 waveSpeeds[0] = waveSpeeds[1] = 0.0;
770
771 // assert(eigenValues[2] > -ZERO_TOL);
772 } else {
773 // Compute f-waves (default)
774 waveSpeeds[0] = eigenValues[0];
775 waveSpeeds[1] = eigenValues[1];
776 waveSpeeds[2] = eigenValues[2];
777
778 for (int waveNumber = 0; waveNumber < 3; waveNumber++) {
779 fWaves[0][waveNumber] = beta[waveNumber] * eigenVectors[1][waveNumber]; // Select 2nd and
780 fWaves[1][waveNumber] = beta[waveNumber] * eigenVectors[2][waveNumber]; // 3rd component of the augmented
781 // decomposition
782 fWaves[2][waveNumber] = beta[waveNumber] * eigenVectors[1][waveNumber]; // needed for the transverse velocity
783 }
784
785 // Adding the flux of the transverse velocity huv for a more accurate representation similar to Clawpack
786 fWaves[2][0] = fWaves[2][0] * vLeft;
787 fWaves[2][2] = fWaves[2][2] * vRight;
788 fWaves[2][1] = hRight * uRight * vRight - hLeft * uLeft * vLeft - fWaves[2][0] - fWaves[2][2];
789 }
790 }
791} // namespace applications::exahype2::ShallowWater
792
793template <class T, class Shortcuts>
795 const T* const NOALIAS QL,
796 const T* const NOALIAS QR,
797 const tarch::la::Vector<DIMENSIONS, double>& x,
798 const tarch::la::Vector<DIMENSIONS, double>& h,
799 const double t,
800 const double dt,
801 const int normal,
802 T* const NOALIAS FL,
803 T* const NOALIAS FR
804) {
805 T hLeft = QL[Shortcuts::h];
806 T hRight = QR[Shortcuts::h];
807 // Initialise the respective hu and hv values depending on which normal is currently evaluated, so
808 // hu == Q[Shortcuts::hu] if normal == 0 and hu == Q[Shortcuts::hv] if normal == 1.
809 T huLeft = QL[Shortcuts::hu + normal];
810 T huRight = QR[Shortcuts::hu + normal];
811 T hvLeft = QL[Shortcuts::hu + 1 - normal];
812 T hvRight = QR[Shortcuts::hu + 1 - normal];
813 T bLeft = QL[Shortcuts::z];
814 T bRight = QR[Shortcuts::z];
815 T uLeft = 0;
816 T uRight = 0;
817 T vLeft = 0;
818 T vRight = 0;
819
820 WetDryState wetDryState;
821
822 // Height of the augmented homogeneous Riemann-problem at middle state (computed by determineMiddleState)
823 T hMiddle = 0;
824
825 // Riemann-structure of the homogeneous Riemann-problem
826 RiemannStructure riemannStructure;
827
828 // Declare variables which are used over and over again
829 T sqrtGhLeft = 0;
830 T sqrtGhRight = 0;
831
832 T sqrthLeft = 0;
833 T sqrthRight = 0;
834 T sqrtG = 0;
835
836 T middleSpeeds[2];
837
838 // Set speeds to zero (will be determined later)
839 uLeft = uRight = 0;
840 vLeft = vRight = 0;
841
842 for (int i = 0; i < 3; i++) {
843 FL[i] = 0;
844 FR[i] = 0;
845 }
846
847 // Compute speeds or set them to zero (dry cells)
848 if (hLeft > hThreshold) {
849 uLeft = huLeft / hLeft;
850 vLeft = hvLeft / hLeft;
851 } else {
852 bLeft += hLeft;
853 hLeft = huLeft = uLeft = 0;
854 hvLeft = vLeft = 0;
855 }
856
857 if (hRight > hThreshold) {
858 uRight = huRight / hRight;
859 vRight = hvRight / hRight;
860 } else {
861 bRight += hRight;
862 hRight = huRight = uRight = 0;
863 hvRight = vRight = 0;
864 }
865
866 wetDryState = determineWetDryState(
867 hLeft,
868 huLeft,
869 hvLeft,
870 uLeft,
871 vLeft,
872 bLeft,
873 hRight,
874 huRight,
875 hvRight,
876 uRight,
877 vRight,
878 bRight,
879 hMiddle,
880 wetDryState,
881 riemannStructure,
882 middleSpeeds
883 );
884
885 if (wetDryState == WetDryState::DryDry) {
886 return 0; // No computation necessary for a dry region
887 }
888
889 // Precompute some terms which are fixed during
890 // the computation after some specific point
891 sqrtG = std::sqrt(g);
892 sqrthLeft = std::sqrt(hLeft);
893 sqrthRight = std::sqrt(hRight);
894
895 sqrtGhLeft = sqrtG * sqrthLeft;
896 sqrtGhRight = sqrtG * sqrthRight;
897
898 // Where to store the three waves
899 // [3][3] array necessary to also store the transverse components
900 T fWaves[3][3];
901 // and their speeds
902 T waveSpeeds[3];
903
904 // Compute the wave decomposition of the augmented state vector
906 hLeft,
907 huLeft,
908 uLeft,
909 vLeft,
910 bLeft,
911 hRight,
912 huRight,
913 uRight,
914 vRight,
915 bRight,
916 hMiddle,
917 sqrtGhLeft,
918 sqrtGhRight,
919 sqrthLeft,
920 sqrthRight,
921 sqrtG,
922 wetDryState,
923 riemannStructure,
924 fWaves,
925 waveSpeeds,
926 middleSpeeds
927 );
928
929 /*
930 * Computation of the left and right going fluctuations, based on the wave speeds.
931 */
932 // Compute the updates from the three propagating waves
933 // A^-\delta Q = \sum{s[i]<0} \beta[i] * r[i] = A^-\delta Q = \sum{s[i]<0} Z^i
934 // A^+\delta Q = \sum{s[i]>0} \beta[i] * r[i] = A^-\delta Q = \sum{s[i]<0} Z^i
935 for (int waveNumber = 0; waveNumber < 3; waveNumber++) {
936 if (waveSpeeds[waveNumber] < 0.0) { // Left going
937 FL[Shortcuts::h] += fWaves[0][waveNumber];
938 FL[Shortcuts::hu + normal] += fWaves[1][waveNumber];
939 FL[Shortcuts::hu + 1 - normal] += fWaves[2][waveNumber];
940 } else if (waveSpeeds[waveNumber] > 0.0) { // Right going
941 FR[Shortcuts::h] -= fWaves[0][waveNumber];
942 FR[Shortcuts::hu + normal] -= fWaves[1][waveNumber];
943 FR[Shortcuts::hu + 1 - normal] -= fWaves[2][waveNumber];
944 } else {
945 FL[Shortcuts::h] += 0.5 * fWaves[0][waveNumber];
946 FL[Shortcuts::hu + normal] += 0.5 * fWaves[1][waveNumber];
947 FL[Shortcuts::hu + 1 - normal] += 0.5 * fWaves[2][waveNumber];
948 FR[Shortcuts::h] -= 0.5 * fWaves[0][waveNumber];
949 FR[Shortcuts::hu + normal] -= 0.5 * fWaves[1][waveNumber];
950 FR[Shortcuts::hu + 1 - normal] -= 0.5 * fWaves[2][waveNumber];
951 }
952 }
953
954 // Compute maximum absolute wave speed (-> CFL-condition) and return it
955 return std::fmax(std::fabs(waveSpeeds[0]), std::fmax(std::fabs(waveSpeeds[1]), std::fabs(waveSpeeds[2])));
956}
static GPU_CALLABLE_METHOD RiemannStructure determineRiemannStructure(const T &hLeft, const T &hRight, const T &uLeft, const T &uRight)
static GPU_CALLABLE_METHOD double augmentedStateRiemannSolver(const T *const NOALIAS QL, const T *const NOALIAS QR, const tarch::la::Vector< DIMENSIONS, double > &x, const tarch::la::Vector< DIMENSIONS, double > &h, const double t, const double dt, const int normal, T *const NOALIAS FL, T *const NOALIAS FR)
A implementation of the augmented state Riemann solver from David L.
static GPU_CALLABLE_METHOD void computeWaveDecomposition(T &hLeft, T &huLeft, T &uLeft, T &vLeft, T &bLeft, T &hRight, T &huRight, T &uRight, T &vRight, T &bRight, T &hMiddle, const T &sqrtGhLeft, const T &sqrtGhRight, const T &sqrthLeft, const T &sqrthRight, const T &sqrtG, WetDryState &wetDryState, RiemannStructure &riemannStructure, T fWaves[3][3], T waveSpeeds[3], T *middleSpeeds)
static GPU_CALLABLE_METHOD WetDryState determineWetDryState(T &hLeft, T &huLeft, T &hvLeft, T &uLeft, T &vLeft, T &bLeft, T &hRight, T &huRight, T &hvRight, T &uRight, T &vRight, T &bRight, T &hMiddle, WetDryState &wetDryState, RiemannStructure &riemannStructure, T *middleSpeeds)
RiemannStructure
The Riemann-struture of the homogeneous Riemann-problem.
static GPU_CALLABLE_METHOD void solveLinearEquation(const T matrix[3][3], const T b[3], T x[3])
static GPU_CALLABLE_METHOD T computeMiddleState(const T &hLeft, const T &hRight, const T &uLeft, const T &uRight, RiemannStructure &riemannStructure, T *middleSpeeds)
constexpr double hThreshold
Definition PDE.h:56