Skip to content

Commit a7ba482

Browse files
authored
Merge pull request #146 from GeoOcean/GreenWaves
Green waves
2 parents 4c2bbfe + 5c707a2 commit a7ba482

8 files changed

Lines changed: 2658 additions & 2374 deletions

File tree

bluemath_tk/additive/additive.py

Lines changed: 1662 additions & 0 deletions
Large diffs are not rendered by default.

bluemath_tk/additive/greensurge.py

Lines changed: 134 additions & 1941 deletions
Large diffs are not rendered by default.

bluemath_tk/additive/greenwaves.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
from functools import lru_cache
2+
import struct
3+
import numpy as np
4+
import xarray as xr
5+
from tqdm import tqdm
6+
7+
def greenwaves_wind_setup_reconstruction_raw(
8+
greensurge_dataset: str,
9+
ds_GFD_info_update: xr.Dataset,
10+
xds_vortex_interp: xr.Dataset,
11+
) -> xr.Dataset:
12+
"""
13+
Compute the GreenSurge wind contribution and return an xarray Dataset with the results.
14+
15+
Parameters
16+
----------
17+
greensurge_dataset : str
18+
Path to the GreenSurge dataset directory.
19+
ds_GFD_info_update : xr.Dataset
20+
Updated GreenSurge information dataset.
21+
xds_vortex_interp : xr.Dataset
22+
Interpolated vortex dataset.
23+
24+
Returns
25+
-------
26+
xr.Dataset
27+
Dataset containing the GreenSurge wind setup contribution.
28+
"""
29+
30+
ds_gfd_metadata = ds_GFD_info_update
31+
wind_direction_input = xds_vortex_interp
32+
velocity_thresholds = np.array([0, 100, 100])
33+
drag_coefficients = np.array([0.00063, 0.00723, 0.00723])
34+
35+
direction_bins = ds_gfd_metadata.wind_directions.values
36+
forcing_cell_indices = ds_gfd_metadata.element_forcing_index.values
37+
wind_speed_reference = ds_gfd_metadata.wind_speed.values.item()
38+
base_drag_coeff = GS_LinearWindDragCoef(
39+
wind_speed_reference, drag_coefficients, velocity_thresholds
40+
)
41+
time_step_hours = ds_gfd_metadata.time_step_hours.values
42+
43+
time_start = wind_direction_input.time.values.min()
44+
time_end = wind_direction_input.time.values.max()
45+
duration_in_steps = (
46+
int((ds_gfd_metadata.simulation_duration_hours.values) / time_step_hours) + 1
47+
)
48+
output_time_vector = np.arange(
49+
time_start, time_end, np.timedelta64(int(time_step_hours * 60), "m")
50+
)
51+
num_output_times = len(output_time_vector)
52+
53+
direction_data = wind_direction_input.Dir.values
54+
wind_speed_data = wind_direction_input.W.values
55+
56+
sample_path = (
57+
f"{greensurge_dataset}/GF_T_0_D_0/output.raw"
58+
)
59+
sample_data = read_raw_with_header(sample_path)
60+
n_faces = sample_data.shape[-1]
61+
wind_setup_output = np.zeros((num_output_times, n_faces), dtype=np.float32)
62+
water_level_accumulator = np.zeros(sample_data.shape, dtype=np.float32)
63+
64+
for time_index in tqdm(range(num_output_times), desc="Processing time steps"):
65+
water_level_accumulator[:] = 0
66+
for cell_index in forcing_cell_indices.astype(int):
67+
current_dir = direction_data[cell_index, time_index] % 360
68+
adjusted_bins = np.where(direction_bins == 0, 360, direction_bins)
69+
closest_direction_index = np.abs(adjusted_bins - current_dir).argmin()
70+
71+
raw_path = f"{greensurge_dataset}/GF_T_{cell_index}_D_{closest_direction_index}/output.raw"
72+
water_level_case = read_raw_with_header(raw_path)
73+
74+
water_level_case = np.nan_to_num(water_level_case, nan=0)
75+
76+
wind_speed_value = wind_speed_data[cell_index, time_index]
77+
drag_coeff_value = GS_LinearWindDragCoef(
78+
wind_speed_value, drag_coefficients, velocity_thresholds
79+
)
80+
81+
scaling_factor = (wind_speed_value**2 / wind_speed_reference**2) * (
82+
drag_coeff_value / base_drag_coeff
83+
)
84+
water_level_accumulator += water_level_case * scaling_factor
85+
86+
step_window = min(duration_in_steps, num_output_times - time_index)
87+
if (num_output_times - time_index) > step_window:
88+
wind_setup_output[time_index : time_index + step_window] += (
89+
water_level_accumulator
90+
)
91+
else:
92+
shift_counter = step_window - (num_output_times - time_index)
93+
wind_setup_output[
94+
time_index : time_index + step_window - shift_counter
95+
] += water_level_accumulator[: step_window - shift_counter]
96+
97+
ds_wind_setup = xr.Dataset(
98+
{"WL": (["time", "nface"], wind_setup_output)},
99+
coords={
100+
"time": output_time_vector,
101+
"nface": np.arange(wind_setup_output.shape[1]),
102+
},
103+
)
104+
return ds_wind_setup
105+
106+
@lru_cache(maxsize=256)
107+
def read_raw_with_header(raw_path: str) -> np.ndarray:
108+
"""
109+
Read a .raw file with a 256-byte header and return a numpy float32 array.
110+
111+
Parameters
112+
----------
113+
raw_path : str
114+
Path to the .raw file.
115+
116+
Returns
117+
-------
118+
np.ndarray
119+
The data array reshaped according to the header dimensions.
120+
"""
121+
with open(raw_path, "rb") as f:
122+
header_bytes = f.read(256)
123+
dims = list(struct.unpack("4i", header_bytes[:16]))
124+
dims = [d for d in dims if d > 0]
125+
if len(dims) == 0:
126+
raise ValueError(f"{raw_path}: invalid header, no dimension > 0 found")
127+
data = np.fromfile(f, dtype=np.float32)
128+
expected_size = np.prod(dims)
129+
if data.size != expected_size:
130+
raise ValueError(
131+
f"{raw_path}: size mismatch (data={data.size}, expected={expected_size}, shape={dims})"
132+
)
133+
return np.reshape(data, dims)
134+
135+
def GS_LinearWindDragCoef(
136+
Wspeed: np.ndarray, CD_Wl_abc: np.ndarray, Wl_abc: np.ndarray
137+
) -> np.ndarray:
138+
"""
139+
Calculate the linear drag coefficient based on wind speed and specified thresholds.
140+
141+
Parameters
142+
----------
143+
Wspeed : np.ndarray
144+
Wind speed values (1D array).
145+
CD_Wl_abc : np.ndarray
146+
Coefficients for the drag coefficient calculation, should be a 1D array of length 3.
147+
Wl_abc : np.ndarray
148+
Wind speed thresholds for the drag coefficient calculation, should be a 1D array of length 3.
149+
150+
Returns
151+
-------
152+
np.ndarray
153+
Calculated drag coefficient values based on the input wind speed.
154+
"""
155+
156+
Wla = Wl_abc[0]
157+
Wlb = Wl_abc[1]
158+
Wlc = Wl_abc[2]
159+
CDa = CD_Wl_abc[0]
160+
CDb = CD_Wl_abc[1]
161+
CDc = CD_Wl_abc[2]
162+
163+
# coefs lines y=ax+b
164+
if not Wla == Wlb:
165+
a_CDline_ab = (CDa - CDb) / (Wla - Wlb)
166+
b_CDline_ab = CDb - a_CDline_ab * Wlb
167+
else:
168+
a_CDline_ab = 0
169+
b_CDline_ab = CDa
170+
if not Wlb == Wlc:
171+
a_CDline_bc = (CDb - CDc) / (Wlb - Wlc)
172+
b_CDline_bc = CDc - a_CDline_bc * Wlc
173+
else:
174+
a_CDline_bc = 0
175+
b_CDline_bc = CDb
176+
a_CDline_cinf = 0
177+
b_CDline_cinf = CDc
178+
179+
if Wspeed <= Wlb:
180+
CD = a_CDline_ab * Wspeed + b_CDline_ab
181+
elif Wspeed > Wlb and Wspeed <= Wlc:
182+
CD = a_CDline_bc * Wspeed + b_CDline_bc
183+
else:
184+
CD = a_CDline_cinf * Wspeed + b_CDline_cinf
185+
186+
return CD

bluemath_tk/wrappers/delft3d/delft3d_example.py

100755100644
File mode changed.

0 commit comments

Comments
 (0)