def fit_prob_exceed_model(hazard_input_vals, pb_exceed, SYS_DS, out_path): """ Fit a Lognormal CDF model to simulated probability exceedance data :param hazard_input_vals: input values for hazard intensity (numpy array) :param pb_exceed: probability of exceedance (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted exceedance model parameters (PANDAS dataframe) """ # DataFrame for storing the calculated System Damage Algorithms for # exceedance probabilities. indx = pd.Index(SYS_DS[1:], name='Damage States') sys_dmg_model = pd.DataFrame(index=indx, columns=['Median', 'LogStdDev', 'Location', 'Chi-Sqr']) # ----- Initial fit ----- sys_dmg_ci = [{} for _ in xrange(len(SYS_DS))] sys_dmg_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): x_sample = hazard_input_vals y_sample = pb_exceed[dx] p0m = np.mean(y_sample) p0s = np.std(y_sample) # Fit the dist: params_pe = lmfit.Parameters() params_pe.add('median', value=p0m) # , min=0, max=10) params_pe.add('logstd', value=p0s) params_pe.add('loc', value=0.0, vary=False) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) sys_dmg_model.ix[SYS_DS[dx]] \ = (sys_dmg_fit[dx].params['median'].value, sys_dmg_fit[dx].params['logstd'].value, sys_dmg_fit[dx].params['loc'].value, sys_dmg_fit[dx].chisqr) print("\n" + "-" * 79) print(Fore.YELLOW + "Fitting system FRAGILITY data: Lognormal CDF" + Fore.RESET) print("-" * 79) print("INITIAL System Fragilities:\n\n", sys_dmg_model, '\n') # ----- Check for crossover and resample as needed ----- for dx in range(1, len(SYS_DS)): x_sample = hazard_input_vals y_sample = pb_exceed[dx] mu_hi = sys_dmg_fit[dx].params['median'].value sd_hi = sys_dmg_fit[dx].params['logstd'].value loc_hi = sys_dmg_fit[dx].params['loc'].value y_model_hi = stats.lognorm.cdf(x_sample, sd_hi, loc=loc_hi, scale=mu_hi) params_pe.add('median', value=mu_hi, min=0, max=10) params_pe.add('logstd', value=sd_hi) params_pe.add('loc', value=0.0, vary=False) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) ###################################################################### if dx >= 2: mu_lo, sd_lo, loc_lo, chi = \ sys_dmg_model.ix[SYS_DS[dx - 1]].values y_model_lo = stats.lognorm.cdf(x_sample, sd_lo, loc=loc_lo, scale=mu_lo) if sum(y_model_lo - y_model_hi < 0): print(Fore.MAGENTA + "There is overlap for curve pair : " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) # Test if higher curve is co-incident with, # or precedes lower curve if (mu_hi <= mu_lo) or (loc_hi <= loc_lo): print(" *** Mean of higher curve too low: resampling") params_pe.add('median', value=mu_hi, min=mu_lo) sys_dmg_fit[dx] = lmfit.minimize( res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) (mu_hi, sd_hi, loc_hi) = \ (sys_dmg_fit[dx].params['median'].value, sys_dmg_fit[dx].params['logstd'].value, sys_dmg_fit[dx].params['loc'].value) # Thresholds for testing top or bottom crossover delta_top = (3.0 * sd_lo - (mu_hi - mu_lo)) / 3 delta_btm = (3.0 * sd_lo + (mu_hi - mu_lo)) / 3 # Test for top crossover: resample if crossover detected if (sd_hi < sd_lo) and (sd_hi <= delta_top): print(" *** Attempting to correct upper crossover") params_pe.add('logstd', value=sd_hi, min=delta_top) sys_dmg_fit[dx] = lmfit.minimize( res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) # Test for bottom crossover: resample if crossover detected # elif (sd_hi >= sd_lo) and sd_hi >= delta_btm: elif sd_hi >= delta_btm: print(" *** Attempting to correct lower crossover") params_pe.add('logstd', value=sd_hi, max=delta_btm) sys_dmg_fit[dx] = lmfit.minimize( res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) else: print(Fore.GREEN + "There is NO overlap for curve pair: " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) ###################################################################### sys_dmg_model.ix[SYS_DS[dx]] = \ sys_dmg_fit[dx].params['median'].value, \ sys_dmg_fit[dx].params['logstd'].value, \ sys_dmg_fit[dx].params['loc'].value, \ sys_dmg_fit[dx].chisqr # sys_dmg_ci[dx] = lmfit.conf_interval(sys_dmg_fit[dx], \ # sigmas=[0.674,0.950,0.997]) print("\nFINAL System Fragilities: \n") print(sys_dmg_model) # for dx in range(1, len(SYS_DS)): # print("\n\nFragility model statistics for damage state: %s" # % SYS_DS[dx]) # print("Goodness-of-Fit chi-square test statistic: %f" # % sys_dmg_fit[dx].chisqr) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_dmg_ci[dx]) # ----- Write fitted model params to file ----- sys_dmg_model.to_csv(os.path.join(out_path, 'system_model_fragility.csv'), sep=',') # ----- Plot the simulation data ----- fontP = FontProperties() fontP.set_size('small') fig = plt.figure(figsize=(9, 4.5), facecolor='white') ax = fig.add_subplot(111, axisbg='white') spl.add_legend_subtitle("Data") for i in range(1, len(SYS_DS)): ax.plot(hazard_input_vals, pb_exceed[i], label=SYS_DS[i], clip_on=False, color=spl.COLR_DS[i], linestyle='', alpha=0.3, marker=markers[i - 1], markersize=4, markeredgecolor=spl.COLR_DS[i]) # ----- Plot the fitted models ----- dmg_mdl_arr = np.zeros((len(SYS_DS), len(hazard_input_vals))) # plt.plot([0], marker='None', linestyle='None', # label="\nFitted Model: LogNormal") spl.add_legend_subtitle("\nFitted Model: LogNormal CDF") for dx in range(1, len(SYS_DS)): shape = sys_dmg_model.loc[SYS_DS[dx], 'LogStdDev'] loc = sys_dmg_model.loc[SYS_DS[dx], 'Location'] scale = sys_dmg_model.loc[SYS_DS[dx], 'Median'] dmg_mdl_arr[dx] = stats.lognorm.cdf( x_sample, shape, loc=loc, scale=scale) ax.plot(hazard_input_vals, dmg_mdl_arr[dx], label=SYS_DS[dx], clip_on=False, color=spl.COLR_DS[dx], alpha=0.65, linestyle='-', linewidth=1.6) # xbuffer = min(int(len(x_sample)/10), 5) * (x_sample[2]-x_sample[1]) # ax.set_xlim([min(x_sample)-xbuffer, max(x_sample)+xbuffer]) # ax.margins(0.03, None) outfig = os.path.join(out_path, 'fig_MODEL_sys_pb_exceed.png') spl.format_fig(ax, figtitle='System Fragility: ' + fc.system_class, x_lab='Peak Ground Acceleration (g)', y_lab='P($D_s$ > $d_s$ | PGA)', x_scale=None, y_scale=None, x_tick_val=None, y_tick_val=np.linspace(0.0, 1.0, num=11, endpoint=True), x_grid=False, y_grid=True, add_legend=True) # ----- Finish plotting ----- plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_dmg_model
def fit_restoration_data_multimode(RESTORATION_TIME_RANGE, sys_fn, SYS_DS, out_path): """ ********************************************************************* This function is not yet mature and is meant only for experimentation ********************************************************************* Function for fitting a bimodal normal cdf to restoration data :param RESTORATION_TIME_RANGE: restoration time range (numpy array) :param sys_fn: system functionality restoration over time (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted restoration model parameters (PANDAS dataframe) """ indx = pd.Index(SYS_DS[1:], name='Damage States') sys_rst_mdl_mode2 = pd.DataFrame(index=indx, columns=['Mean1', 'SD1', 'Weight1', 'Mean2', 'SD2', 'Weight2', 'Chi-Sqr']) sys_mix_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[DS] (m_est, s_est), pcov = curve_fit(norm_cdf, x_sample, y_sample) params_mx = lmfit.Parameters() params_mx.add('m1', value=m_est) params_mx.add('s1', value=s_est) params_mx.add('w1', value=0.6) params_mx.add('m2', value=m_est) params_mx.add('s2', value=s_est) params_mx.add('w2', value=0.4) sys_mix_fit[dx] = lmfit.minimize(res_bimodal_norm_cdf, params_mx, args=(x_sample, y_sample), method='leastsq') m1 = sys_mix_fit[dx].params['m1'].value s1 = sys_mix_fit[dx].params['s1'].value w1 = sys_mix_fit[dx].params['w1'].value m2 = sys_mix_fit[dx].params['m2'].value s2 = sys_mix_fit[dx].params['s2'].value w2 = sys_mix_fit[dx].params['w2'].value # sys_mix_ci[dx] = lmfit.conf_interval(sys_mix_fit[dx], \ # sigmas=[0.674,0.950,0.997], trace=False) sys_rst_mdl_mode2.ix[DS] = m1, s1, w1, m2, s2, w2, \ sys_mix_fit[dx].chisqr sys_rst_mdl_mode2.to_csv(os.path.join(sc.output_path, 'system_model_restoration__mode2.csv'), sep=',') print("\n\n" + "-" * 79) print("System Restoration Parameters: Bimodal Normal CDF Model") print("-" * 79 + "\n") print(sys_rst_mdl_mode2) # sys_rst_ci_df = ci_dict_to_df(sys_mix_ci) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_mix_ci[dx]) # ........................................................................ # w, h = plt.figaspect(0.5) w, h = [9, 4.5] fig = plt.figure(figsize=(w, h), dpi=250, facecolor='white') ax = fig.add_subplot(111, axisbg='white') spl.add_legend_subtitle("Simulation Data") for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE plt.plot( x_sample[1:], sys_fn[DS].values[1:] * 100, label=DS, clip_on=False, color=spl.COLR_DS[dx], alpha=0.4, linestyle='', marker=markers[dx - 1], markersize=4 ) spl.add_legend_subtitle("\nModel: Bimodal Normal CDF") for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE plt.plot( x_sample[1:], bimodal_norm_cdf( x_sample, *sys_rst_mdl_mode2.ix[DS].values[:-1])[1:] * 100, label=DS, clip_on=False, color=spl.COLR_DS[dx], alpha=0.65, linestyle='-', linewidth=1.5 ) x_pwr = int(np.ceil(np.log10(max(RESTORATION_TIME_RANGE)))) x_tiks = [10 ** t for t in range(0, x_pwr + 1)] outfig = os.path.join(out_path, 'fig_MODEL_sys_rst_mode2.png') ax.margins(0.03, None) spl.format_fig(ax, figtitle='Multimodal Restoration Model for: ' + fc.system_class, x_lab='Time (' + sc.time_unit + ')', y_lab='Percent Functional', x_scale='log', y_scale=None, x_tick_pos=x_tiks, x_tick_val=x_tiks, y_tick_val=range(0, 101, 20), x_lim=[min(x_tiks), max(x_tiks)], y_lim=[0, 100], x_grid=True, y_grid=True, add_legend=True) plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_rst_mdl_mode2
def fit_restoration_data(RESTORATION_TIME_RANGE, sys_fn, SYS_DS, out_path): """ Fits a normal CDF to each of the damage states, i.e. for each column of data in 'sys_fn' :param RESTORATION_TIME_RANGE: restoration time range (numpy array) :param sys_fn: system functionality restoration over time (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted restoration model parameters (PANDAS dataframe) """ indx = pd.Index(SYS_DS[1:], name='Damage States') sys_rst_mdl_mode1 = pd.DataFrame(index=indx, columns=['Mean', 'StdDev', 'Chi-Sqr']) # ----- Get the initial fit ----- sys_rst_ci = [{} for _ in xrange(len(SYS_DS))] sys_rst_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[SYS_DS[dx]] # Fit the dist. Add initial estimate if needed. init_m = np.mean(y_sample) init_s = np.std(y_sample) params = lmfit.Parameters() params.add('mean', value=init_m) params.add('stddev', value=init_s) sys_rst_fit[dx] = lmfit.minimize(res_norm_cdf, params, args=(x_sample, y_sample), method='leastsq') sys_rst_mdl_mode1.ix[SYS_DS[dx]] \ = sys_rst_fit[dx].params['mean'].value, \ sys_rst_fit[dx].params['stddev'].value, \ sys_rst_fit[dx].chisqr print("\n\n" + "-" * 79) print(Fore.YELLOW + "Fitting system RESTORATION data: Unimodal Normal CDF" + Fore.RESET) print("-" * 79) print("INITIAL Restoration Parameters:\n\n", sys_rst_mdl_mode1, '\n') # ----- Check for crossover and resample as needed ----- for dx in range(1, len(SYS_DS)): x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[SYS_DS[dx]] m1_hi = sys_rst_fit[dx].params['mean'].value s1_hi = sys_rst_fit[dx].params['stddev'].value y_model_hi = norm_cdf(x_sample, m1_hi, s1_hi) # -------------------------------------------------------------------- # Check for crossover... if dx >= 2: m1_lo, s1_lo, r1_chi = sys_rst_mdl_mode1.ix[SYS_DS[dx - 1]].values y_model_lo = norm_cdf(x_sample, m1_lo, s1_lo) if sum(y_model_lo - y_model_hi < 0): print(Fore.MAGENTA + "There is overlap for curve pair : " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) k = 0 crossover = True mu_err = 0 sdtop_err = 0 sdbtm_err = 0 while k < 50 and crossover: # Test if higher curve is co-incident with, # or precedes lower curve if (m1_hi <= m1_lo): if not mu_err > 0: print(" *** Attempting to correct mean...") params.add('mean', value=m1_hi, min=m1_lo * 1.01) sys_rst_fit[dx] = lmfit.minimize( res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) mu_err += 1 # Thresholds for testing top or bottom crossover delta_top = (1 + k / 100.0) * ( 3.0 * s1_lo - (m1_hi - m1_lo)) / 3 delta_btm = (1 - k / 100.0) * ( 3.0 * s1_lo + (m1_hi - m1_lo)) / 3 # Test for top crossover: resample if x-over detected if (s1_hi < s1_lo) or (s1_hi <= delta_top): if not sdtop_err > 0: print(" *** " + "Attempting to correct top crossover...") params.add('mean', value=m1_hi * 1.01, min=m1_lo * 1.01) params.add('stddev', value=s1_hi, min=delta_top) sys_rst_fit[dx] = lmfit.minimize( res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) sdtop_err += 1 # Test for bottom crossover: resample if x-over detected elif (s1_hi >= delta_btm): if not sdbtm_err > 0: print(" *** " + "Attempting to correct bottom crossover...") params.add('stddev', value=s1_hi, min=delta_btm) sys_rst_fit[dx] = lmfit.minimize( res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) sdbtm_err += 1 y_model_hi = norm_cdf(x_sample, m1_hi, s1_hi) crossover = sum(y_model_lo < y_model_hi) k += 1 # Test if crossover correction succeeded if not sum(y_model_lo < y_model_hi): print(Fore.YELLOW + " Crossover corrected!" + Fore.RESET) else: print(Fore.RED + Style.BRIGHT + " Crossover NOT corrected!" + Fore.RESET + Style.RESET_ALL) else: print(Fore.GREEN + "There is NO overlap for curve pair: " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) # -------------------------------------------------------------------- # * Need to find a solution to reporting confidence interval reliably: # # sys_rst_ci[dx], trace = lmfit.conf_interval(sys_rst_fit[dx], \ # sigmas=[0.674,0.950,0.997], trace=True) # -------------------------------------------------------------------- sys_rst_mdl_mode1.ix[SYS_DS[dx]] \ = sys_rst_fit[dx].params['mean'].value, \ sys_rst_fit[dx].params['stddev'].value, \ sys_rst_fit[dx].chisqr print("\nFINAL Restoration Parameters: \n") print(sys_rst_mdl_mode1) # for dx in range(1, len(SYS_DS)): # print("\n\nRestoration model statistics for damage state: %s" # % SYS_DS[dx]) # print("Goodness-of-Fit chi-square test statistic: %f" # % sys_rst_fit[dx].chisqr) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_rst_ci[dx]) sys_rst_mdl_mode1.to_csv(os.path.join(out_path, 'system_model_restoration__mode1.csv'), sep=',') fig = plt.figure(figsize=(9, 4.5), facecolor='white') ax = fig.add_subplot(111, axisbg='white') # --- Plot simulation data points --- spl.add_legend_subtitle("Simulation Data:") for i in range(1, len(SYS_DS)): ax.plot(RESTORATION_TIME_RANGE[1:], sys_fn[SYS_DS[i]][1:] * 100, label=SYS_DS[i], clip_on=False, color=spl.COLR_DS[i], linestyle='', alpha=0.35, marker=markers[i - 1], markersize=4, markeredgecolor=set2[-i]) # --- Plot the fitted models --- spl.add_legend_subtitle("\nModel: Normal CDF") for dx in range(1, len(SYS_DS)): m1 = sys_rst_mdl_mode1.ix[SYS_DS[dx]]['Mean'] s1 = sys_rst_mdl_mode1.ix[SYS_DS[dx]]['StdDev'] ax.plot(RESTORATION_TIME_RANGE[1:], norm_cdf(RESTORATION_TIME_RANGE, m1, s1)[1:] * 100, label=SYS_DS[dx], clip_on=False, color=spl.COLR_DS[dx], linestyle='-', linewidth=1.5, alpha=0.65) x_pwr = int(np.ceil(np.log10(max(RESTORATION_TIME_RANGE)))) x_tiks = [10 ** t for t in range(0, x_pwr + 1)] outfig = os.path.join(out_path, 'fig_MODEL_sys_rst_mode1.png') ax.margins(0.03, None) spl.format_fig(ax, figtitle='Restoration Curves: ' + fc.system_class, x_lab='Time (' + sc.time_unit + ')', y_lab='Percent Functional', x_scale='log', # <OR> None y_scale=None, x_tick_pos=x_tiks, x_tick_val=x_tiks, y_tick_val=range(0, 101, 20), x_lim=[min(x_tiks), max(x_tiks)], y_lim=[0, 100], x_grid=True, y_grid=True, add_legend=True) plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_rst_mdl_mode1
def fit_prob_exceed_model(hazard_input_vals, pb_exceed, SYS_DS, out_path): """ Fit a Lognormal CDF model to simulated probability exceedance data :param hazard_input_vals: input values for hazard intensity (numpy array) :param pb_exceed: probability of exceedance (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted exceedance model parameters (PANDAS dataframe) """ # DataFrame for storing the calculated System Damage Algorithms for # exceedance probabilities. indx = pd.Index(SYS_DS[1:], name='Damage States') sys_dmg_model = pd.DataFrame( index=indx, columns=['Median', 'LogStdDev', 'Location', 'Chi-Sqr']) decimals = pd.Series([3, 3, 3], index=['Median', 'LogStdDev', 'Location']) # ----- Initial fit ----- sys_dmg_ci = [{} for _ in xrange(len(SYS_DS))] sys_dmg_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): x_sample = hazard_input_vals y_sample = pb_exceed[dx] p0m = np.mean(y_sample) p0s = np.std(y_sample) # Fit the dist: params_pe = lmfit.Parameters() params_pe.add('median', value=p0m) # , min=0, max=10) params_pe.add('logstd', value=p0s) params_pe.add('loc', value=0.0, vary=False) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) sys_dmg_model.loc[SYS_DS[dx]] \ = (sys_dmg_fit[dx].params['median'].value, sys_dmg_fit[dx].params['logstd'].value, sys_dmg_fit[dx].params['loc'].value, sys_dmg_fit[dx].chisqr) # sys_dmg_model['Median'] = sys_dmg_model['Median'].map('{:,.3f}'.format) # sys_dmg_model['LogStdDev'] = sys_dmg_model['LogStdDev'].map('{:,.3f}'.format) # sys_dmg_model['Location'] = sys_dmg_model['Location'].map('{:,.1f}'.format) print("\n" + "-" * 79) print(Fore.YELLOW + "Fitting system FRAGILITY data: Lognormal CDF" + Fore.RESET) print("-" * 79) # sys_dmg_model = sys_dmg_model.round(decimals) print("INITIAL System Fragilities:\n\n", sys_dmg_model, '\n') # ----- Check for crossover and resample as needed ----- for dx in range(1, len(SYS_DS)): x_sample = hazard_input_vals y_sample = pb_exceed[dx] mu_hi = sys_dmg_fit[dx].params['median'].value sd_hi = sys_dmg_fit[dx].params['logstd'].value loc_hi = sys_dmg_fit[dx].params['loc'].value y_model_hi = stats.lognorm.cdf(x_sample, sd_hi, loc=loc_hi, scale=mu_hi) params_pe.add('median', value=mu_hi, min=0, max=10) params_pe.add('logstd', value=sd_hi) params_pe.add('loc', value=0.0, vary=False) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) ###################################################################### if dx >= 2: mu_lo, sd_lo, loc_lo, chi = \ sys_dmg_model.loc[SYS_DS[dx - 1]].values y_model_lo = stats.lognorm.cdf(x_sample, sd_lo, loc=loc_lo, scale=mu_lo) if sum(y_model_lo - y_model_hi < 0): print(Fore.MAGENTA + "There is overlap for curve pair : " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) # Test if higher curve is co-incident with, # or precedes lower curve if (mu_hi <= mu_lo) or (loc_hi <= loc_lo): print(" *** Mean of higher curve too low: resampling") params_pe.add('median', value=mu_hi, min=mu_lo) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) (mu_hi, sd_hi, loc_hi) = \ (sys_dmg_fit[dx].params['median'].value, sys_dmg_fit[dx].params['logstd'].value, sys_dmg_fit[dx].params['loc'].value) # Thresholds for testing top or bottom crossover delta_top = (3.0 * sd_lo - (mu_hi - mu_lo)) / 3 delta_btm = (3.0 * sd_lo + (mu_hi - mu_lo)) / 3 # Test for top crossover: resample if crossover detected if (sd_hi < sd_lo) and (sd_hi <= delta_top): print(" *** Attempting to correct upper crossover") params_pe.add('logstd', value=sd_hi, min=delta_top) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) # Test for bottom crossover: resample if crossover detected # elif (sd_hi >= sd_lo) and sd_hi >= delta_btm: elif sd_hi >= delta_btm: print(" *** Attempting to correct lower crossover") params_pe.add('logstd', value=sd_hi, max=delta_btm) sys_dmg_fit[dx] = lmfit.minimize(res_lognorm_cdf, params_pe, args=(x_sample, y_sample)) else: print(Fore.GREEN + "There is NO overlap for curve pair: " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) ###################################################################### sys_dmg_model.loc[SYS_DS[dx]] = \ sys_dmg_fit[dx].params['median'].value, \ sys_dmg_fit[dx].params['logstd'].value, \ sys_dmg_fit[dx].params['loc'].value, \ sys_dmg_fit[dx].chisqr # sys_dmg_ci[dx] = lmfit.conf_interval(sys_dmg_fit[dx], \ # sigmas=[0.674,0.950,0.997]) # sys_dmg_model['Median'] = sys_dmg_model['Median'].map(lambda x: '{0:.2f}'.format(x)) # sys_dmg_model['LogStdDev'] = sys_dmg_model['LogStdDev'].map(lambda x: '{0:.2f}'.format(x)) # sys_dmg_model['Location'] = sys_dmg_model['Location'].map('{0:.1f}'.format) print("\nFINAL System Fragilities: \n") print(sys_dmg_model) # for dx in range(1, len(SYS_DS)): # print("\n\nFragility model statistics for damage state: %s" # % SYS_DS[dx]) # print("Goodness-of-Fit chi-square test statistic: %f" # % sys_dmg_fit[dx].chisqr) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_dmg_ci[dx]) # ----- Write fitted model params to file ----- sys_dmg_model.to_csv(os.path.join(out_path, 'system_model_fragility.csv'), sep=',') # ----- Plot the simulation data ----- fontP = FontProperties() fontP.set_size('small') fig = plt.figure(figsize=(9, 5), facecolor='white') ax = fig.add_subplot(111, axisbg='white') spl.add_legend_subtitle("Data") for i in range(1, len(SYS_DS)): ax.plot(hazard_input_vals, pb_exceed[i], label=SYS_DS[i], clip_on=False, color=spl.COLR_DS[i], linestyle='', alpha=0.4, marker=markers[i - 1], markersize=4, markeredgecolor=spl.COLR_DS[i]) # ----- Plot the fitted models ----- # xformodel = np.linspace(0, max(hazard_input_vals), # max(hazard_input_vals)/0.01 + 1, endpoint=True) xformodel = np.linspace(0, 2.0, 201, endpoint=True) dmg_mdl_arr = np.zeros((len(SYS_DS), len(xformodel))) spl.add_legend_subtitle("\nFitted Model: LogNormal CDF") for dx in range(1, len(SYS_DS)): shape = sys_dmg_model.loc[SYS_DS[dx], 'LogStdDev'] loc = sys_dmg_model.loc[SYS_DS[dx], 'Location'] scale = sys_dmg_model.loc[SYS_DS[dx], 'Median'] dmg_mdl_arr[dx] = stats.lognorm.cdf(xformodel, shape, loc=loc, scale=scale) ax.plot(xformodel, dmg_mdl_arr[dx], label=SYS_DS[dx], clip_on=False, color=spl.COLR_DS[dx], alpha=0.65, linestyle='-', linewidth=1.6) # xbuffer = min(int(len(x_sample)/10), 5) * (x_sample[2]-x_sample[1]) # ax.set_xlim([min(x_sample)-xbuffer, max(x_sample)+xbuffer]) # ax.margins(0.03, None) outfig = os.path.join(out_path, 'fig_MODEL_sys_pb_exceed.png') spl.format_fig(ax, figtitle='System Fragility: ' + fc.system_class, x_lab='Peak Ground Acceleration (g)', y_lab='P($D_s$ > $d_s$ | PGA)', x_scale=None, y_scale=None, x_tick_val=None, y_tick_pos=np.linspace(0.0, 1.0, num=6, endpoint=True), y_tick_val=np.linspace(0.0, 1.0, num=6, endpoint=True), x_grid=True, y_grid=True, add_legend=True) # ----- Finish plotting ----- plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_dmg_model
def fit_restoration_data_multimode(RESTORATION_TIME_RANGE, sys_fn, SYS_DS, out_path): """ ********************************************************************* This function is not yet mature and is meant only for experimentation ********************************************************************* Function for fitting a bimodal normal cdf to restoration data :param RESTORATION_TIME_RANGE: restoration time range (numpy array) :param sys_fn: system functionality restoration over time (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted restoration model parameters (PANDAS dataframe) """ indx = pd.Index(SYS_DS[1:], name='Damage States') sys_rst_mdl_mode2 = pd.DataFrame(index=indx, columns=[ 'Mean1', 'SD1', 'Weight1', 'Mean2', 'SD2', 'Weight2', 'Chi-Sqr' ]) sys_mix_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[DS] (m_est, s_est), pcov = curve_fit(norm_cdf, x_sample, y_sample) params_mx = lmfit.Parameters() params_mx.add('m1', value=m_est) params_mx.add('s1', value=s_est) params_mx.add('w1', value=0.6) params_mx.add('m2', value=m_est) params_mx.add('s2', value=s_est) params_mx.add('w2', value=0.4) sys_mix_fit[dx] = lmfit.minimize(res_bimodal_norm_cdf, params_mx, args=(x_sample, y_sample), method='leastsq') m1 = sys_mix_fit[dx].params['m1'].value s1 = sys_mix_fit[dx].params['s1'].value w1 = sys_mix_fit[dx].params['w1'].value m2 = sys_mix_fit[dx].params['m2'].value s2 = sys_mix_fit[dx].params['s2'].value w2 = sys_mix_fit[dx].params['w2'].value # sys_mix_ci[dx] = lmfit.conf_interval(sys_mix_fit[dx], \ # sigmas=[0.674,0.950,0.997], trace=False) sys_rst_mdl_mode2.loc[DS] = m1, s1, w1, m2, s2, w2, \ sys_mix_fit[dx].chisqr sys_rst_mdl_mode2.to_csv(os.path.join( sc.output_path, 'system_model_restoration__mode2.csv'), sep=',') print("\n\n" + "-" * 79) print("System Restoration Parameters: Bimodal Normal CDF Model") print("-" * 79 + "\n") print(sys_rst_mdl_mode2) # sys_rst_ci_df = ci_dict_to_df(sys_mix_ci) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_mix_ci[dx]) # ........................................................................ # w, h = plt.figaspect(0.5) w, h = [9, 4.5] fig = plt.figure(figsize=(w, h), dpi=250, facecolor='white') ax = fig.add_subplot(111, axisbg='white') spl.add_legend_subtitle("Simulation Data") for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE plt.plot(x_sample[1:], sys_fn[DS].values[1:] * 100, label=DS, clip_on=False, color=spl.COLR_DS[dx], alpha=0.4, linestyle='', marker=markers[dx - 1], markersize=4) spl.add_legend_subtitle("\nModel: Bimodal Normal CDF") for dx in range(1, len(SYS_DS)): DS = SYS_DS[dx] x_sample = RESTORATION_TIME_RANGE plt.plot(x_sample[1:], bimodal_norm_cdf(x_sample, * sys_rst_mdl_mode2.loc[DS].values[:-1])[1:] * 100, label=DS, clip_on=False, color=spl.COLR_DS[dx], alpha=0.65, linestyle='-', linewidth=1.5) x_pwr = int(np.ceil(np.log10(max(RESTORATION_TIME_RANGE)))) x_tiks = [10**t for t in range(0, x_pwr + 1)] outfig = os.path.join(out_path, 'fig_MODEL_sys_rst_mode2.png') ax.margins(0.03, None) spl.format_fig(ax, figtitle='Multimodal Restoration Model for: ' + fc.system_class, x_lab='Time (' + sc.time_unit + ')', y_lab='Percent Functional', x_scale='log', y_scale=None, x_tick_pos=x_tiks, x_tick_val=x_tiks, y_tick_val=range(0, 101, 20), x_lim=[min(x_tiks), max(x_tiks)], y_lim=[0, 100], x_grid=True, y_grid=True, add_legend=True) plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_rst_mdl_mode2
def fit_restoration_data(RESTORATION_TIME_RANGE, sys_fn, SYS_DS, out_path): """ Fits a normal CDF to each of the damage states, i.e. for each column of data in 'sys_fn' :param RESTORATION_TIME_RANGE: restoration time range (numpy array) :param sys_fn: system functionality restoration over time (2D numpy array) :param SYS_DS: discrete damage states (list) :param out_path: directory path for writing output (string) :returns: fitted restoration model parameters (PANDAS dataframe) """ indx = pd.Index(SYS_DS[1:], name='Damage States') sys_rst_mdl_mode1 = pd.DataFrame(index=indx, columns=['Mean', 'StdDev', 'Chi-Sqr']) # ----- Get the initial fit ----- sys_rst_ci = [{} for _ in xrange(len(SYS_DS))] sys_rst_fit = [[] for _ in xrange(len(SYS_DS))] for dx in range(1, len(SYS_DS)): x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[SYS_DS[dx]] # Fit the dist. Add initial estimate if needed. init_m = np.mean(y_sample) init_s = np.std(y_sample) params = lmfit.Parameters() params.add('mean', value=init_m) params.add('stddev', value=init_s) sys_rst_fit[dx] = lmfit.minimize(res_norm_cdf, params, args=(x_sample, y_sample), method='leastsq') sys_rst_mdl_mode1.loc[SYS_DS[dx]] \ = sys_rst_fit[dx].params['mean'].value, \ sys_rst_fit[dx].params['stddev'].value, \ sys_rst_fit[dx].chisqr print("\n\n" + "-" * 79) print(Fore.YELLOW + "Fitting system RESTORATION data: Unimodal Normal CDF" + Fore.RESET) print("-" * 79) # # Format output to limit displayed decimal precision # sys_rst_mdl_mode1['Mean'] = \ # sys_rst_mdl_mode1['Mean'].map('{:,.3f}'.format) # sys_rst_mdl_mode1['StdDev'] = \ # sys_rst_mdl_mode1['StdDev'].map('{:,.3f}'.format) print("INITIAL Restoration Parameters:\n\n", sys_rst_mdl_mode1, '\n') # ----- Check for crossover and resample as needed ----- for dx in range(1, len(SYS_DS)): x_sample = RESTORATION_TIME_RANGE y_sample = sys_fn[SYS_DS[dx]] m1_hi = sys_rst_fit[dx].params['mean'].value s1_hi = sys_rst_fit[dx].params['stddev'].value y_model_hi = norm_cdf(x_sample, m1_hi, s1_hi) # -------------------------------------------------------------------- # Check for crossover... if dx >= 2: m1_lo, s1_lo, r1_chi = sys_rst_mdl_mode1.loc[SYS_DS[dx - 1]].values y_model_lo = norm_cdf(x_sample, m1_lo, s1_lo) if sum(y_model_lo - y_model_hi < 0): print(Fore.MAGENTA + "There is overlap for curve pair : " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) k = 0 crossover = True mu_err = 0 sdtop_err = 0 sdbtm_err = 0 while k < 50 and crossover: # Test if higher curve is co-incident with, # or precedes lower curve if (m1_hi <= m1_lo): if not mu_err > 0: print(" *** Attempting to correct mean...") params.add('mean', value=m1_hi, min=m1_lo * 1.01) sys_rst_fit[dx] = lmfit.minimize(res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) mu_err += 1 # Thresholds for testing top or bottom crossover delta_top = (1 + k / 100.0) * (3.0 * s1_lo - (m1_hi - m1_lo)) / 3 delta_btm = (1 - k / 100.0) * (3.0 * s1_lo + (m1_hi - m1_lo)) / 3 # Test for top crossover: resample if x-over detected if (s1_hi < s1_lo) or (s1_hi <= delta_top): if not sdtop_err > 0: print(" *** " + "Attempting to correct top crossover...") params.add('mean', value=m1_hi * 1.01, min=m1_lo * 1.01) params.add('stddev', value=s1_hi, min=delta_top) sys_rst_fit[dx] = lmfit.minimize(res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) sdtop_err += 1 # Test for bottom crossover: resample if x-over detected elif (s1_hi >= delta_btm): if not sdbtm_err > 0: print(" *** " + "Attempting to correct bottom crossover...") params.add('stddev', value=s1_hi, min=delta_btm) sys_rst_fit[dx] = lmfit.minimize(res_norm_cdf, params, args=(x_sample, y_sample)) (m1_hi, s1_hi) = \ (sys_rst_fit[dx].params['mean'].value, sys_rst_fit[dx].params['stddev'].value) sdbtm_err += 1 y_model_hi = norm_cdf(x_sample, m1_hi, s1_hi) crossover = sum(y_model_lo < y_model_hi) k += 1 # Test if crossover correction succeeded if not sum(y_model_lo < y_model_hi): print(Fore.YELLOW + " Crossover corrected!" + Fore.RESET) else: print(Fore.RED + Style.BRIGHT + " Crossover NOT corrected!" + Fore.RESET + Style.RESET_ALL) else: print(Fore.GREEN + "There is NO overlap for curve pair: " + SYS_DS[dx - 1] + '-' + SYS_DS[dx] + Fore.RESET) # -------------------------------------------------------------------- # * Need to find a solution to reporting confidence interval reliably: # # sys_rst_ci[dx], trace = lmfit.conf_interval(sys_rst_fit[dx], \ # sigmas=[0.674,0.950,0.997], trace=True) # -------------------------------------------------------------------- sys_rst_mdl_mode1.loc[SYS_DS[dx]] \ = sys_rst_fit[dx].params['mean'].value, \ sys_rst_fit[dx].params['stddev'].value, \ sys_rst_fit[dx].chisqr # sys_rst_mdl_mode1['Mean'] = \ # sys_rst_mdl_mode1['Mean'].map('{:,.3f}'.format) # sys_rst_mdl_mode1['StdDev'] = \ # sys_rst_mdl_mode1['StdDev'].map('{:,.3f}'.format) print("\nFINAL Restoration Parameters: \n") print(sys_rst_mdl_mode1) # for dx in range(1, len(SYS_DS)): # print("\n\nRestoration model statistics for damage state: %s" # % SYS_DS[dx]) # print("Goodness-of-Fit chi-square test statistic: %f" # % sys_rst_fit[dx].chisqr) # print("Confidence intervals: ") # lmfit.printfuncs.report_ci(sys_rst_ci[dx]) sys_rst_mdl_mode1.to_csv(os.path.join( out_path, 'system_model_restoration__mode1.csv'), sep=',') fig = plt.figure(figsize=(9, 4.5), facecolor='white') ax = fig.add_subplot(111, axisbg='white') # --- Plot simulation data points --- spl.add_legend_subtitle("Simulation Data:") for i in range(1, len(SYS_DS)): ax.plot(RESTORATION_TIME_RANGE[1:], sys_fn[SYS_DS[i]][1:] * 100, label=SYS_DS[i], clip_on=False, color=spl.COLR_DS[i], linestyle='', alpha=0.35, marker=markers[i - 1], markersize=4, markeredgecolor=set2[-i]) # --- Plot the fitted models --- spl.add_legend_subtitle("\nModel: Normal CDF") for dx in range(1, len(SYS_DS)): m1 = sys_rst_mdl_mode1.loc[SYS_DS[dx]]['Mean'] s1 = sys_rst_mdl_mode1.loc[SYS_DS[dx]]['StdDev'] ax.plot(RESTORATION_TIME_RANGE[1:], norm_cdf(RESTORATION_TIME_RANGE, m1, s1)[1:] * 100, label=SYS_DS[dx], clip_on=False, color=spl.COLR_DS[dx], linestyle='-', linewidth=1.5, alpha=0.65) x_pwr = int(np.ceil(np.log10(max(RESTORATION_TIME_RANGE)))) x_tiks = [10**t for t in range(0, x_pwr + 1)] outfig = os.path.join(out_path, 'fig_MODEL_sys_rst_mode1.png') ax.margins(0.03, None) spl.format_fig( ax, figtitle='Restoration Model for: ' + fc.system_class, x_lab='Time (' + sc.time_unit + ')', y_lab='Percent Functional', x_scale='log', # <OR> None y_scale=None, x_tick_pos=x_tiks, x_tick_val=x_tiks, y_tick_val=range(0, 101, 20), x_lim=[min(x_tiks), max(x_tiks)], y_lim=[0, 100], x_grid=True, y_grid=True, add_legend=True) plt.savefig(outfig, format='png', bbox_inches='tight', dpi=300) plt.close(fig) return sys_rst_mdl_mode1