Peano
Loading...
Searching...
No Matches
meteotsunami-r1.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
3import numpy as np
4
5import peano4
6import exahype2
7
8L = 300e3
9physical_offset = [-39870.0, 0.0]
10physical_size = [379700.0, L / 3]
11square_side = max(physical_size)
12
13offset = [
14 physical_offset[0] - 0.5 * (square_side - physical_size[0]),
15 physical_offset[1] - 0.5 * (square_side - physical_size[1]),
16]
17size = [square_side, square_side]
18
19
20def manning_coefficient(H: float, z0: float = 0.003) -> float:
21 """
22 Compute the Manning coefficient n based on the Chezy formulation:
23
24 C = 18 * log(0.37 * H / z0)
25 n = H^(1/6) / C
26
27 Parameters
28 ----------
29 H : float
30 Water depth in meters.
31 z0 : float, optional
32 Roughness length in meters (default: 0.003).
33
34 Returns
35 -------
36 n : float
37 Manning coefficient (dimensionless).
38 """
39 if H <= 0:
40 raise ValueError("Water depth H must be positive.")
41
42 C = 18.0 * np.log10(0.37 * H / z0)
43 n = H ** (1 / 6) / C
44 return n
45
46
47H = 50
48manning_coefficient_value = manning_coefficient(H)
49print("Manning coefficient:", manning_coefficient_value)
50
51initial_conditions = f"""
52 static tarch::reader::NetCDFFieldParser fieldParser(
53 "bathymetry/test_R1_dx100_H{H}_b.grd",
54 PhysicalDomainSizeX,
55 PhysicalDomainSizeY,
56 PhysicalDomainOffsetX,
57 PhysicalDomainOffsetY,
58 "z",
59 "x",
60 "y"
61 );
62
63 // TODO: This sampling is needed to make the square domain works without NaNs. However, it gives unphysical solutions.
64 // The setup should be a rectangular domain, but this breaks limiting. The limiter needs to be fixed first.
65 const double sampleX =
66 x(0) < PhysicalDomainOffsetX ? PhysicalDomainOffsetX :
67 (x(0) > PhysicalDomainOffsetX + PhysicalDomainSizeX ? PhysicalDomainOffsetX + PhysicalDomainSizeX : x(0));
68 const double sampleY =
69 x(1) < PhysicalDomainOffsetY ? PhysicalDomainOffsetY :
70 (x(1) > PhysicalDomainOffsetY + PhysicalDomainSizeY ? PhysicalDomainOffsetY + PhysicalDomainSizeY : x(1));
71
72 const double bathymetry = fieldParser.sampleTopology(sampleX, sampleY);
73 Q[Shortcuts::h] = -std::min(bathymetry, 0.0);
74 Q[Shortcuts::hu] = 0.0;
75 Q[Shortcuts::hv] = 0.0;
76 Q[Shortcuts::z] = bathymetry;
77"""
78
79boundary_conditions = """
80 if (normal == 0) {
81 // x-normal: left and right boundary
82 // transmissive
83 Qoutside[0] = Qinside[0];
84 Qoutside[1] = Qinside[1];
85 Qoutside[2] = Qinside[2];
86 Qoutside[3] = Qinside[3];
87 } else if (normal == 1) {
88 // y-normal: top and bottom boundary
89 // wall boundary
90 Qoutside[0] = Qinside[0];
91 Qoutside[1] = Qinside[1];
92 Qoutside[2] = -Qinside[2];
93 Qoutside[3] = Qinside[3];
94 }
95"""
96
97is_physically_admissible = """
98 if (x(0) < PhysicalDomainOffsetX or x(0) > PhysicalDomainOffsetX + PhysicalDomainSizeX) {
99 return false;
100 }
101 if (x(1) < PhysicalDomainOffsetY or x(1) > PhysicalDomainOffsetY + PhysicalDomainSizeY) {
102 return false;
103 }
104
105 if (std::abs(x[0] - DomainOffset[0]) < h[0] or std::abs(x[0] - DomainOffset[0] - DomainSize[0]) < h[0]) {
106 return false;
107 }
108 if (std::abs(x[1] - DomainOffset[1]) < h[1] or std::abs(x[1] - DomainOffset[1] - DomainSize[1]) < h[1]) {
109 return false;
110 }
111
112 if (x(0) - PhysicalDomainOffsetX > 0.85 * PhysicalDomainSizeX) {
113 return false;
114 }
115 if (x(0) - PhysicalDomainOffsetX < 0.15 * PhysicalDomainSizeX) {
116 return false;
117 }
118
119 return true;
120"""
121
122parser = exahype2.ArgumentParser()
123parser.set_defaults(
124 min_depth=2,
125 end_time=100.0, # 13200.0,
126 degrees_of_freedom=7,
127)
128args = parser.parse_args()
129
130constants = {
131 "g": [9.81, "double"],
132 "hThreshold": [1e-5, "double"],
133 "PhysicalDomainOffsetX": [physical_offset[0], "double"],
134 "PhysicalDomainOffsetY": [physical_offset[1], "double"],
135 "PhysicalDomainSizeX": [physical_size[0], "double"],
136 "PhysicalDomainSizeY": [physical_size[1], "double"],
137 "water_density": [1000, "double"],
138 "manning_param": [manning_coefficient_value, "double"],
139 "corriolis_param": [9.35e-5, "double"],
140 "pressure": [100, "double"],
141 "speed_pressure": [22.15, "double"],
142 "period_pressure": [1800, "double"],
143 "background_pressure": [101300, "double"],
144}
145
146args.min_depth = 2
147max_h = 1.1 * min(physical_size) / (3.0**args.min_depth)
148min_h = max_h * 3.0 ** (-args.amr_levels)
149dg_order = args.degrees_of_freedom - 1
150
151aderdg_solver = exahype2.solvers.aderdg.GlobalAdaptiveTimeStep(
152 name="ADERDGSolver",
153 order=dg_order,
154 unknowns={"h": 1, "hu": 1, "hv": 1, "z": 1},
155 auxiliary_variables=0,
156 min_cell_h=min_h,
157 max_cell_h=max_h,
158 time_step_relaxation=0.9,
159)
160
161aderdg_solver.set_implementation(
162 initial_conditions=initial_conditions,
163 boundary_conditions=boundary_conditions,
164 flux=f"ShallowWater::flux<double, Shortcuts, {aderdg_solver.unknowns}>(Q, x, h, t, dt, normal, F);",
165 ncp=f"ShallowWater::nonconservativeProduct<double, Shortcuts, {aderdg_solver.unknowns}>(Q, deltaQ, x, h, t, dt, normal, BTimesDeltaQ);",
166 max_eigenvalue="return ShallowWater::maxEigenvalue<double, Shortcuts>(Q,x,h,t,dt,normal);",
167 riemann_solver="""
168 // Compute max eigenvalue over face
169 double smax = 0.0;
170 for(int xy=0; xy<Order+1; xy++){
171 smax = std::max(smax, maxEigenvalue(&QL[xy*4], x, h, t, dt, direction));
172 smax = std::max(smax, maxEigenvalue(&QR[xy*4], x, h, t, dt, direction));
173 }
174
175 for(int xy=0; xy<Order+1; xy++){
176 if (QL[xy*4+0] <= hThreshold or QR[xy*4+0] <= hThreshold) {
177 FR[xy*4+0] = FL[xy*4+0] = 0.0;
178 FR[xy*4+1] = FL[xy*4+1] = 0.0;
179 FR[xy*4+2] = FL[xy*4+2] = 0.0;
180 FR[xy*4+3] = FL[xy*4+3] = 0.0;
181 continue;
182 }
183
184 // h gets added term from bathymetry, u and v are regular Rusanov, b does not change
185 FL[xy*4+0] = 0.5*(FL[xy*4+0]+FR[xy*4+0] + smax*(QL[xy*4+0]+QL[xy*4+3]-QR[xy*4+0]-QR[xy*4+3]) );
186 FL[xy*4+1] = 0.5*(FL[xy*4+1]+FR[xy*4+1] + smax*(QL[xy*4+1]-QR[xy*4+1]) );
187 FL[xy*4+2] = 0.5*(FL[xy*4+2]+FR[xy*4+2] + smax*(QL[xy*4+2]-QR[xy*4+2]) );
188 FL[xy*4+3] = 0.0;
189
190 FR[xy*4+0] = FL[xy*4+0];
191 FR[xy*4+1] = FL[xy*4+1];
192 FR[xy*4+2] = FL[xy*4+2];
193 FR[xy*4+3] = 0.0;
194
195 // Contribution from NCP
196 FL[xy*4+direction+1] += 0.5*g*0.5*(QL[xy*4+0]+QR[xy*4+0])*(QR[xy*4+3]+QR[xy*4+0]-QL[xy*4+3]-QL[xy*4+0]);
197 FR[xy*4+direction+1] -= 0.5*g*0.5*(QL[xy*4+0]+QR[xy*4+0])*(QR[xy*4+3]+QR[xy*4+0]-QL[xy*4+3]-QL[xy*4+0]);
198 }
199 """,
200 source_term=f"meteotsunami::sourceTerm<double, Shortcuts, {aderdg_solver.unknowns}, {aderdg_solver.auxiliary_variables}>(Q, x, h, t, dt, S);",
201)
202
203aderdg_solver.add_user_solver_includes(
204 """
205#include "../PDE.h"
206#include "SourceTerm.h"
207#include "tarch/reader/NetCDFFieldParser.h"
208 """
209)
210
211fv_solver = exahype2.solvers.fv.godunov.GlobalAdaptiveTimeStep(
212 name="FVSolver",
213 patch_size=dg_order * 2 + 1,
214 unknowns={"h": 1, "hu": 1, "hv": 1},
215 auxiliary_variables={"z": 1},
216 min_volume_h=min_h,
217 max_volume_h=max_h,
218 time_step_relaxation=0.9,
219)
220
221fv_solver.set_implementation(
222 initial_conditions=initial_conditions,
223 boundary_conditions=boundary_conditions,
224 source_term=f"meteotsunami::sourceTerm<double, Shortcuts, {fv_solver.unknowns}, {fv_solver.auxiliary_variables}>(Q, x, h, t, dt, S);",
225 riemann_solver="return fWaveRiemannSolver<double, Shortcuts>(QL, QR, x, h, t, dt, normal, FL, FR);",
226)
227
228fv_solver.add_user_solver_includes(
229 """
230#include "../FWaveRiemannSolver.h"
231#include "SourceTerm.h"
232#include "tarch/reader/NetCDFFieldParser.h"
233 """
234)
235
236limiter_solver = exahype2.solvers.limiting.StaticLimiting(
237 name="LimiterSolver",
238 regular_solver=aderdg_solver,
239 limiting_solver=fv_solver,
240 physical_admissibility_criterion=is_physically_admissible,
241)
242
243project = exahype2.Project(
244 namespace=["applications", "exahype2", "ShallowWater"],
245 project_name="Meteotsunami",
246 directory=".",
247 executable="ExaHyPE",
248)
249
250project.add_solver(aderdg_solver)
251project.add_solver(fv_solver)
252project.add_solver(limiter_solver)
253
254if args.number_of_snapshots <= 0:
255 time_in_between_plots = 0.0
256else:
257 time_in_between_plots = args.end_time / args.number_of_snapshots
258 project.set_output_path(args.output)
259
260project.set_global_simulation_parameters(
261 dimensions=2,
262 size=size,
263 offset=offset,
264 min_end_time=args.end_time,
265 max_end_time=args.end_time,
266 first_plot_time_stamp=0.0,
267 time_in_between_plots=time_in_between_plots,
268 periodic_BC=[
269 args.periodic_boundary_conditions_x,
270 args.periodic_boundary_conditions_y,
271 ],
272)
273
274project.set_build_mode(mode=peano4.output.string_to_mode(args.build_mode))
275project = project.generate_Peano4_project(verbose=False)
276for const_name, const_info in constants.items():
277 const_val, const_type = const_info
278 project.constants.export_constexpr_with_type(const_name, str(const_val), const_type)
279project.output.makefile.set_target_device(args.target_device)
280project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)
float manning_coefficient(float H, float z0=0.003)
Compute the Manning coefficient n based on the Chezy formulation: