You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
5.1 KiB
Python
149 lines
5.1 KiB
Python
3 years ago
|
import os
|
||
|
import json
|
||
|
import yaml
|
||
|
import numpy as np
|
||
|
import pandas as pd
|
||
|
from scipy.optimize import curve_fit
|
||
|
|
||
|
fname = 'adopted-input-values.xlsx'
|
||
|
output_path = '../probabilistic-analysis'
|
||
|
|
||
|
# Load input file
|
||
|
df = pd.read_excel(fname)
|
||
|
|
||
|
# Remove calculation columns (headers that begin with #)
|
||
|
df = df.drop([c for c in df.columns if c.startswith('#')], axis=1)
|
||
|
|
||
|
# Parse lists and other objects in dataframe
|
||
|
for col_name in [c for c in df.columns if df[c].dtype == 'O']:
|
||
|
parsed_vals = []
|
||
|
for i, cell_contents in enumerate(df[col_name]):
|
||
|
if type(cell_contents) is not str:
|
||
|
parsed_vals.append([])
|
||
|
else:
|
||
|
try:
|
||
|
parsed_vals.append(json.loads(cell_contents))
|
||
|
except json.decoder.JSONDecodeError as e:
|
||
|
raise Exception(
|
||
|
('\n\nInvalid JSON string in column "{}" on row {}: {}'
|
||
|
'\nEnsure brackets are matched, and strings are '
|
||
|
'double-quoted, e.g. ["A", "B", "C"]\n').format(
|
||
|
col_name, i + 2,
|
||
|
cell_contents)).with_traceback(e.__traceback__)
|
||
|
|
||
|
df[col_name] = parsed_vals
|
||
|
|
||
|
df = df.rename(columns={col_name: col_name.replace('#list', '')})
|
||
|
|
||
|
parameters = {
|
||
|
'beach_scenario':
|
||
|
'# Beach scenrio name including north/south or other special conditions',
|
||
|
'beach_name': '# Beach name as it appears in photgrammetry database',
|
||
|
'n_runs': '# Number of runs for Monte Carlo simulation',
|
||
|
'start_year': '# Beginning of period for simulation',
|
||
|
'end_year': '# End of period for simulation',
|
||
|
'output_years': '# Years used for reporting',
|
||
|
'output_ep': '# Encounter probability values for reporting',
|
||
|
'output_folder': '# Path to output folder',
|
||
|
'figure_folder': '# Path to figure folder',
|
||
|
'zsa_profile_file':
|
||
|
'# Path to storm demand file (zone of slope adjustment)',
|
||
|
'zrfc_profile_file':
|
||
|
'# Path to storm demand file (zone of reduced foundation capacity)',
|
||
|
'sea_level_rise': '# Projected sea levels (m)',
|
||
|
'storm_demand':
|
||
|
'# Storm demand annual recurrance intervals (years) and volumes (m3/m)',
|
||
|
'bruun_factor': '# Bruun factor (-)',
|
||
|
'underlying_recession': '# Underlying shoreline recession rate (m/year)',
|
||
|
'diagnostics': '# Choose profiles to output probabilistic diagnostics',
|
||
|
'omit': '# Choose profiles to omit from analysis',
|
||
|
'min_chainage':
|
||
|
'# Set minimum chainages for non-erodable sections of profiles',
|
||
|
'segment_gaps':
|
||
|
'# Break shoreline into multiple segments (with a gap at this profile)',
|
||
|
'insert_points': '# Insert points before specific profile',
|
||
|
'append_points': '# Add points after specific profile',
|
||
|
}
|
||
|
|
||
|
|
||
|
def gordon_log_fit(ari, p1, p2):
|
||
|
"""
|
||
|
Fit log curve based on ARI values. Gordon (1987) provides two cases:
|
||
|
1. Low demand, open beaches:
|
||
|
p1 = 5
|
||
|
p2 = 30
|
||
|
2. High demand, rip heads
|
||
|
p1 = 40
|
||
|
p2 = 40
|
||
|
|
||
|
Args:
|
||
|
ari (array_like): input array of ARI values
|
||
|
p1 (float): parameter 1
|
||
|
p2 (float): parameter 2
|
||
|
Returns:
|
||
|
the fitted values for the given ARIs
|
||
|
"""
|
||
|
|
||
|
return p1 + p2 * np.log(ari)
|
||
|
|
||
|
|
||
|
def get_storm_demand(x):
|
||
|
"""
|
||
|
Get storm demand based on a 100 year ARI storm demand volume.
|
||
|
|
||
|
Args:
|
||
|
x (float): storm demand volume for 100 year ARI event
|
||
|
Returns:
|
||
|
storm demand volumes for 1, 10, 100, 1000 year ARI events
|
||
|
"""
|
||
|
if x > 140:
|
||
|
# Repeat values to supress scipy covariance warning
|
||
|
ref_ari = np.array([0.03, 0.03, 100, 100])
|
||
|
ref_vol = np.array([-100, -100, x, x])
|
||
|
else:
|
||
|
# Repeat values to supress scipy covariance warning
|
||
|
ref_ari = np.array([1, 1, 100, 100])
|
||
|
ref_vol = np.array([1, 1, x, x])
|
||
|
|
||
|
ari = np.array([1, 10, 100, 1000])
|
||
|
try:
|
||
|
p, _ = curve_fit(gordon_log_fit, ref_ari, ref_vol, p0=(5., 30.))
|
||
|
vol = gordon_log_fit(ari, *p)
|
||
|
except ValueError:
|
||
|
vol = ari * np.nan
|
||
|
|
||
|
return ari, vol
|
||
|
|
||
|
|
||
|
# Calculate storm demand volumes
|
||
|
df = df.assign(storm_demand_ari=0).astype('object')
|
||
|
df = df.assign(storm_demand_vol=0).astype('object')
|
||
|
|
||
|
for i in range(len(df)):
|
||
|
vol_ari_100 = df['storm_demand'][i]
|
||
|
ari, vol = get_storm_demand(vol_ari_100)
|
||
|
|
||
|
df.at[i, 'storm_demand_ari'] = ari.tolist()
|
||
|
df.at[i, 'storm_demand_vol'] = vol.round(1).tolist()
|
||
|
|
||
|
# Remove original storm demand column
|
||
|
df = df.drop('storm_demand', axis=1)
|
||
|
|
||
|
for i, row in df.sort_values(by='beach_scenario').iterrows():
|
||
|
output_file = os.path.join(output_path, row['beach_scenario'] + '.yaml')
|
||
|
with open(output_file, 'w') as f:
|
||
|
f.write('# {}\n'.format(row['beach_scenario']))
|
||
|
|
||
|
for parameter, description in parameters.items():
|
||
|
col_names = [c for c in df.columns if parameter in c]
|
||
|
|
||
|
# Write parameter description
|
||
|
f.write('\n{}\n'.format(description))
|
||
|
|
||
|
# Collect and write parameter values
|
||
|
values = {c.split('_')[-1]: row[c] for c in col_names}
|
||
|
if len(values) == 1:
|
||
|
# Remove from dictionary if parameter just has one value
|
||
|
values = list(values.values())[0]
|
||
|
yaml.dump({parameter: values}, f, default_flow_style=False)
|