Peano
Loading...
Searching...
No Matches
AtmosphericPressure.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
4template <auto pointLonA, auto pointLatA, auto pointLonB, auto pointLatB>
6 const auto x,
7 const auto y,
8 const auto t,
9 auto projectionFunctionForward,
10 auto projectionFunctionInverse
11) {
12 constexpr auto aLon = pointLonA;
13 constexpr auto aLat = pointLatA;
14
15 constexpr auto bLon = pointLonB;
16 constexpr auto bLat = pointLatB;
17
18 const auto [lon, lat] = projectionFunctionInverse(x, y);
19
20 // https://edanya.uma.es/hysea/index.php/benchmarks/meteo-tsunami/143-reproducing-real-meteotsunami-in-the-gulf-of-mexico
21 auto pressureGaus = [](const auto x, const auto y) {
22 constexpr auto C = 1;
23 constexpr auto AL = 37.61;
24 constexpr auto AR = 4.665;
25 constexpr auto BL = 0.31;
26 constexpr auto BR = 0.5;
27 const auto y2 = ((y / C) * (y / C));
28 if (x < 0) {
29 return -AL * 100 * x * std::exp(-y2 - ((x / BL) * (x / BL)));
30 }
31 if (x > 0) {
32 return -AR * 100 * x * std::exp(-y2 - ((x / BR) * (x / BR)));
33 }
34 return 0.0;
35 };
36
37 auto distanceOverTime = [](const auto t) {
38 constexpr auto speed = 20.0; // 20 m/s
39 return speed * t;
40 };
41
42 auto pointOnVector = [projectionFunctionForward](const auto l) {
43 // In geographic lon/lat, the A→B connection is generally curved on the globe.
44 // In projected AEA x/y (meters), we can treat A→B as a straight segment for local operations.
45 // Precompute the segment vector (dx, dy) and its length once (thread-safe statics).
46 static const auto A = projectionFunctionForward(aLon, aLat);
47 static const auto B = projectionFunctionForward(bLon, bLat);
48 static const auto dx = B.first - A.first;
49 static const auto dy = B.second - A.second;
50 static const double dist = std::hypot(dx, dy);
51
52 const double t = (dist > 0.0) ? (l / dist) : 0.0;
53
54 const double x = A.first + t * dx;
55 const double y = A.second + t * dy;
56
57 return std::pair{x, y}; // {x, y}
58 };
59
60 // Rotates (lon, lat) so that the path from a to b lies along the x-axis; used to align atmospheric pressure
61 // spatially.
62 auto rotLonLatOffsets =
63 [projectionFunctionForward](const auto lon, const auto lat, const auto centerLon, const auto centerLat) {
64 // Compute the angle in projected x/y (meters), not lon/lat (degrees):
65 // x/y have uniform metric units (no cos(lat) scaling, no dateline wrap), so atan2(dy,dx) yields a
66 // physically correct direction for rotation and alignment.
67 static const auto A = projectionFunctionForward(aLon, aLat);
68 static const auto B = projectionFunctionForward(bLon, bLat);
69 static const auto distanceX = B.first - A.first;
70 static const auto distanceY = B.second - A.second;
71
72 static const auto rad = std::atan2(distanceY, distanceX);
73
74 static const auto s = std::sin(rad);
75 static const auto c = std::cos(rad);
76
77 // Use remainder to unwrap the longitude delta into (−180, 180]: this avoids 360° jumps at the dateline
78 // so we rotate using the smallest local offset around the center (ensure lon units are degrees).
79 // Only longitude wraps at the dateline; latitude has no 360° discontinuity,
80 // so unwrapping is needed for lon deltas but not for lat.
81
82 const auto offsetLon = lon - centerLon;
83 const auto offsetLat = lat - centerLat;
84
85 const auto unwrapLon = std::remainder(offsetLon, 360.0);
86 constexpr double D2R = M_PI / 180.0;
87 const auto scalingFactor = std::cos(centerLat * D2R); // scale longitude by cos(latitude)
88 const auto scaledLon = unwrapLon * scalingFactor;
89
90 const auto newLon = (scaledLon * c) + (offsetLat * s);
91 const auto newLat = (-scaledLon * s) + (offsetLat * c);
92
93 const auto normalisedLon = std::abs(scalingFactor) > 1e-12 ? (newLon / scalingFactor) : 0.0;
94 return std::pair{normalisedLon, newLat};
95 };
96
97 const auto distance = distanceOverTime(t);
98 const auto [centerX, centerY] = pointOnVector(distance);
99
100 // Offsets cannot be projected: map projections take absolute lon/lat ↔ x/y points.
101 // Because the inverse/forward map is nonlinear, inverse(center+offset) − inverse(center) ≠ inverse(offset).
102 // Always project absolute points first, then form offsets in the target space.
103 const auto [centerLon, centerLat] = projectionFunctionInverse(centerX, centerY);
104
105 const auto [rotLon, rotLat] = rotLonLatOffsets(lon, lat, centerLon, centerLat);
106 return pressureGaus(rotLon, rotLat);
107};
static auto atmosphericPressure(const auto x, const auto y, const auto t, auto projectionFunctionForward, auto projectionFunctionInverse)
Computes the moving atmospheric pressure disturbance for the Gulf of Mexico test case.