Peano
FVSolver.cpp
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 #include "FVSolver.h"
4 
5 #include "Constants.h"
8 
9 tarch::logging::Log applications::exahype2::swe::FVSolver::_log("applications::exahype2::swe::FVSolver");
10 
11 
12 void applications::exahype2::swe::FVSolver::initialCondition(
13  [[maybe_unused]] double* const NOALIAS Q, // Q[3+1]
14  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
15  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
16  [[maybe_unused]] const bool gridIsConstructed
17 ) {
18  Q[Shortcuts::h] = 100;
19  Q[Shortcuts::hu] = 0;
20  Q[Shortcuts::hv] = 0;
21  Q[Shortcuts::b] = -100;
22 }
23 
24 
25 void applications::exahype2::swe::FVSolver::boundaryConditions(
26  [[maybe_unused]] const double* const NOALIAS Qinside, // Qinside[3+1]
27  [[maybe_unused]] double* const NOALIAS Qoutside, // Qoutside[3+1]
28  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
29  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
30  [[maybe_unused]] const double t,
31  [[maybe_unused]] const int normal
32 ) {
33  Qoutside[Shortcuts::h] = Qinside[Shortcuts::h];
34  Qoutside[Shortcuts::hu] = -Qinside[Shortcuts::hu];
35  Qoutside[Shortcuts::hv] = -Qinside[Shortcuts::hv];
36  Qoutside[Shortcuts::b] = Qinside[Shortcuts::b];
37 }
38 
39 
40 ::exahype2::RefinementCommand applications::exahype2::swe::FVSolver::refinementCriterion(
41  [[maybe_unused]] const double* const NOALIAS Q, // Q[3+1]
42  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
43  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
44  [[maybe_unused]] const double t
45 ) {
46  auto result = ::exahype2::RefinementCommand::Keep;
47  // @todo Implement
48  return result;
49 }
50 
51 
53  [[maybe_unused]] const double* const NOALIAS Q, // Q[3+1]
54  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
55  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
56  [[maybe_unused]] const double t,
57  [[maybe_unused]] const double dt,
58  [[maybe_unused]] const int normal
59 ) {
60  double L[3];
61  L[Shortcuts::h] = 0.0;
62  L[Shortcuts::hu] = 0.0;
63  L[Shortcuts::hv] = 0.0;
64 
65  constexpr double DRY_TOL = std::numeric_limits<double>::epsilon();
66  if (Q[Shortcuts::h] <= DRY_TOL) {
67  return 0.0;
68  }
69 
70  const double u = Q[Shortcuts::hu + normal] / Q[Shortcuts::h];
71  const double c = std::sqrt(GRAV * Q[Shortcuts::h]);
72  L[Shortcuts::h] = u;
73  L[Shortcuts::hu] = u + c;
74  L[Shortcuts::hv] = u - c;
75 
76  return std::fmax(std::fabs(L[0]), std::fmax(std::fabs(L[1]), std::fabs(L[2])));
77 }
78 
79 
81  [[maybe_unused]] const double* const NOALIAS Q, // Q[3+1]
82  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
83  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
84  [[maybe_unused]] const double t,
85  [[maybe_unused]] const double dt,
86  [[maybe_unused]] const int normal,
87  [[maybe_unused]] double* const NOALIAS F // F[3]
88 ) {
89  F[Shortcuts::h] = 0.0;
90  F[Shortcuts::hu] = 0.0;
91  F[Shortcuts::hv] = 0.0;
92  F[Shortcuts::b] = 0.0;
93 
94  constexpr double DRY_TOL = std::numeric_limits<double>::epsilon();
95  if (Q[Shortcuts::h] <= DRY_TOL) {
96  return;
97  }
98 
99  const double ih = 1.0 / Q[Shortcuts::h];
100 
101  F[Shortcuts::h] = Q[Shortcuts::hu + normal];
102  F[Shortcuts::hu] = Q[Shortcuts::hu + normal] * Q[Shortcuts::hu] * ih;
103  F[Shortcuts::hv] = Q[Shortcuts::hu + normal] * Q[Shortcuts::hv] * ih;
104  F[Shortcuts::hu + normal] += 0.5 * GRAV * Q[Shortcuts::h] * Q[Shortcuts::h];
105 }
106 
107 
108 void applications::exahype2::swe::FVSolver::nonconservativeProduct(
109  [[maybe_unused]] const double* const NOALIAS Q, // Q[3+1]
110  [[maybe_unused]] const double* const NOALIAS deltaQ, // deltaQ[3+1]
111  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
112  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
113  [[maybe_unused]] const double t,
114  [[maybe_unused]] const double dt,
115  [[maybe_unused]] const int normal,
116  [[maybe_unused]] double* const NOALIAS BTimesDeltaQ // BTimesDeltaQ[3]
117 ) {
118  BTimesDeltaQ[Shortcuts::h] = 0.0;
119  BTimesDeltaQ[Shortcuts::hu] = 0.0;
120  BTimesDeltaQ[Shortcuts::hv] = 0.0;
121  BTimesDeltaQ[Shortcuts::b] = 0.0;
122 
123  constexpr double DRY_TOL = std::numeric_limits<double>::epsilon();
124  if (Q[Shortcuts::h] <= DRY_TOL) {
125  return;
126  }
127 
128  BTimesDeltaQ[Shortcuts::hu + normal] = GRAV * Q[Shortcuts::h] * (deltaQ[Shortcuts::h] + deltaQ[Shortcuts::b]);
129 }
130 
131 namespace corriolis_force {
132  namespace {
145  auto constant([[maybe_unused]] const auto x, [[maybe_unused]] const auto y) {
153  return 1.04e-4; // Example value for mid-latitudes
154  };
155 
168  auto betaPlane([[maybe_unused]] const auto x, [[maybe_unused]] const auto y) {
176  constexpr double f0 = 1.0e-4; // f0 = reference Coriolis value
177  constexpr double beta = 2.0e-11; // β: meridional gradient of the Coriolis parameter (df/dy), used in β-plane
178  // approximation
179  return f0 + (beta * y);
180  };
181 
194  auto sphere([[maybe_unused]] const auto x, [[maybe_unused]] const auto y) {
203  constexpr double omega = 7.2921150e-5;
204  constexpr double toRad = M_PI / 180.0;
205 
206  const auto [lon, lat] = projection::albers_equal_area::ellipsoid::inverse(x, y);
207  const auto latRad = lat * toRad;
208  return 2.0 * omega * std::sin(latRad);
209  };
210  } // namespace
211 } // namespace corriolis_force
212 
213 namespace {
229  auto manningFriction(const auto h, const auto hu, const auto hv) {
230  namespace constants = ::applications::exahype2::swe;
231  constexpr auto manningFriction = 0.025; // sm^(1/3)
232  constexpr auto hMin = 0.2; // threshold to ensure stable bottom friction computation when h → 0
233 
234  const auto hSafe = (h > hMin ? h : hMin); // minimal water depth to avoid division by zero or instability near
235  // dry cells
236  const auto h43 = (hSafe * std::cbrt(hSafe)); // h^(4/3) is not stable for small h -> 0, TODO may need some more
237  // work
238 
239  return -constants::GRAV * manningFriction * manningFriction * std::sqrt((hu * hu) + (hv * hv)) / h43;
240  }
241 
242  auto atmosphericPressure(const auto x, const auto y, const auto t) {
243  namespace constants = ::applications::exahype2::swe;
244  constexpr auto aLon = constants::pointLonA;
245  constexpr auto aLat = constants::pointLatA;
246 
247  constexpr auto bLon = constants::pointLonB;
248  constexpr auto bLat = constants::pointLatB;
249 
250  // https://edanya.uma.es/hysea/index.php/benchmarks/meteo-tsunami/143-reproducing-real-meteotsunami-in-the-gulf-of-mexico
251  auto pressureGaus = [](const auto x, const auto y) {
252  constexpr auto C = 0.5;
253  constexpr auto AL = 37.61;
254  constexpr auto AR = 4.665;
255  constexpr auto BL = 0.31;
256  constexpr auto BR = 0.5;
257  const auto y2 = ((y / C) * (y / C));
258  if (x < 0) {
259  return -AL * x * std::exp(-y2 - ((x / BL) * (x / BL)));
260  }
261  if (x > 0) {
262  return -AR * x * std::exp(-y2 - ((x / BR) * (x / BR)));
263  }
264  return 0.0;
265  };
266 
267  auto distanceOverTime = [](const auto t) {
268  constexpr auto speed = 20.0; // 20 m/s
269  return speed * t;
270  };
271 
272  // Computes the geographic point at distance l (in meters) along the great-circle vector from a to b,
273  // using projected coordinates for distance calculation and linear interpolation in lon/lat.
274  auto pointOnVector = [](const auto l) {
275  const auto [aX, aY] = projection::albers_equal_area::ellipsoid::forward(aLon, aLat);
276  const auto [bX, bY] = projection::albers_equal_area::ellipsoid::forward(bLon, bLat);
277  static const auto distance = std::sqrt(((bX - aX) * (bX - aX)) + ((bY - aY) * (bY - aY)));
278  return std::pair{aLon + (l * (bLon - aLon) / distance), aLat + (l * (bLat - aLat) / distance)};
279  };
280 
281  // Rotates (lon, lat) so that the path from a to b lies along the x-axis; used to align atmospheric pressure
282  // spatially.
283  auto rotLonLat = [](const auto lon, const auto lat) {
284  constexpr auto dLon = bLon - aLon;
285  constexpr auto dLat = bLat - aLat;
286 
287  static const auto rad = std::atan2(dLat, dLon);
288 
289  static const auto sin = std::sin(rad);
290  static const auto cos = std::cos(rad);
291 
292  const auto lonNew = (lon * cos) + (lat * sin);
293  const auto latNew = (-lon * sin) + (lat * cos);
294  return std::pair{lonNew, latNew};
295  };
296 
297  const auto distance = distanceOverTime(t);
298  const auto [lon, lat] = projection::albers_equal_area::ellipsoid::inverse(x, y);
299  const auto [lonCenter, latCenter] = pointOnVector(distance);
300  const auto [lonRot, latRot] = rotLonLat(lon - lonCenter, lat - latCenter);
301  return pressureGaus(lonRot, latRot);
302  };
303 } // namespace
304 
305 void applications::exahype2::swe::FVSolver::sourceTerm(
306  [[maybe_unused]] const double* const NOALIAS Q, // Q[3+1]
307  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& x,
308  [[maybe_unused]] const tarch::la::Vector<DIMENSIONS, double>& h,
309  [[maybe_unused]] const double t,
310  [[maybe_unused]] const double dt,
311  [[maybe_unused]] double* const NOALIAS S // S[3]
312 ) {
313  namespace constants = ::applications::exahype2::swe;
314 
321  // default values
322  S[Shortcuts::h] = 0;
323  S[Shortcuts::hu] = 0;
324  S[Shortcuts::hv] = 0;
325 
326  // Skip dry cells
327  constexpr double DRY_TOL = std::numeric_limits<double>::epsilon();
328  if (Q[Shortcuts::h] <= DRY_TOL) {
329  return;
330  }
331 
339  constexpr auto waterDensity = constants::waterDensity;
340 
341  const auto xCoord = x(0);
342  const auto yCoord = x(1);
343 
344  const auto uVel = Q[Shortcuts::hu] / Q[Shortcuts::h];
345  const auto vVel = Q[Shortcuts::hv] / Q[Shortcuts::h];
346 
347  // Update Source term
348  S[Shortcuts::hu] = corriolis_force::sphere(xCoord, yCoord) * uVel;
349  S[Shortcuts::hv] = -corriolis_force::sphere(xCoord, yCoord) * vVel;
350 
351  S[Shortcuts::hu] += manningFriction(Q[Shortcuts::h], Q[Shortcuts::hu], Q[Shortcuts::hv]) * Q[Shortcuts::hu];
352  S[Shortcuts::hv] += manningFriction(Q[Shortcuts::h], Q[Shortcuts::hu], Q[Shortcuts::hv]) * Q[Shortcuts::hv];
353 
354  auto wrapperAtmosphericPressure = [](const auto x, const auto y, const auto t) {
355  return atmosphericPressure(x, y, t);
356  };
357 
359  partialX(wrapperAtmosphericPressure, xCoord, yCoord, t)
360  / waterDensity;
362  partialY(wrapperAtmosphericPressure, xCoord, yCoord, t)
363  / waterDensity;
364 }
static double maxEigenvalue(const double *const Q, int normal, const double CCZ4e, const double CCZ4ds, const double CCZ4GLMc, const double CCZ4GLMd)
u
Definition: euler.py:113
flux
Definition: euler.py:29
auto partialY(auto f, const auto x, const auto y, const auto t)
Approximates the partial derivative ∂f/∂y of a function f(x, y, t) using central differences.
auto partialX(auto f, const auto x, const auto y, const auto t)
Approximates the partial derivative ∂f/∂x of a function f(x, y, t) using central differences.
auto inverse(const auto x, const auto y)
Inverts the Albers Equal-Area Conic projection for WGS_1984 and returns the geographic coordinates (l...
auto forward(const auto lon, const auto lat)
hu
Definition: swe.py:80
hv
Definition: swe.py:81
b
Definition: swe.py:82
h
Definition: swe.py:79
tarch::logging::Log _log
This is variant 1 of the fused kernels.