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
0 commit comments