From 39fa8662b9dcea8feef6a338efef2fd9a42124e8 Mon Sep 17 00:00:00 2001 From: Dan Howe Date: Mon, 9 May 2022 12:59:14 +0200 Subject: [PATCH] Add 'get_adopted_input_values.py' --- inputs/get_adopted_input_values.py | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 inputs/get_adopted_input_values.py diff --git a/inputs/get_adopted_input_values.py b/inputs/get_adopted_input_values.py new file mode 100644 index 0000000..77875f0 --- /dev/null +++ b/inputs/get_adopted_input_values.py @@ -0,0 +1,148 @@ +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)