Skip to content

Commit 3299cb6

Browse files
authored
Merge pull request NHERI-SimCenter#398 from yisangriB/master
sy - polishing hurricane generator
2 parents f03b6f6 + b56acd4 commit 3299cb6

8 files changed

Lines changed: 36492 additions & 17717 deletions

File tree

modules/performRegionalEventSimulation/regionalWindField/ComputeIntensityMeasure.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ def simulate_storm_cpp( # noqa: C901, D103
156156
][1]
157157
# updating landfall properties
158158
scenario_info['Storm']['LandingAngle'] = scenario_data[0]['CycloneParam'][2]
159-
scenario_info['Storm']['Pressure'] = scenario_data[0]['CycloneParam'][3]
160-
scenario_info['Storm']['Speed'] = scenario_data[0]['CycloneParam'][4]
161-
scenario_info['Storm']['Radius'] = scenario_data[0]['CycloneParam'][5]
159+
scenario_info['Storm']['Pressure'] = scenario_data[0]['Pressure']
160+
scenario_info['Storm']['Speed'] = scenario_data[0]['Speed']
161+
scenario_info['Storm']['Radius'] = scenario_data[0]['Radius']
162162

163163
config = {'Scenario': scenario_info, 'Event': event_info}
164164
abs_path_config = os.path.abspath(os.path.join(input_dir, 'SimuConfig.json')) # noqa: PTH100, PTH118
@@ -191,27 +191,34 @@ def simulate_storm_cpp( # noqa: C901, D103
191191
)
192192
df = pd.DataFrame.from_dict( # noqa: PD901
193193
{
194-
'Lat': scenario_data[0]['TrackSimu'],
194+
'Lat': scenario_data[0]['TrackSimu']['Latitude'],
195+
'Lon': scenario_data[0]['TrackSimu']['Longitude'],
195196
}
196197
)
197198
df.to_csv(abs_path_latw, sep=',', header=False, index=False)
199+
198200
if scenario_info['Generator'] == 'SimulationHist':
199201
df = pd.DataFrame.from_dict( # noqa: PD901
200202
{
201-
'Lat': scenario_data[0]['TrackSimu'],
203+
'Lat': scenario_data[0]['TrackSimu']['Latitude'],
204+
'Lon': scenario_data[0]['TrackSimu']['Longitude'],
202205
}
203206
)
207+
204208
df.to_csv(abs_path_latw, sep=',', header=False, index=False)
205209
# terrain file
206210
if 'Terrain' in scenario_info.keys(): # noqa: SIM118
211+
file_name = os.path.basename(scenario_info['Terrain'])
212+
207213
abs_path_terrain = os.path.abspath( # noqa: PTH100
208-
os.path.join(input_dir, scenario_info['Terrain']) # noqa: PTH118
214+
os.path.join(input_dir, file_name) # noqa: PTH118
209215
)
210216
else:
211217
# default terrain z0 = 0.01 everywhere for the defined domain
212218
abs_path_terrain = os.path.abspath( # noqa: PTH100
213219
os.path.join(input_dir, 'DefaultTerrain.geojson') # noqa: PTH118
214220
)
221+
215222
dict_dt = {
216223
'type': 'FeatureCollection',
217224
'features': [
@@ -292,6 +299,7 @@ def simulate_storm_cpp( # noqa: C901, D103
292299
if os.path.exists(output_subdir): # noqa: PTH110
293300
shutil.rmtree(output_subdir)
294301
os.makedirs(output_subdir) # noqa: PTH103
302+
295303
args = [
296304
windsimu_bin,
297305
'--config',
@@ -310,19 +318,26 @@ def simulate_storm_cpp( # noqa: C901, D103
310318
output_subdir,
311319
'--output',
312320
output_subdir,
313-
]
321+
]
322+
314323

315324
pert_list.append(abs_path_pert)
316325
args_list.append(args)
317326
odir_list.append(output_subdir)
318327
# running
319328
print('ComputeIntensityMeaure: running analysis.') # noqa: T201
329+
print(args_list) # noqa: T201
320330
procs_list = [
321331
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # noqa: S603
322332
for cmd in args_list
323333
]
324334
for proc in procs_list:
325-
proc.communicate()
335+
stdout, stderr = proc.communicate() # Wait for the process to finish and get its output
336+
if proc.returncode == 0:
337+
print(f"Output of process (PID {proc.pid}): {stdout.decode()}")
338+
else:
339+
print(f"Error in process (PID {proc.pid}): {stderr.decode()}")
340+
326341
# loading output
327342
print('ComputeIntensityMeaure: postprocessing simulation data.') # noqa: T201
328343
for j in range(num_per_site):
@@ -352,9 +367,9 @@ def simulate_storm_cpp( # noqa: C901, D103
352367
)
353368
station_res['PWS']['windspeed'] = df.values.tolist() # noqa: PD011
354369
res.append(station_res)
355-
shutil.rmtree(odir_list[j])
370+
#shutil.rmtree(odir_list[j])
356371
# house-keeping
357-
os.remove(abs_path_config) # noqa: PTH107
372+
#os.remove(abs_path_config) # noqa: PTH107
358373
else:
359374
print( # noqa: T201
360375
'ComputeIntensityMeasure: currently only supporting LinearAnalytical model'
@@ -419,13 +434,13 @@ def convert_wind_speed(event_info, simu_res): # noqa: D103
419434
zg_t = 274.32
420435
# conversion
421436
pws_raw = interp_wind_by_height(pws_raw, measure_height, reference_height)
422-
print(np.max(pws_raw)) # noqa: T201
437+
# print(np.max(pws_raw)) # noqa: T201
423438
# computing gradient-height wind speed
424439
pws_tmp = pws_raw * (zg / reference_height) ** (1.0 / alpha)
425440
# converting exposure
426441
pws_tmp = pws_tmp * (reference_height / zg_t) ** (1.0 / alpha_t)
427442
pws = pws_tmp * gust_factor_ESDU(gust_duration_simu, gust_duration)
428-
print(np.max(pws)) # noqa: T201
443+
# print(np.max(pws)) # noqa: T201
429444
# appending to pws_mr
430445
pws_mr.append(pws)
431446

modules/performRegionalEventSimulation/regionalWindField/CreateScenario.py

Lines changed: 152 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noqa: C901, D103
4848
# Number of scenarios
4949
source_num = scenario_info.get('Number', 1)
50+
5051
# Directly defining earthquake ruptures
5152
if scenario_info['Generator'] == 'Simulation':
5253
# Collecting site locations
@@ -81,9 +82,16 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
8182
header=None,
8283
index_col=None,
8384
)
84-
track_simu = df.iloc[:, 0].values.tolist() # noqa: PD011
85+
#track_simu = df.iloc[:, 0].values.tolist() # noqa: PD011
86+
track_simu = {
87+
'Latitude': df.iloc[:, 0].values.tolist(), # noqa: PD011
88+
'Longitude': df.iloc[:, 1].values.tolist(), # noqa: PD011
89+
}
8590
else:
86-
track_simu = track['Latitude']
91+
#track_simu = track['Latitude']
92+
track_simu = track
93+
94+
8795
# Reading Terrain info (if provided)
8896
terrain_file = scenario_info.get('Terrain', None)
8997
if terrain_file:
@@ -97,11 +105,21 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
97105
param.append(scenario_info['Storm']['Landfall']['Latitude'])
98106
param.append(scenario_info['Storm']['Landfall']['Longitude'])
99107
param.append(scenario_info['Storm']['Landfall']['LandingAngle'])
100-
param.append(scenario_info['Storm']['Landfall']['Pressure'])
101-
param.append(scenario_info['Storm']['Landfall']['Speed'])
102-
param.append(scenario_info['Storm']['Landfall']['Radius'])
108+
#param.append(scenario_info['Storm']['Landfall']['Pressure'])
109+
#param.append(scenario_info['Storm']['Landfall']['Speed'])
110+
#param.append(scenario_info['Storm']['Landfall']['Radius'])
111+
landfall_prs = 1013.0 - float(scenario_info['Storm']['Landfall']['Pressure'])
112+
landfall_spd = float(scenario_info['Storm']['Landfall']['Speed'])* 1.852 # from kts to km/h
113+
landfall_rad = float(scenario_info['Storm']['Landfall']['Radius'])* 1.852 # from nmile to km
103114
except: # noqa: E722
104115
print('CreateScenario: please provide all needed landfall properties.') # noqa: T201
116+
117+
118+
119+
track_simu,dummy,dummy,dummy = interpolate_track(track_simu)
120+
prs_list, spd_list, rad_list = landfall2track(landfall_prs, landfall_spd, landfall_rad, track_simu)
121+
122+
105123
# Monte-Carlo
106124
# del_par = [0, 0, 0] # default
107125
# Parsing mesh configurations
@@ -117,6 +135,9 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
117135
i: {
118136
'Type': 'Wind',
119137
'CycloneParam': param,
138+
'Pressure': prs_list,
139+
'Speed': spd_list,
140+
'Radius': rad_list,
120141
'StormTrack': track,
121142
'StormMesh': mesh_info,
122143
'Terrain': terrain_data,
@@ -212,6 +233,7 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
212233
tmploc = dist2land.index(
213234
0
214235
) # the first landing point in case the storm sway back and forth
236+
215237
# simulation track
216238
track_simu_file = scenario_info['Storm'].get('TrackSimu', None)
217239
if track_simu_file:
@@ -221,20 +243,30 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
221243
header=None,
222244
index_col=None,
223245
)
224-
track_simu = df.iloc[:, 0].values.tolist() # noqa: PD011
246+
#track_simu = df.iloc[:, 0].values.tolist() # noqa: PD011
247+
248+
track_simu = {
249+
'Latitude': df.iloc[:, 0].values.tolist(), # noqa: PD011
250+
'Longitude': df.iloc[:, 1].values.tolist(), # noqa: PD011
251+
}
252+
225253
except: # noqa: E722
226254
print( # noqa: T201
227255
'CreateScenario: warning - TrackSimu file not found, using the full track.'
228256
)
229-
track_simu = track_lat
257+
#track_simu = track_lat
258+
track_simu = track
230259
else:
231260
print( # noqa: T201
232261
'CreateScenario: warning - no truncation defined, using the full track.'
233262
)
234263
# tmp = track_lat
235264
# track_simu = tmp[max(0, tmploc - 5): len(dist2land) - 1]
236265
# print(track_simu)
237-
track_simu = track_lat
266+
#track_simu = track_lat
267+
track_simu = track
268+
269+
238270
# Reading data
239271
try:
240272
landfall_lat = float(df_chs[('USA_LAT', 'degrees_north')].iloc[tmploc]) # noqa: RUF031, RUF100
@@ -249,46 +281,71 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
249281
print('CreateScenario: error - no landing angle is found.') # noqa: T201
250282
if landfall_ang > 180.0: # noqa: PLR2004
251283
landfall_ang = landfall_ang - 360.0
252-
landfall_prs = (
253-
1013.0
254-
- np.min(
255-
[
256-
float(x)
257-
for x in df_chs[('USA_PRES', 'mb')] # noqa: PD011, RUF031, RUF100
258-
.iloc[tmploc - 5 :]
259-
.values.tolist()
260-
if x != ' '
261-
]
262-
)
263-
)
264-
landfall_spd = (
265-
float(df_chs[('STORM_SPEED', 'kts')].iloc[tmploc]) * 0.51444 # noqa: RUF031, RUF100
266-
) # convert knots/s to km/s
284+
# prs_list = (
285+
# 1013.0
286+
# - np.min(
287+
# [
288+
# float(x)
289+
# for x in df_chs[('USA_PRES', 'mb')] # noqa: PD011, RUF031, RUF100
290+
# .iloc[:]
291+
# .values.tolist()
292+
# if x != ' '
293+
# ]
294+
# )
295+
# )
296+
297+
air_pressure = df_chs[('USA_PRES', 'mb')].iloc[:].values.tolist()
298+
prs_list = []
299+
for x in air_pressure:
300+
if x == ' ':
301+
prs_list += [0] # no record == insignificant dp
302+
else:
303+
prs_list += [1013.0 - float(x)]
304+
305+
306+
# prs_list = [1013.0 - float(x) for x in df_chs[('USA_PRES', 'mb')].iloc[:].values.tolist() ]
307+
308+
# spd_list = (
309+
# # float(df_chs[('STORM_SPEED', 'kts')].iloc[tmploc]) * 0.51444 # noqa: RUF031, RUF100
310+
# float(df_chs[('STORM_SPEED', 'kts')].iloc[:]) * 0.00051444 # noqa: RUF031, RUF100
311+
# ) # convert knots to km/h // TODO: check if it was intended to be km/s or m/s. If it should be km/s this is correct.
312+
313+
spd_list = [float(x)* 1.852 for x in df_chs[('STORM_SPEED', 'kts')].iloc[:].values.tolist() ]
314+
267315
try:
268-
landfall_rad = (
269-
float(df_chs[('USA_RMW', 'nmile')].iloc[tmploc]) * 1.60934 # noqa: RUF031, RUF100
270-
) # convert nmile to km
316+
# rad_list = (
317+
# float(df_chs[('USA_RMW', 'nmile')].iloc[:]) * 1.852 # noqa: RUF031, RUF100
318+
# ) # convert nmile to km // sy - from nmile to km is 1.852 not 1.609
319+
rad_list = [float(x)* 1.852 for x in df_chs[('USA_RMW', 'nmile')].iloc[:].values.tolist() ]
320+
271321
except: # noqa: E722
272322
# No available radius of maximum wind is found
273323
print('CreateScenario: warning - switching to REUNION_RMW.') # noqa: T201
274324
try:
275325
# If the default option (USA_RMW) is not available, switching to REUNION_RMW
276-
landfall_rad = (
277-
float(df_chs[('REUNION_RMW', 'nmile')].iloc[tmploc]) * 1.60934 # noqa: RUF031, RUF100
278-
) # convert nmile to km
326+
# rad_list = (
327+
# float(df_chs[('REUNION_RMW', 'nmile')].iloc[:]) * 1.852# noqa: RUF031, RUF100
328+
# ) # convert nmile to km // sy - from nmile to km is 1.852 not 1.609
329+
rad_list = [float(x)* 1.852 for x in df_chs[('REUNION_RMW', 'nmile')].iloc[:].values.tolist() ]
330+
279331
except: # noqa: E722
280332
# No available radius of maximum wind is found
281333
print( # noqa: T201
282334
'CreateScenario: warning - no available radius of maximum wind is found, using a default 50 km.'
283335
)
284-
landfall_rad = 50
336+
rad_list = 50
337+
338+
339+
track_simu, prs_list, spd_list, rad_list = interpolate_track(track_simu, prs_list, spd_list, rad_list )
340+
341+
285342
param = []
286343
param.append(landfall_lat)
287344
param.append(landfall_lon)
288345
param.append(landfall_ang)
289-
param.append(landfall_prs)
290-
param.append(landfall_spd)
291-
param.append(landfall_rad)
346+
#param.append(prs_list)
347+
#param.append(spd_list)
348+
#param.append(rad_list)
292349
# Monte-Carlo
293350
# del_par = [0, 0, 0] # default
294351
# Parsing mesh configurations
@@ -304,6 +361,9 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
304361
i: {
305362
'Type': 'Wind',
306363
'CycloneParam': param,
364+
'Pressure': prs_list,
365+
'Speed': spd_list,
366+
'Radius': rad_list,
307367
'StormTrack': track,
308368
'StormMesh': mesh_info,
309369
'Terrain': terrain_data,
@@ -318,3 +378,62 @@ def create_wind_scenarios(scenario_info, event_info, stations, data_dir): # noq
318378

319379
else:
320380
print('CreateScenario: currently only supporting Simulation generator.') # noqa: T201, RET503
381+
382+
383+
384+
def interpolate_track(track_simu, prs_list=None, spd_list=None, rad_list=None): # noqa: C901, D103
385+
386+
# if maximum of (dlat, dlong) is larger than the threshold value, we will add more intermediate points
387+
388+
track_lat = track_simu['Latitude']
389+
track_long = track_simu['Longitude']
390+
thres = 0.1
391+
392+
track_lat_new = [track_lat[0]]
393+
track_long_new = [track_long[0]]
394+
395+
if prs_list is not None:
396+
prs_new = [prs_list[0]]
397+
spd_new = [spd_list[0]]
398+
rad_new = [rad_list[0]]
399+
else:
400+
prs_new, spd_new, rad_new = None, None, None
401+
402+
for nn in range(len(track_lat)-1):
403+
dlat = (track_lat[nn+1]-track_lat[nn])
404+
dlon = (track_long[nn+1]-track_long[nn])
405+
406+
if prs_list is not None:
407+
dp = (prs_list[nn+1]-prs_list[nn])
408+
ds = (spd_list[nn+1]-spd_list[nn])
409+
dr = (rad_list[nn+1]-rad_list[nn])
410+
411+
412+
npoints = int(max(dlat, dlon)//thres) + 1 # // is same as Ceil: This should be 1 if interval is sufficiently small
413+
for np in range(npoints):
414+
track_lat_new += [track_lat[nn] + dlat/npoints*(np+1)]
415+
track_long_new += [track_long[nn] + dlon/npoints*(np+1)]
416+
417+
if prs_list is not None:
418+
prs_new += [prs_list[nn] + dp/npoints*(np+1)]
419+
spd_new += [spd_list[nn] + ds/npoints*(np+1)]
420+
rad_new += [rad_list[nn] + dr/npoints*(np+1)]
421+
422+
track_simu_interpolated = {
423+
'Latitude': track_lat_new,
424+
'Longitude': track_long_new
425+
}
426+
427+
return track_simu_interpolated, prs_new, spd_new, rad_new
428+
429+
430+
def landfall2track(landfall_prs, landfall_spd, landfall_rad, track_simu):
431+
432+
# TODO filling model should replace this function
433+
#print(landfall_prs)
434+
#print(track_simu)
435+
prs_list = [landfall_prs] * len(track_simu['Latitude'])
436+
psd_list = [landfall_spd] * len(track_simu['Latitude'])
437+
rad_list = [landfall_rad] * len(track_simu['Latitude'])
438+
439+
return prs_list, psd_list, rad_list,

modules/performRegionalEventSimulation/regionalWindField/HurricaneSimulation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
if scenario_info['Type'] == 'Wind':
111111
if 'Simulation' in scenario_info['Generator']:
112112
if scenario_info['ModelType'] == 'LinearAnalyticalPy':
113+
# TODO: sy - the below python code need to be updated because now scenarios['track_simu'] contains both latitude and longitude. Only c++ code updated for now
113114
# simulating storm
114115
storm_simu = simulate_storm( # noqa: F405
115116
scenarios, event_info, 'LinearAnalytical'

0 commit comments

Comments
 (0)