Peano
Loading...
Searching...
No Matches
HLLCRiemannSolver.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
9 template <class T>
10 static inline GPU_CALLABLE_METHOD double middleHeightEstimation(
11 const T& hLeft,
12 const T& hRight,
13 const T& uLeft,
14 const T& uRight
15 ) {
16 T hMin = std::fmin(hLeft, hRight);
17 T hMax = std::fmax(hLeft, hRight);
18
19 T uDiff = uRight - uLeft;
20 T hMiddle = 0;
21
22 if (hLeft <= hThreshold or hRight <= hThreshold) {
23 hMiddle = 0;
24 return hMiddle;
25 }
26
27 T phiMin = uDiff + 2.0 * (std::sqrt(g * hMin) - std::sqrt(g * hMax));
28 T phiMax = uDiff + (hMax - hMin) * (std::sqrt(0.5 * g * (hMax + hMin) / (hMax * hMin)));
29 T hZero = 0;
30 T gLeft = 0;
31 T gRight = 0;
32 T phiZero = 0;
33 T phiZeroDerivative = 0;
34 T slope = 0;
35
36 if (phiMax <= 0.0) { // Shock-shock Riemann structure
37 hZero = hMax;
38 for (int i = 0; i < NEWTON_ITER; ++i) {
39 gLeft = std::sqrt(0.5 * g * (1 / hZero + 1 / hLeft));
40 gRight = std::sqrt(0.5 * g * (1 / hZero + 1 / hRight));
41 phiZero = uDiff + (hZero - hLeft) * gLeft + (hZero - hRight) * gRight;
42 if (std::fabs(phiZero) < NEWTON_TOL) {
43 break;
44 }
45 phiZeroDerivative = gLeft - g * (hZero - hLeft) / (4.0 * (hZero * hZero) * gLeft) + gRight
46 - g * (hZero - hRight) / (4.0 * (hZero * hZero) * gRight);
47 slope = 2.0 * std::sqrt(hZero) * phiZeroDerivative;
48 hZero = std::pow((std::sqrt(hZero) - phiZero / slope), 2);
49 }
50 hMiddle = hZero;
51 return hMiddle;
52 } else if (phiMin > 0.0) { // Rarefaction-rarefaction
53 hMiddle = std::max(0.0, uLeft - uRight + 2.0 * (std::sqrt(g * hLeft) + std::sqrt(g * hRight)));
54 hMiddle = 1.0 / (16.0 * g) * hMiddle * hMiddle;
55 return hMiddle;
56 } else { // One rarefaction-one shock
57 hZero = hMin;
58 for (int i = 0; i < NEWTON_ITER; ++i) {
59 phiZero = uDiff + 2.0 * (std::sqrt(g * hZero) - std::sqrt(g * hMax))
60 + (hZero - hMin) * std::sqrt(0.5 * g * (1 / hZero + 1 / hMin));
61 slope = (phiMax - phiZero) / (hMax - hMin);
62 hZero = hZero - phiZero / slope;
63 }
64 hMiddle = hZero;
65 return hMiddle;
66 }
67 }
68} // namespace applications::exahype2::ShallowWater
69
70template <class T, class Shortcuts>
71static inline GPU_CALLABLE_METHOD double applications::exahype2::ShallowWater::hllcRiemannSolver(
72 const T* const NOALIAS QL,
73 const T* const NOALIAS QR,
74 const tarch::la::Vector<DIMENSIONS, double>& x,
75 const tarch::la::Vector<DIMENSIONS, double>& h,
76 const double t,
77 const double dt,
78 const int normal,
79 T* const NOALIAS FL,
80 T* const NOALIAS FR
81) {
82 // Correction of particle velocities according to Kurganov (modified version).
83 // The vectors contain the horizontal velocities u and v of the cells.
84 T horizontalVelocitiesLeft[2] = {
85 QL[Shortcuts::hu] * QL[Shortcuts::h] * std::sqrt(2)
86 / std::sqrt(std::pow(QL[Shortcuts::h], 4) + std::pow(std::fmax(QL[Shortcuts::h], hThreshold), 4)),
87 QL[Shortcuts::hv] * QL[Shortcuts::h] * std::sqrt(2)
88 / std::sqrt(std::pow(QL[Shortcuts::h], 4) + std::pow(std::fmax(QL[Shortcuts::h], hThreshold), 4))
89 };
90
91 T horizontalVelocitiesRight[2] = {
92 QR[Shortcuts::hu] * QR[Shortcuts::h] * std::sqrt(2)
93 / std::sqrt(std::pow(QR[Shortcuts::h], 4) + std::pow(std::fmax(QR[Shortcuts::h], hThreshold), 4)),
94 QR[Shortcuts::hv] * QR[Shortcuts::h] * std::sqrt(2)
95 / std::sqrt(std::pow(QR[Shortcuts::h], 4) + std::pow(std::fmax(QR[Shortcuts::h], hThreshold), 4))
96 };
97
98 // Hydrostatic reconstruction of the heights
99 const T zMax = std::fmax(QL[Shortcuts::z], QR[Shortcuts::z]);
100 const T hLeft = std::fmax(0.0, QL[Shortcuts::h] + QL[Shortcuts::z] - zMax);
101 const T hRight = std::fmax(0.0, QR[Shortcuts::h] + QR[Shortcuts::z] - zMax);
102
103 // Check for dry land after the hydrostatic reconstruction
104 // as it might set both heights to zero or close to zero
105 // even if they weren't before the reconstruction.
106 const bool leftWet = tarch::la::greater(hLeft, hThreshold);
107 const bool rightWet = tarch::la::greater(hRight, hThreshold);
108 const bool leftDry = not leftWet;
109 const bool rightDry = not rightWet;
110
111 for (int i = 0; i < 3; ++i) {
112 FL[i] = 0.0;
113 FR[i] = 0.0;
114 }
115
116 // Dry land, skip the Riemann problem
117 if (leftDry and rightDry) {
118 return 0.0;
119 }
120
121 // Also reconstruct the momenta based on the reconstructed height.
122 // Compute the conserved momentum variables according to the Kurganov modified speeds.
123 const T huLeft = hLeft * horizontalVelocitiesLeft[0];
124 const T hvLeft = hLeft * horizontalVelocitiesLeft[1];
125 const T huRight = hRight * horizontalVelocitiesRight[0];
126 const T hvRight = hRight * horizontalVelocitiesRight[1];
127
128 // Wave speed estimation process
129 // 1) calculate the first h* estimate
130 // celerity a = squareRoot(g * h)
131 T celerityLeft = std::sqrt(g * hLeft);
132 T celerityRight = std::sqrt(g * hRight);
133 T hStarEstimate = 0.0;
134
135 // The following is a case distinction which determines which wave speed estimate is used:
136 // If the option UseTwoRarefactionsEstimate is enabled, the middle height estimation is implemented as described in
137 // Toro2019 and Toro2024.
138 // If not, the middle height estimation process is implemented as in the Clawpack Riemann solver, which should be
139 // more exact (this version is still experimental).
140 constexpr bool UseTwoRarefactionsEstimate = true;
141 if constexpr (UseTwoRarefactionsEstimate) {
142 // Estimate of the celerity based on the assumption of two outer rarefaction waves
143 T celerityEstimate = 0.5 * (celerityLeft + celerityRight)
144 - 0.25 * (horizontalVelocitiesRight[normal] - horizontalVelocitiesLeft[normal]);
145 // Estimate for the middle state height
146 hStarEstimate = std::pow(celerityEstimate, 2) / g;
147 // This is an experimental idea which could be used for better capturing developing vacuum states
148 // if ((2 * (celerityLeft + celerityRight)) <= (horizontalVelocitiesRight[normal] -
149 // horizontalVelocitiesLeft[normal])) { // Vacuum condition, check for safety
150 // hStarEstimate = 0.0;
151 //}
152 } else {
153 hStarEstimate = middleHeightEstimation(
154 hLeft,
155 hRight,
156 horizontalVelocitiesLeft[normal],
157 horizontalVelocitiesRight[normal]
158 );
159 }
160
161 // Additionally, as described in the book Computational Algorithms for Shallow Water Equations, the
162 // wave speeds estimates will be modified depending on whether there is a wet/dry front in order to better capture
163 // the speed of that wet/dry front, described in the book section 11.10.3 Dry-Bed Approximate Riemann Solvers.
164
165 // Outer wave speeds
166 T sLeft = 0;
167 T sRight = 0;
168 T qLeft = 0;
169 T qRight = 0;
170
171 // 2) determine qL and qR, case distinction whether a shock wave or a rarefaction wave is expected && 3) compute wave
172 // speed estimates sLeft, sRight and sStar.
173 if (hLeft > hThreshold) {
174 if (hRight > hThreshold) {
175 qLeft = (hStarEstimate > hLeft)
176 ? std::sqrt(0.5 * ((hStarEstimate + hLeft) * hStarEstimate) / (std::pow(hLeft, 2)))
177 : 1.0;
178 sLeft = horizontalVelocitiesLeft[normal] - qLeft * celerityLeft;
179 } else {
180 // If the right cell is dry, the left cell should always contain a rarefaction and not a shock.
181 sLeft = horizontalVelocitiesLeft[normal] - 1.0 * celerityLeft;
182 }
183 } else {
184 // Left cell is dry, its respective speed is set to the wet dry front characteristic of the single rarefaction fan.
185 sLeft = horizontalVelocitiesRight[normal] - 2 * celerityRight;
186 }
187
188 if (hRight > hThreshold) {
189 if (hLeft > hThreshold) {
190 qRight = (hStarEstimate > hRight)
191 ? std::sqrt(0.5 * ((hStarEstimate + hRight) * hStarEstimate) / (std::pow(hRight, 2)))
192 : 1.0;
193 sRight = horizontalVelocitiesRight[normal] + qRight * celerityRight;
194 } else {
195 // If the left cell is dry, the right cell should always contain a rarefaction and not a shock.
196 sRight = horizontalVelocitiesRight[normal] + 1.0 * celerityRight;
197 }
198 } else {
199 // Right cell is dry, its respective speed is set to the wet dry front characteristic of the single rarefaction fan.
200 sRight = horizontalVelocitiesLeft[normal] + 2 * celerityLeft;
201 }
202
203 // Middle speed of the shear wave
204 T sStar = 0;
205
206 // If one takes the formula for sStar and the above speed estimates, sStar coincides with either the left or the
207 // right wave speed respectively in the case of a dry state Riemann problem.
208 if (hLeft <= hThreshold) {
209 sStar = sLeft;
210 } else if (hRight <= hThreshold) {
211 sStar = sRight;
212 } else {
213 sStar
214 = (sLeft * hRight * (horizontalVelocitiesRight[normal] - sRight)
215 - sRight * hLeft * (horizontalVelocitiesLeft[normal] - sLeft))
216 / (hRight * (horizontalVelocitiesRight[normal] - sRight) - hLeft * (horizontalVelocitiesLeft[normal] - sLeft)); // TODO: Possible instability
217 }
218
219 // The middle wave speed should not be higher than the outer wave speeds, so the left and right wave speeds.
220 // Thus, it should be limited.
221 sStar = std::max(sLeft, sStar);
222 sStar = std::min(sRight, sStar);
223
224 // 4) Compute qStarLeft and qStarRight (only differ in the transverse velocity component).
225 // Compute hStar based on wave speeds estimates sLeft and sRight.
226 T qStarLeft[3];
227 T qStarRight[3];
228 T hStarLeft = 0;
229 T hStarRight = 0;
230
231 // Compute the conserved variable states Q in the star region
232 if (hLeft > hThreshold) {
233 hStarLeft = hLeft * ((sLeft - horizontalVelocitiesLeft[normal]) / (sLeft - sStar));
234 // TODO: This commented-out code should hold mathematically if the left cell is wet and the right cell is dry but
235 // still experimental
236 if (hRight <= hThreshold) {
237 hStarLeft = (1 / 3) * hLeft;
238 }
239 qStarLeft[0] = hStarLeft;
240 qStarLeft[1] = (normal == 0) ? (hStarLeft * sStar) : (hStarLeft * horizontalVelocitiesLeft[0]);
241 qStarLeft[2] = (normal == 0) ? (hStarLeft * horizontalVelocitiesLeft[1]) : (hStarLeft * sStar);
242 } else {
243 qStarLeft[0] = 0;
244 qStarLeft[1] = 0;
245 qStarLeft[2] = 0;
246 }
247
248 if (hRight > hThreshold) {
249 if (hLeft <= hThreshold) {
250 hStarRight = (1 / 3) * hRight;
251 }
252 hStarRight = hRight * ((sRight - horizontalVelocitiesRight[normal]) / (sRight - sStar));
253 qStarRight[0] = hStarRight;
254 qStarRight[1] = (normal == 0) ? (hStarRight * sStar) : (hStarRight * horizontalVelocitiesRight[0]);
255 qStarRight[2] = (normal == 0) ? (hStarRight * horizontalVelocitiesRight[1]) : (hStarRight * sStar);
256 } else {
257 qStarRight[0] = 0;
258 qStarRight[1] = 0;
259 qStarRight[2] = 0;
260 }
261
262 // Compute the fluxes in the left and right cell
263 double fluxLeft[3] = {
264 normal == 0 ? (hLeft * horizontalVelocitiesLeft[0]) : (hLeft * horizontalVelocitiesLeft[1]),
265 normal == 0
266 ? (hLeft * horizontalVelocitiesLeft[0] * horizontalVelocitiesLeft[0] + 0.5 * g * std::pow(hLeft, 2))
267 : (hLeft * horizontalVelocitiesLeft[1] * horizontalVelocitiesLeft[0]),
268 normal == 0
269 ? (hLeft * horizontalVelocitiesLeft[0] * horizontalVelocitiesLeft[1])
270 : (hLeft * horizontalVelocitiesLeft[1] * horizontalVelocitiesLeft[1] + 0.5 * g * std::pow(hLeft, 2))
271 };
272
273 double fluxRight[3] = {
274 normal == 0 ? (hRight * horizontalVelocitiesRight[0]) : (hRight * horizontalVelocitiesRight[1]),
275 normal == 0
276 ? (hRight * horizontalVelocitiesRight[0] * horizontalVelocitiesRight[0] + 0.5 * g * std::pow(hRight, 2))
277 : (hRight * horizontalVelocitiesRight[1] * horizontalVelocitiesRight[0]),
278 normal == 0
279 ? (hRight * horizontalVelocitiesRight[0] * horizontalVelocitiesRight[1])
280 : (hRight * horizontalVelocitiesRight[1] * horizontalVelocitiesRight[1] + 0.5 * g * std::pow(hRight, 2))
281 };
282
283 // 5) Now the HLLC flux can be calculated, case distinction based on sLeft and sRight.
284 double hllcFlux[3];
285 if (hLeft > hThreshold && hRight > hThreshold) {
286 if (sLeft >= 0) {
287 hllcFlux[0] = fluxLeft[0];
288 hllcFlux[1] = fluxLeft[1];
289 hllcFlux[2] = fluxLeft[2];
290 } else if (sStar >= 0) {
291 hllcFlux[0] = fluxLeft[0] + sLeft * (qStarLeft[0] - hLeft);
292 hllcFlux[1] = fluxLeft[1] + sLeft * (qStarLeft[1] - (hLeft * horizontalVelocitiesLeft[0]));
293 hllcFlux[2] = fluxLeft[2] + sLeft * (qStarLeft[2] - (hLeft * horizontalVelocitiesLeft[1]));
294 } else if (sRight >= 0) {
295 hllcFlux[0] = fluxRight[0] + sRight * (qStarRight[0] - hRight);
296 hllcFlux[1] = fluxRight[1] + sRight * (qStarRight[1] - (hRight * horizontalVelocitiesRight[0]));
297 hllcFlux[2] = fluxRight[2] + sRight * (qStarRight[2] - (hRight * horizontalVelocitiesRight[1]));
298 } else { // if sRight <= 0
299 hllcFlux[0] = fluxRight[0];
300 hllcFlux[1] = fluxRight[1];
301 hllcFlux[2] = fluxRight[2];
302 }
303 } else if (hLeft <= hThreshold) { // Q_{Left}^* is not defined
304 if (sLeft >= 0) { // sLeft == sStar
305 hllcFlux[0] = fluxLeft[0];
306 hllcFlux[1] = fluxLeft[1];
307 hllcFlux[2] = fluxLeft[2];
308 } else if (sRight >= 0) {
309 hllcFlux[0] = fluxRight[0] + sRight * (qStarRight[0] - hRight);
310 hllcFlux[1] = fluxRight[1] + sRight * (qStarRight[1] - (hRight * horizontalVelocitiesRight[0]));
311 hllcFlux[2] = fluxRight[2] + sRight * (qStarRight[2] - (hRight * horizontalVelocitiesRight[1]));
312 } else {
313 hllcFlux[0] = fluxRight[0];
314 hllcFlux[1] = fluxRight[1];
315 hllcFlux[2] = fluxRight[2];
316 }
317 } else if (hRight <= hThreshold) { // Q_{Right}^* is not defined
318 if (sLeft >= 0) {
319 hllcFlux[0] = fluxLeft[0];
320 hllcFlux[1] = fluxLeft[1];
321 hllcFlux[2] = fluxLeft[2];
322 } else if (sRight >= 0) { // sRight == sStar
323 hllcFlux[0] = fluxLeft[0] + sLeft * (qStarLeft[0] - hLeft);
324 hllcFlux[1] = fluxLeft[1] + sLeft * (qStarLeft[1] - (hLeft * horizontalVelocitiesLeft[0]));
325 hllcFlux[2] = fluxLeft[2] + sLeft * (qStarLeft[2] - (hLeft * horizontalVelocitiesLeft[1]));
326 } else {
327 hllcFlux[0] = fluxRight[0];
328 hllcFlux[1] = fluxRight[1];
329 hllcFlux[2] = fluxRight[2];
330 }
331 }
332
333 // Now include the source term already in the fluctuation computation.
334 // Source term is based on the reconstructed water heights (for well-balancedness).
335 // Always the square of the original value minus the square of the reconstructed value.
336 double sourceLeft[3] = {
337 0,
338 normal == 0 ? g * 0.5 * ((QL[Shortcuts::h] * QL[Shortcuts::h]) - (hLeft * hLeft)) : 0.0,
339 normal == 0 ? 0.0 : g * 0.5 * ((QL[Shortcuts::h] * QL[Shortcuts::h]) - (hLeft * hLeft))
340 };
341
342 double sourceRight[3] = {
343 0,
344 normal == 0 ? g * 0.5 * ((QR[Shortcuts::h] * QR[Shortcuts::h]) - (hRight * hRight)) : 0.0,
345 normal == 0 ? 0.0 : g * 0.5 * ((QR[Shortcuts::h] * QR[Shortcuts::h]) - (hRight * hRight))
346 };
347
348 // Compute left- and right-going fluctuations based on the HLLC-flux and the source term
349 for (int i = 0; i < 3; ++i) {
350 FL[i] = hllcFlux[i] + sourceLeft[i];
351 FR[i] = hllcFlux[i] + sourceRight[i];
352 }
353
354 return (std::max(std::abs(sLeft), std::abs(sRight)));
355}
static GPU_CALLABLE_METHOD double hllcRiemannSolver(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)
Implementation of a HLLC solver utilising Einfeldt speeds for the outer wave speed estimates.
static GPU_CALLABLE_METHOD double middleHeightEstimation(const T &hLeft, const T &hRight, const T &uLeft, const T &uRight)
constexpr double hThreshold
Definition PDE.h:56