Source code for buckpy.buckpy_postprocessing

"""
This module contains the post-processing functions of BuckPy.
"""

import time
import numpy as np
import pandas as pd
import pandas.io.formats.excel
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

[docs] def pp_comb_prob(df_in, df_buckle, col_no, n_sim): """ Count the number of set combinations based on given set number Parameters ---------- df_in : pandas DataFrame DataFrame containing the set number of combination with buckles along the pipeline. df_buckle : pandas DataFrame DataFrame containing the count and probability of set number of combination with buckles. col_no : String Column name of the set number n_sim : int Number of simulations. Returns ------- df_out : pandas DataFrame DataFrame containing post-processed statistics about the number of set combination with buckles along the pipeline. """ def pp_count_comb(row, df, col): # Add 1 to the value each time there is a buckle at the current set df.iloc[int(row.loc['isim']), int(row.loc[col])] += 1.0 # Row number is the total simulation number and column number is the total set number n_col = df_in.unique().size df_out = pd.DataFrame(0, index = np.arange(int(n_sim)), columns = np.arange(int(n_col + 1.0))) # Add 1 to the value in df_out each time there is a buckle df_buckle.apply(lambda row: pp_count_comb(row, df_out, col_no), axis = 1) # Delete the all 0 rows and the first column, and add prefix to column names df_out = df_out[(df_out.T != 0).any()].iloc[:, 1:].add_prefix('Set_') # Count the number of unique set combinations col_list = df_out.columns.values.tolist() df_out = df_out.groupby(col_list).size().reset_index().rename( columns = {0: 'Number of Simulations'}) # Calculate probability and sort values in descending order based on count and reset index df_out['Probability of Combination'] = df_out['Number of Simulations'] / n_sim df_out = df_out.sort_values( by = 'Number of Simulations', ascending = False).reset_index(drop = True) return df_out
[docs] def pp_rename_columns(df_in, df_out): """ Rename columns in the post-processing DataFrame. Parameters ---------- df_in : pandas DataFrame DataFrame containing the set number of combination with buckles along the pipeline. df_out : pandas DataFrame DataFrame containing post-processed statistics about the number of set combination with buckles along the pipeline. Returns ------- df_out : pandas DataFrame DataFrame containing post-processed statistics about the number of set combination with buckles along the pipeline. Notes ----- Use the Python built-in ``set`` for unique labels, e.g., ``set(labels)``. """ # Change column names from 'Set_1' to predefined names new_col_list = np.array(df_in['col_name'].values).tolist() df_out.columns = np.concatenate( np.array([new_col_list, ['Number of Simulations', 'Probability of Combination']], dtype = object)).tolist() # Create new column name list sorted by KP values and reorder columns df_out['Combination Id'] = df_out.index + 1 col_list = np.array(df_in.sort_values(by = 'index').loc[:, 'col_name'].values).tolist() cols = np.concatenate( np.array([['Combination Id', 'Number of Simulations', 'Probability of Combination'], col_list], dtype = object)).tolist() df_out = df_out[cols] # Create a double header header_first_line = [''] * 3 + ['Number of Buckles'] * (len(cols) - 3) header_turples = list(zip(header_first_line, cols)) double_header = pd.MultiIndex.from_tuples(header_turples) df_out.columns = double_header return df_out
[docs] def pp_comb_buckles_per_set(df_pp, df_pp_set, n_sim): """ Count the number of set combinations and sort based on the most frequent set combinations based on post-processing set. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. df_pp_set : pandas DataFrame DataFrame containing the definition of the post-processed sets. n_sim : int Number of simulations. Returns ------- df_set_comb : pandas DataFrame DataFrame containing post-processed statistics about the number of set combination with buckles along the pipeline. """ # Create index columns for the original index and the sorted pp set value index df_pp_set = df_pp_set[['pp_set', 'KP_from', 'KP_to']].sort_values( by = ['pp_set', 'KP_from']).reset_index() df_pp_set = df_pp_set.reset_index().rename(columns = {'level_0': 'index_sorted'}) # Select the number of simulation and post-precessing set number with a buckle df_set_buckle_no = df_pp[['isim', 'pp_set']].sort_values(by = ['isim', 'pp_set']) # Count the number of set combinations based on given set number df_set_comb = pp_comb_prob(df_pp_set['pp_set'], df_set_buckle_no, 'pp_set', n_sim) # Insert the extra column of duplicated set number into df_set_comb df_duplicated = df_pp_set[df_pp_set.duplicated(subset = ['pp_set'])].reset_index(drop = True) df_duplicated.apply(lambda row: df_set_comb.insert( int(row['index_sorted']), f"Duplicate_{int(row['pp_set'])}", df_set_comb.iloc[:, int(row['index_sorted'] - 1)]), axis = 1) # Create new column name using KP from and KP to df_pp_set[['KP_from', 'KP_to']] = df_pp_set[['KP_from', 'KP_to']].astype(int).astype(str) df_pp_set['col_name'] = 'KP ' + df_pp_set['KP_from'] + ' to ' + df_pp_set['KP_to'] # Rename column names from 'Set_' to certain column names df_set_comb = pp_rename_columns(df_pp_set, df_set_comb) return df_set_comb
[docs] def pp_comb_buckles_per_section(df_pp, df_scen, n_sim): """ Count the number of set combinations and sort based on the most frequent set combinations based on section number in the route data. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. df_scen : DataFrame Dataframe containing the design data along the pipeline route (mesh) that remains constant among deterministic and Monte-Carlo simulations. n_sim : int Number of simulations. Returns ------- df_set_comb : pandas DataFrame DataFrame containing post-processed statistics about the number of set combination with buckles along the pipeline. """ def pp_section_no(kp, kp_list): # Add the current KP to the kp list and sort it kp_list = np.append(kp_list, kp) kp_list.sort() # Find the index of the current KP section_no = np.where(kp_list == kp)[0][0] return section_no # Select the number of simulation and post-precessing KP number with a buckle df_buckle_kp = df_pp[['isim', 'KP']].sort_values(by = ['isim', 'KP']) # Use KP range to group KP into sections and rename columns df_section = df_scen[['KP From', 'KP To', 'Point ID From', 'Point ID To']].drop_duplicates( subset = ['KP From']).reset_index(drop = True) df_section.columns = ['KP_from', 'KP_to', 'point_id_from', 'point_id_to'] # Find the unique KP value and create section number column in df_buckle_kp df_kp = pd.DataFrame({'KP': df_buckle_kp['KP'].unique()}) df_kp['Section No'] = df_kp.apply(lambda row: pp_section_no( row['KP'], df_section['KP_from'].unique()), axis = 1) # Merge the Section Number column to df_buckle_kp df_buckle_kp = pd.merge(df_buckle_kp, df_kp, on = 'KP', how = 'left') # Count the number of set combinations based on given set number df_set_comb = pp_comb_prob(df_section['KP_from'], df_buckle_kp, 'Section No', n_sim) # Create new column name using Point ID df_section = df_section.sort_values(by = 'KP_from').reset_index() df_section['col_name'] = df_section['point_id_from'] + ' to ' + df_section['point_id_to'] # Rename column names from 'Set_' to certain column names df_set_comb = pp_rename_columns(df_section, df_set_comb) return df_set_comb
[docs] def pp_char_vas(df_pp, df_buckling, df_set): """ Determine the characteristic VAS. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. df_buckling : pandas DataFrame DataFrame containing buckling data. df_set : pandas DataFrame DataFrame containing sets along the pipeline route. Returns ------- df_set : pandas DataFrame Updated DataFrame containing characteristic VAS information. """ df_merged = pd.merge(df_pp, df_buckling, on='pp_set') df_merged = df_merged.sort_values(by = ['pp_set', 'VAS_op']) df_merged['VAS_op'] = df_merged['VAS_op'].round(1) # Dataframe grouping the VAS in ascending order at each set along the pipeline route df_char_vas = df_merged.groupby(by = ['pp_set', 'VAS_op', 'prob_buckling'], as_index = False) \ .agg(VAS_occurrence = ('VAS_op', 'count'), no_buckles = ('no_buckles', 'max')) # Add the probability associated to the characteristic VAS df_set_temp = df_set.copy().drop_duplicates(subset = 'pp_set', keep = 'first') df_char_vas = df_char_vas.merge(df_set_temp[['pp_set', 'Characteristic VAS Probability']], on = 'pp_set', how = 'inner') # Cumulative distributions of the VAS (conditional and unconditional) df_char_vas['VAS_prob_cond'] = df_char_vas['VAS_occurrence'] / df_char_vas['no_buckles'] df_char_vas['VAS_cumsum_prob_cond'] = df_char_vas[['pp_set', 'VAS_prob_cond']] \ .groupby(by = 'pp_set').cumsum() # Lines below associate VAS=0 to cases not buckling df_char_vas['VAS_cumsum_prob_uncond'] = (1.0 - df_char_vas['prob_buckling']) \ + df_char_vas['VAS_cumsum_prob_cond'] * df_char_vas['prob_buckling'] # Complementary distributions of the VAS (conditional and unconditional) df_char_vas['prob_exceedance_cond'] = 1.0 - df_char_vas['VAS_cumsum_prob_cond'] df_char_vas['prob_exceedance_uncond'] = 1.0 - df_char_vas['VAS_cumsum_prob_uncond'] # Difference bewteen the complementary distributions and target probabilities of excedance df_char_vas['delta_prob_exceedance_cond'] = \ (df_char_vas['prob_exceedance_cond'] - \ df_char_vas['Characteristic VAS Probability']).abs() df_char_vas['delta_prob_exceedance_uncond'] = \ (df_char_vas['prob_exceedance_uncond'] - \ df_char_vas['Characteristic VAS Probability']).abs() # NumPy array with the rows containing the characteristic VAS of each pp_set vas_cond_indices = df_char_vas.groupby( by = 'pp_set')['delta_prob_exceedance_cond'].idxmin().to_numpy() vas_uncond_indices = df_char_vas.groupby( by = 'pp_set')['delta_prob_exceedance_uncond'].idxmin().to_numpy() # Find conditional VAS by pp_set df_vas_cond = df_char_vas.loc[vas_cond_indices, ['pp_set', 'VAS_op']] df_vas_cond.rename(columns = {'VAS_op': 'VAS_charac_conditional'}, inplace = True) # Find unconditional VAS by pp_set df_vas_uncond = df_char_vas.loc[vas_uncond_indices, ['pp_set', 'VAS_op']] df_vas_uncond.rename(columns = {'VAS_op': 'VAS_charac_unconditional'}, inplace = True) # Assign conditional and unconditional VAS by pp_set df_set = pd.merge(df_set, df_vas_cond, on = 'pp_set', how = 'outer') df_set = pd.merge(df_set, df_vas_uncond, on = 'pp_set', how = 'outer') df_set.loc[df_set['prob_buckling'] < df_set['Characteristic VAS Probability'], 'VAS_charac_unconditional'] = 0.0 return df_set
[docs] def pp_char_fric(df_pp, df_buckling, df_set, prob_exceed_char_fric): """ Determine the characteristic lateral breakout friction. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. df_buckling : pandas DataFrame DataFrame containing buckling data. df_set : pandas DataFrame DataFrame containing sets along the pipeline route. prob_exceed_char_fric : float Probability of exceeding characteristic lateral breakout friction. Returns ------- df_set : pandas DataFrame Updated DataFrame containing characteristic lateral breakout friction information. """ # Dataframe containing the list of frictions in ascending order at each set df_merged = pd.merge(df_pp, df_buckling, on='pp_set') df_merged = df_merged.sort_values(by = ['pp_set', 'mulat_op']) # Dataframe grouping the frictions in ascending ordet at each set along the pipeline route df_char_fric = df_merged.groupby( by = ['pp_set', 'mulat_op', 'prob_buckling'], as_index = False) \ .agg(mulat_occurrence = ('mulat_op', 'count'), no_buckles = ('no_buckles', 'max')) # Cumulative distributions of the friction (conditional and unconditional) df_char_fric['mulat_prob_cond'] = df_char_fric['mulat_occurrence'] / df_char_fric['no_buckles'] df_char_fric['mulat_cumsum_prob_cond'] = df_char_fric[['pp_set', 'mulat_prob_cond']] \ .groupby(by = 'pp_set').cumsum() # Lines below associate friction=0 to cases not buckling df_char_fric['mulat_cumsum_prob_uncond'] = (1.0 - df_char_fric['prob_buckling']) \ + df_char_fric['mulat_cumsum_prob_cond'] * df_char_fric['prob_buckling'] # Complementary distributions of the friction (conditional and unconditional) df_char_fric['prob_exceedance_cond'] = 1.0 - df_char_fric['mulat_cumsum_prob_cond'] df_char_fric['prob_exceedance_uncond'] = 1.0 - df_char_fric['mulat_cumsum_prob_uncond'] # Difference between the complementary distributions and target probabilities of excedance df_char_fric['delta_prob_exceedance_cond'] = (df_char_fric['prob_exceedance_cond'] - prob_exceed_char_fric).abs() df_char_fric['delta_prob_exceedance_uncond'] = (df_char_fric['prob_exceedance_uncond'] - prob_exceed_char_fric).abs() # NumPy array with the rows containing the characteristic friction of each pp_set indices_mulat_cond = df_char_fric.groupby(by = 'pp_set')['delta_prob_exceedance_cond'] \ .idxmin().to_numpy() indices_mulat_uncond = df_char_fric.groupby(by = 'pp_set')['delta_prob_exceedance_uncond'] \ .idxmin().to_numpy() # Find conditional friction by pp_set df_fric_cond = df_char_fric.loc[indices_mulat_cond, ['pp_set', 'prob_buckling', 'mulat_op']] df_fric_cond.rename(columns = {'mulat_op': 'mulat_charac_conditional'}, inplace = True) # Find unconditional friction by pp_set df_fric_uncond = df_char_fric.loc[indices_mulat_uncond, ['pp_set', 'prob_buckling', 'mulat_op']] df_fric_uncond.rename(columns = {'mulat_op': 'mulat_charac_unconditional'}, inplace = True) # Assign conditional and unconditional friction by pp_set df_set = pd.merge(df_set, df_fric_cond, on = 'pp_set', how = 'outer') df_set = pd.merge(df_set, df_fric_uncond, on = 'pp_set', how = 'outer') df_set.loc[df_set['prob_buckling'] < prob_exceed_char_fric, 'mulat_charac_unconditional'] = 0.0 return df_set
[docs] def pp_elem(df_pp, n_sim): """ Perform post-processing of the probability of buckling, VAS and lateral breakout friction at each element along the pipeline route. Parameters ---------- df_pp : pandas DataFrame DataFrame containing the results of the analyses. n_sim : int Total number of simulations. Returns ------- df_elem : pandas DataFrame DataFrame containing post-processed probabilities, VAS and lateral breakout friction for each pipeline element. """ # Probability of buckling at each element along the pipeline route df_buckling = df_pp[['KP', 'isim']].groupby('KP', as_index = False).agg( no_buckles = ('isim', 'nunique')) df_buckling['prob_buckling'] = df_buckling['no_buckles'] / n_sim df_buckling['prob_not_buckling'] = 1.0 - df_buckling['prob_buckling'] # VAS at each element along the pipeline route df_vas = df_pp[['KP', 'VAS_op']].groupby('KP', as_index = False).agg( VAS_mean = ('VAS_op', 'mean'), VAS_std = ('VAS_op', 'std'), VAS_min = ('VAS_op', 'min'), VAS_max = ('VAS_op', 'max')) # Lateral breakout friction at each element along the pipeline route df_friction = df_pp[['KP', 'mulat_op']].groupby('KP', as_index = False).agg( mulat_mean = ('mulat_op', 'mean'), mulat_std = ('mulat_op', 'std'), mulat_min = ('mulat_op', 'min'), mulat_max = ('mulat_op', 'max')) # Merge df_buckling and df_vas on 'KP' df_elem = pd.merge(df_buckling, df_vas, on = 'KP') # Merge merged_df and df_friction on 'KP' df_elem = pd.merge(df_elem, df_friction, on = 'KP') # Change labels for print-out df_elem = df_elem.rename(columns = { 'KP': 'Centroid of the Element (m)', 'no_buckles': 'Number of Simulations with a Buckle', 'prob_buckling': 'Probability of Buckling', 'prob_not_buckling': 'Probability of not Buckling', 'VAS_mean': 'Mean of the VAS (m)', 'VAS_std': 'Standard Deviation of the VAS (m)', 'VAS_min': 'Minimum VAS (m)', 'VAS_max': 'Maximum VAS (m)', 'mulat_mean': 'Mean of the Lateral Breakout Friction', 'mulat_std': 'Standard Deviation of the Lateral Breakout Friction', 'mulat_min': 'Minimum Lateral Breakout Friction', 'mulat_max': 'Maximum Lateral Breakout Friction'}) df_elem = df_elem.fillna(0.0) return df_elem
[docs] def pp_no_buckles(df_pp, n_sim): """ Perform post-processing of the number of buckles along the pipeline. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. n_sim : int Total number of simulations. Returns ------- df_no_buckles : pandas DataFrame DataFrame containing post-processed statistics about the number of buckles along the pipeline. """ # Number of buckles per simulation df_grouped = df_pp[['isim', 'KP']].groupby('isim', as_index = False).agg( no_buckles = ('KP', 'count')) # Number of simulations as a function of the number of buckles along the pipeline df_no_buckles = df_grouped.pivot_table(columns = ['no_buckles'], aggfunc = 'size') df_no_buckles = df_no_buckles.reset_index() df_no_buckles = df_no_buckles.rename(columns={ 'n_buckle': 'Total Number of Buckles', 0: 'Occurrence'}) # Accounting for cases without buckle in cumsum new_row = {'no_buckles': 0, 'Occurrence': n_sim - df_no_buckles['Occurrence'].sum()} df_no_buckles = pd.concat([df_no_buckles, pd.DataFrame(new_row, index=[0])], ignore_index=True) df_no_buckles = df_no_buckles.sort_values(by = ['no_buckles']).reset_index(drop=True) # Distribution of the expected number of buckles along the pipeline df_no_buckles['Probability'] = df_no_buckles['Occurrence'] / n_sim df_no_buckles['Cumulative Probability'] = df_no_buckles['Probability'].cumsum() # Change labels for print-out df_no_buckles = df_no_buckles.rename(columns = { 'no_buckles': 'Number of Buckles', 'Occurrence': 'Number of Simulations', 'Probability': 'Probability of Buckling', 'Cumulative Probability': 'Cumulative Probability of Buckling'}) return df_no_buckles
[docs] def pp_sets(df_pp, n_sim, prob_exceed_char_fric, df_pp_set, df_scen): """ Perform post-processing of the probability of buckling, VAS and lateral breakout friction at each post-processing set along the pipeline route. Parameters ---------- df_pp : pandas DataFrame DataFrame containing pipeline element analysis results. n_sim : int Total number of simulations. prob_exceed_char_fric : float Probability of exceedance of the characteristic lateral breakout friction. df_pp_set : DataFrame Definition of element sets for post-processing outputs. df_scen : DataFrame Dataframe containing the design data along the pipeline route (mesh) that remains constant among deterministic and Monte-Carlo simulations. Returns ------- df_set : pandas DataFrame DataFrame containing post-processed statistics about pipeline sets. """ # Probability of buckling at each set along the pipeline route df_pp_set = df_pp_set[['pp_set', 'KP_from', 'KP_to', 'Characteristic VAS Probability']] # Probability of buckling at each set along the pipeline route df_buckling = df_pp[['pp_set', 'KP_from', 'KP_to', 'isim', 'VAS_op']].groupby( 'pp_set', as_index = False).agg( no_simulations_with_buckles = ('isim', 'nunique'), no_buckles = ('VAS_op', 'count')) df_buckling['prob_buckling'] = df_buckling['no_simulations_with_buckles'] / n_sim df_buckling['prob_not_buckling'] = 1.0 - df_buckling['prob_buckling'] # VAS at each set along the pipeline route df_vas = df_pp[['pp_set', 'VAS_op']].groupby('pp_set', as_index = False).agg( VAS_mean = ('VAS_op', 'mean'), VAS_std = ('VAS_op', 'std'), VAS_min = ('VAS_op', 'min'), VAS_max = ('VAS_op', 'max')) # Lateral breakout friction at each set along the pipeline route df_friction = df_pp[['pp_set', 'mulat_op']].groupby('pp_set', as_index = False).agg( mulat_mean = ('mulat_op', 'mean'), mulat_std = ('mulat_op', 'std'), mulat_min = ('mulat_op', 'min'), mulat_max = ('mulat_op', 'max')) # Merge df_buckling and df_vas on 'pp_set' df_set = pd.merge(df_pp_set, df_buckling, on = 'pp_set', how = 'outer') # Merge df_buckling and df_vas on 'pp_set' df_set = pd.merge(df_set, df_vas, on = 'pp_set', how = 'outer') df_set['VAS_mean'] = df_set['VAS_mean'].fillna(0.0) df_set['VAS_std'] = df_set['VAS_std'].fillna(0.0) df_set['VAS_min'] = df_set['VAS_min'].fillna(0.0) df_set['VAS_max'] = df_set['VAS_max'].fillna(0.0) # Determine characteristic VAS df_set = pp_char_vas(df_pp, df_buckling, df_set) # Merge merged_df and df_friction on 'pp_set' and determine characteristic friction df_set = pd.merge(df_set, df_friction, on = 'pp_set', how = 'outer') # Determine characteristic lateral breakout friction df_set = pp_char_fric(df_pp, df_buckling, df_set, prob_exceed_char_fric) # Change labels for print-out df_set = df_set.rename(columns = { 'pp_set': 'Set Label', 'KP_from': 'KP From (m)', 'KP_to': 'KP To (m)', 'no_simulations_with_buckles': 'Number of Simulations with Buckles per Set', 'no_buckles': 'Number of Buckles per Set', 'prob_buckling': 'Probability of Buckling', 'prob_not_buckling': 'Probability of not Buckling', 'VAS_mean': 'Mean of the VAS (m)', 'VAS_std': 'Standard Deviation of the VAS (m)', 'VAS_min': 'Minimum VAS (m)', 'VAS_max': 'Maximum VAS (m)', 'VAS_charac_conditional': 'Characteristic VAS, Conditional (m)', 'VAS_charac_unconditional': 'Characteristic VAS, Unconditional (m)', 'mulat_mean': 'Mean of the Lateral Breakout Friction', 'mulat_std': 'Standard Deviation of the Lateral Breakout Friction', 'mulat_min': 'Minimum Lateral Breakout Friction', 'mulat_max': 'Maximum Lateral Breakout Friction', 'mulat_charac_unconditional': 'Characteristic Lateral Breakout Friction, Buckles'}) # Drop column for print-out df_set = df_set.drop(columns = 'mulat_charac_conditional') # Sort by KP From df_set = df_set.sort_values(by = 'KP From (m)') # Fill characteristic frictions at planned buckles with zero df_set['Characteristic Lateral Breakout Friction Probability'] = \ prob_exceed_char_fric df_set.loc[df_set['Characteristic Lateral Breakout Friction, Buckles'] == 0.0, 'Characteristic Lateral Breakout Friction Probability'] = 0.0 # Define HE of the geotechnical friction df_scen['Lateral Breakout Friction, HE, Geotech'] = \ df_scen.apply(lambda x: np.interp(1.0 - prob_exceed_char_fric, x['mul OP CDF Array'], x['mul OP Array']), axis = 1) df_set['Lateral Breakout Friction, HE, Geotech'] = \ df_set.apply(lambda x: np.interp(x['KP From (m)'], df_scen['KP'], df_scen['Lateral Breakout Friction, HE, Geotech']), axis = 1) # Convert route type strings to descriptive representation df_scen_temp = df_scen.copy() df_scen_temp.loc[df_scen_temp['Route Type'] == 1, 'Route'] = 'Straight' df_scen_temp.loc[df_scen_temp['Route Type'] == 2, 'Route'] = 'Bend' df_scen_temp.loc[df_scen_temp['Route Type'] == 3, 'Route'] = 'Sleeper' df_scen_temp.loc[df_scen_temp['Route Type'] == 4, 'Route'] = 'RCM' # Assign route type to df_set for index, row in df_set.iterrows(): df_set.loc[index, 'Route'] = df_scen_temp.loc[ (df_scen_temp['KP'] > row['KP From (m)']) & (df_scen_temp['KP'] < row['KP To (m)']), 'Route'].values[0] df_set.loc[(df_set['Route'] == 'Sleeper') | (df_set['Route'] == 'RCM'), 'Characteristic Lateral Breakout Friction, Buckles'] = 0.0 # Re-arrange columns df_set = df_set[['Set Label', 'KP From (m)', 'KP To (m)', 'Number of Simulations with Buckles per Set', 'Number of Buckles per Set', 'Probability of Buckling', 'Probability of not Buckling', 'Mean of the VAS (m)', 'Standard Deviation of the VAS (m)', 'Minimum VAS (m)', 'Maximum VAS (m)', 'Characteristic VAS Probability', 'Characteristic VAS, Conditional (m)', 'Characteristic VAS, Unconditional (m)', 'Mean of the Lateral Breakout Friction', 'Standard Deviation of the Lateral Breakout Friction', 'Minimum Lateral Breakout Friction', 'Maximum Lateral Breakout Friction', 'Characteristic Lateral Breakout Friction Probability', 'Characteristic Lateral Breakout Friction, Buckles', 'Lateral Breakout Friction, HE, Geotech']] df_set = df_set.fillna(0.0) df_set['Probability of not Buckling'] = 1.0 - df_set['Probability of Buckling'] return df_set
[docs] def pp_eaf(df_pp_plot): """ Converts units and renames columns for pipeline EAF plot DataFrame. Parameters ---------- df_pp_plot : pandas DataFrame DataFrame containing pipeline plot data. Returns ------- df_pp_plot: pandas DataFrame DataFrame with converted units and renamed columns. """ # Convert the units of 'df_pp_plot' (N to kN) df_pp_plot[['CBF_ht', 'CBF_op', 'EAF_inst', 'EAF_ht', 'EAF_p_op', 'EAF_op', 'EAF_op_unbuck']] /= 1000.0 # Change labels for print-out df_pp_plot = df_pp_plot.rename(columns = { 'KP': 'KP (m)', 'CBF_ht': 'CBF Hydrotest (kN)', 'CBF_op': 'CBF Operation (kN)', 'EAF_inst': 'EAF Installation [RLT] (kN)', 'EAF_ht': 'EAF Hydrotest (kN)', 'EAF_p_op': 'EAF Operation [Pressure Only] (kN)', 'EAF_op': 'EAF Operation (kN)', 'EAF_op_unbuck': 'EAF Operation [without Buckling] (kN)' }) # Drop column for print-out df_pp_plot = df_pp_plot.drop(columns = 'beta2') return df_pp_plot
[docs] def pp_raw(df): """ Converts units and renames columns of the DataFrame containing the raw data from the BuckPy simulations. Parameters ---------- df : pandas DataFrame DataFrame containing raw data from the BuckPy simulations. Returns ------- df : pandas DataFrame DataFrame with converted units and renamed columns. """ # Change labels for print-out df = df.rename(columns = { 'isim': 'Simulation Number', 'KP': 'KP (m)', 'route_type': 'Section Type', 'muax': 'Axial Residual Friction Factor, Operation', 'mulat_op': 'Lateral Breakout Friction Factor, Operation', 'HOOS': 'HOOS Factor', 'CBF_op': 'CBF Operation (kN)', 'VAS_op': 'VAS Operation (m)' }) # Convert units of the CBF (N to kN) df['CBF Operation (kN)'] /= 1000.0 # Rename section types df['Section Type'] = df['Section Type'].astype(object) df.loc[df['Section Type'] == 1, 'Section Type'] = 'Straight' df.loc[df['Section Type'] == 2, 'Section Type'] = 'Bend' df.loc[df['Section Type'] == 3, 'Section Type'] = 'Sleeper' df.loc[df['Section Type'] == 4, 'Section Type'] = 'RCM' # Select the first 10000 rows to optimise the size of the Excel file #TODO: why? if Excel is limited, let's export it to something else as it could be useful to have all case for separate post-processing df = df.iloc[:10000] return df
[docs] def pp_save(output_combination, output_file_name, *args): """ Saves DataFrames to an Excel file with specified formatting. Parameters ---------- output_combination : Boolean Switch to write the most frequent combination set of buckles in the result file. output_file_name : str Name of the output Excel file. df_elem : pandas DataFrame DataFrame containing element data. df_sets : pandas DataFrame DataFrame containing set data. df_no_buckles : pandas DataFrame DataFrame containing data related to the number of buckles. df_pp_plot : pandas DataFrame DataFrame containing force profile data. df_pp_buckle_prop : pandas DataFrame DataFrame containing raw data related to buckling properties. df_comb_per_set : pandas DataFrame DataFrame containing raw data related to KP set combinations based on post-processing set. df_comb_per_section : pandas DataFrame DataFrame containing raw data related to KP set combinations based on route point id. Returns ------- None """ if output_combination: df_elem, df_sets, df_no_buckles, df_pp_plot, df_pp_buckle_prop,\ df_comb_per_set, df_comb_per_section = args else: df_elem, df_sets, df_no_buckles, df_pp_plot, df_pp_buckle_prop = args writer = pd.ExcelWriter(output_file_name) pandas.io.formats.excel.ExcelFormatter.header_style = None # Convert DataFrames to Excel objects df_elem.to_excel(writer, sheet_name = 'Elements', index = False, startrow = 1, header = False) df_sets.to_excel(writer, sheet_name = 'Sets', index = False, startrow = 1, header = False) df_no_buckles.to_excel(writer, sheet_name = 'No Buckles', index = False, startrow = 1, header = False) df_pp_plot.to_excel(writer, sheet_name = 'Force Profiles', index = False, startrow = 1, header = False) df_pp_buckle_prop.to_excel(writer, sheet_name = 'Raw Data', index = False, startrow = 1, header = False) if output_combination: df_comb_per_set.to_excel(writer, sheet_name = 'Comb Buckles per Set', startrow = 1, header = False) df_comb_per_section.to_excel(writer, sheet_name = 'Comb Buckles per Section', startrow = 1, header = False) # Get the workbook and worksheet objects. workbook = writer.book worksheet1 = writer.sheets['Elements'] worksheet2 = writer.sheets['Sets'] worksheet3 = writer.sheets['No Buckles'] worksheet4 = writer.sheets['Force Profiles'] worksheet5 = writer.sheets['Raw Data'] if output_combination: worksheet6 = writer.sheets['Comb Buckles per Set'] worksheet7 = writer.sheets['Comb Buckles per Section'] # Add generic cell formats to Excel file formatc1 = workbook.add_format({'num_format': '#,##0', 'align': 'center', 'valign': 'vcenter', 'text_wrap': True}) formatc2 = workbook.add_format({'num_format': '#,##0.0', 'align': 'center', 'valign': 'vcenter', 'text_wrap': True}) formatc3 = workbook.add_format({'num_format': '0.000', 'align': 'center', 'valign': 'vcenter', 'text_wrap': True}) formath1 = workbook.add_format({'num_format': '#,###', 'bold': True, 'border': 1, 'bg_color': '#C0C0C0', 'align': 'center', 'valign': 'vcenter', 'text_wrap': True}) # Set the column width and format of the Excel worksheets worksheet1.set_column('A:B', 12.5, formatc1) worksheet1.set_column('C:D', 12.5, formatc3) worksheet1.set_column('E:H', 12.5, formatc2) worksheet1.set_column('I:L', 12.5, formatc3) worksheet2.set_column('A:E', 12.5, formatc1) worksheet2.set_column('F:G', 12.5, formatc3) worksheet2.set_column('H:K', 12.5, formatc2) worksheet2.set_column('L:L', 12.5, formatc3) worksheet2.set_column('M:N', 12.5, formatc2) worksheet2.set_column('O:U', 12.5, formatc3) worksheet3.set_column('A:B', 12.5, formatc1) worksheet3.set_column('C:D', 12.5, formatc3) worksheet4.set_column('A:A', 12.5, formatc1) worksheet4.set_column('B:H', 12.5, formatc2) worksheet5.set_column('A:C', 12.5, formatc1) worksheet5.set_column('D:F', 12.5, formatc3) worksheet5.set_column('G:H', 12.5, formatc2) if output_combination: worksheet6.set_column('A:C', 12.5, formatc1) worksheet6.set_column('D:D', 12.5, formatc3) worksheet6.set_column('E:AZ', 12.5, formatc1) worksheet7.set_column('A:C', 12.5, formatc1) worksheet7.set_column('D:D', 12.5, formatc3) worksheet7.set_column('E:AZ', 12.5, formatc1) # Write the column hearders with the defined format for col_num, value in enumerate(df_elem.columns.values): worksheet1.write(0, col_num, value, formath1) for col_num, value in enumerate(df_sets.columns.values): worksheet2.write(0, col_num, value, formath1) for col_num, value in enumerate(df_no_buckles.columns.values): worksheet3.write(0, col_num, value, formath1) for col_num, value in enumerate(df_pp_plot.columns.values): worksheet4.write(0, col_num, value, formath1) for col_num, value in enumerate(df_pp_buckle_prop.columns.values): worksheet5.write(0, col_num, value, formath1) if output_combination: for col_num, value in enumerate(df_comb_per_set.columns.values): worksheet6.write(0, col_num + 1, value[0], formath1) worksheet6.write(1, col_num + 1, value[1], formath1) # Merge header cells for the 3 columns in the first and second row if col_num <= 2: worksheet6.merge_range(0, col_num + 1, 1, col_num + 1, value[1], formath1) # Merge header cells for the 'Number of Buckles' columns in the first row worksheet6.merge_range(0, 4, 0, len(df_comb_per_set.columns.values), df_comb_per_set.columns.levels[0][1], formath1) for col_num, value in enumerate(df_comb_per_section.columns.values): worksheet7.write(0, col_num + 1, value[0], formath1) worksheet7.write(1, col_num + 1, value[1], formath1) # Merge header cells for the 3 columns in the first and second row if col_num <= 2: worksheet7.merge_range(0, col_num + 1, 1, col_num + 1, value[1], formath1) # Merge header cells for the 'Number of Buckles' columns in the first row worksheet7.merge_range(0, 4, 0, len(df_comb_per_section.columns.values), df_comb_per_section.columns.levels[0][1], formath1) # Close the Excel writer and output the Excel file writer.close()
[docs] def pp_outputs(output_file_name, n_sim, output_combination, df_pp_buckle_prop, df_pp_set, df_pp_plot, prob_exceed_char_fric, df_scen): """ Perform post-processing of analysis outputs. Parameters ---------- output_file_name : str Name of the output Excel file n_sim : int Number of simulations. output_combination : Boolean Switch to write the most frequent combination set of buckles in the result file. df_pp_buckle_prop : pandas DataFrame DataFrame containing post-processed buckling properties. df_pp_set : DataFrame Definition of element sets for post-processing outputs. df_pp_plot : pandas DataFrame DataFrame containing post-processed plot data. prob_exceed_char_fric : float Probability of exceedance associated with the characteristic lateral breakout friction. df_pp_set : DataFrame Definition of element sets for post-processing outputs. df_scen : DataFrame Dataframe containing the design data along the pipeline route (mesh) that remains constant among deterministic and Monte-Carlo simulations. Returns ------- df_no_buckles : pandas DataFrame DataFrame containing post-processed statistics about the number of buckles along the pipeline. df_sets : pandas DataFrame DataFrame containing post-processed statistics grouped by pipeline sets. """ # Dataframe containing the raw data df_pp_buckle_prop = df_pp_buckle_prop.sort_values(by = ['KP', 'isim']) df_pp = pd.merge_asof(left = df_pp_buckle_prop, right = df_pp_set, left_on = 'KP', right_on = 'KP_from') # Probability of buckling, VAS and lateral breakout friction sorted by element df_elem = pp_elem(df_pp, n_sim) # Distribution of the expected number of buckles along the pipeline df_no_buckles = pp_no_buckles(df_pp, n_sim) # Probability of buckling, VAS and lateral breakout friction sorted by post-processing set df_sets = pp_sets(df_pp, n_sim, prob_exceed_char_fric, df_pp_set, df_scen) # Post-processing of 'df_pp_plot' for print-out df_pp_plot = pp_eaf(df_pp_plot) # Post-processing of 'df_pp_buckle_prop' for print-out df_pp_buckle_prop = pp_raw(df_pp_buckle_prop) if output_combination: # Calculate the most frequent combination of KP set with buckles df_comb_per_set = pp_comb_buckles_per_set(df_pp, df_pp_set, n_sim) df_comb_per_section = pp_comb_buckles_per_section(df_pp, df_scen, n_sim) # Save key outputs to Excel file pp_save(output_combination, output_file_name, df_elem, df_sets, df_no_buckles, df_pp_plot, df_pp_buckle_prop, df_comb_per_set, df_comb_per_section) else: # Save key outputs to Excel file pp_save(output_combination, output_file_name, df_elem, df_sets, df_no_buckles, df_pp_plot, df_pp_buckle_prop) return df_no_buckles, df_sets
[docs] def pp_plots(plot_file_name, df_scen, df_plot, df_vap_plot, df_no_buckles, df_sets, prob_exceed_char_fric): """ Plot deterministic and probabilistic results Parameters ---------- plot_file_name : str Name of the \*.png file df_scen : DataFrame Dataframe containing the design data along the pipeline route (mesh) that remains constant among deterministic and Monte-Carlo simulations. df_plot : DataFrame Definition on assessed mesh of CBF and EAF in different conditions. for case to plot df_vap_plot : DataFrame Definition of virtual anchor points for case to plot. Columns: ['ielt VAP', 'KP VAP', 'ESF VAP']. df_no_buckles : DataFrame Probability of number of buckles over pipeline. df_sets : DataFrame Probability of buckling and characteristic VAS and friction factors by set. prob_exceed_char_fric : float Probability of exceedance for characteristic friction factors. plot_file_name : str File name where plots are to be saved. """ # Convert the units of 'df_plot', 'df_vap_plot' & 'df_scen' (m to km and N to kN) df_plot[['KP']] /= 1000.0 df_vap_plot[['KP VAP', 'ESF VAP']] /= 1000.0 df_scen[['KP', 'FRF OP Pressure', 'FRF OP Temperature']] /= 1000.0 # Create arrays to plot buckling probability, characteristic VAS and characteristic friction np_kp = np.array([df_sets.iloc[0]['KP From (m)'] / 1000.0]) np_prob = np.array([0.0]) np_vas_cond = np.array([0.0]) np_vas_uncond = np.array([0.0]) np_mul_buckle = np.array([0.0]) for index, row in df_sets.iterrows(): np_kp = np.append(np_kp, np.append( row['KP From (m)'] / 1000.0, row['KP To (m)'] / 1000.0)) np_prob = np.append(np_prob, np.append( 100.0 * row['Probability of Buckling'], 100.0 * row['Probability of Buckling'])) np_vas_cond = np.append(np_vas_cond, np.append( row['Characteristic VAS, Conditional (m)'], row['Characteristic VAS, Conditional (m)'])) np_vas_uncond = np.append(np_vas_uncond, np.append( row['Characteristic VAS, Unconditional (m)'], row['Characteristic VAS, Unconditional (m)'])) np_mul_buckle = np.append(np_mul_buckle, np.append( row['Characteristic Lateral Breakout Friction, Buckles'], row['Characteristic Lateral Breakout Friction, Buckles'])) if index == df_sets.shape[0] - 1: np_kp = np.append(np_kp, row['KP To (m)'] / 1000.0) np_prob = np.append(np_prob, 0.0) np_vas_cond = np.append(np_vas_cond, 0.0) np_vas_uncond = np.append(np_vas_uncond, 0.0) np_mul_buckle = np.append(np_mul_buckle, 0.0) # Create an array to plot the HE of the geotechnical lateral breakdown friction np_mul_kp = np.empty(0) np_mul_geotech = np.empty(0) for index, row in df_scen.iterrows(): # df_scen has at least 2 points, no need to double the points in append functions np_mul_kp = np.append(np_mul_kp, row['KP']) mul_geotech = np.interp(1.0 - prob_exceed_char_fric, row['mul OP CDF Array'], row['mul OP Array']) np_mul_geotech = np.append(np_mul_geotech, mul_geotech) # Generate matplolib figure fig = plt.figure() dpi_size = 110 fig.set_size_inches(19.2 * 100 / dpi_size, 10.8 * 100 / dpi_size) gs = GridSpec(nrows = 2, ncols = 3) # Plot effective axial force profiles from selected case a1=fig.add_subplot(gs[0, :]) a1.plot(df_plot['KP'], df_plot['EAF_inst'], label = 'EAF Installation', color = 'C1') a1.plot(df_plot['KP'], df_plot['EAF_ht'], label = 'EAF Hydrotest', color = 'C2') a1.plot(df_plot['KP'], df_plot['EAF_p_op'], label = 'EAF Operation (Pressure Only)', color = 'C3') a1.plot(df_plot['KP'], df_plot['EAF_op'], label = 'EAF Operation', color = 'C4') a1.plot(df_plot['KP'], df_plot['EAF_op_unbuck'], label = 'EAF Operation (without Buckling)', color = 'C4', linestyle = ':') # Plot intermediate force profile during temperature application for i in range(1, 21): # Shouldnt overwrite existing df_plot['EAF_inst'], separate dataframe used instead eaf_interim = df_scen['FRF OP Pressure'] + (i / 20) * df_scen['FRF OP Temperature'] eaf_interim=np.where(eaf_interim < df_plot['EAF_op_unbuck'].to_numpy(), eaf_interim, np.nan) a1.plot(df_plot['KP'], eaf_interim, color = 'C4', linestyle = '--', alpha = 0.25) #TODO: Indeed, however different sections (routes, straight, sleepers...) can have different ones # and it's interresting to see if area buckle "earlier" than other #? Block commented as all buckling forces per section are constant in the deterministic case # Plot buckling susceptibility areas and actual buckle locations # a1.scatter(df_plot.loc[ # df_plot['CBF_op'] < df_plot['EAF_op_unbuck'], 'KP'], # df_plot.loc[ # df_plot['CBF_op'] < df_plot['EAF_op_unbuck'], 'CBF_op'], # label = 'CBF operation', marker = '.', color = 'C4') # Plot VAP a1.scatter(df_vap_plot['KP VAP'], df_vap_plot['ESF VAP'], marker = '8', c = 'none', edgecolors = 'C3', label = 'VAP') a1.set_xlabel('KP [km]') a1.set_ylabel('Effective Force [kN]') a1.legend() a1.grid() # Plot distribution of number of buckles a2 = fig.add_subplot(gs[1, 0]) a2.plot(df_no_buckles['Number of Buckles'], 100.0 * df_no_buckles['Probability of Buckling'], color = 'C1') a2.set_xlabel('Number of Buckles') a2.set_ylabel('Probability [%]') a2.grid() # Plot lateral friction factor versus location a3 = fig.add_subplot(gs[1, 1]) a3.plot(np_kp, np_mul_buckle, label = f'P{int(100 * prob_exceed_char_fric)} Buckle Friction', color = 'C1') a3.plot(np_mul_kp, np_mul_geotech, label = f'P{int(100 * prob_exceed_char_fric)} Geotech Friction', color = 'C2') a3.set_xlabel('KP [km]') a3.set_ylabel('Lateral Breakout Friction Factor (Operation)') a3.legend() a3.grid() # Plot probability of buckling and characteristic VAS a4 = fig.add_subplot(gs[1, 2]) a4_twin = a4.twinx() # a4.plot(np_kp, np_vas_cond, label = 'Conditional VAS', # color = 'C1') a4.plot(np_kp, np_vas_uncond, label = 'Unconditional VAS', color = 'C2') a4_twin.plot(np_kp, np_prob, label = 'Probability of Buckling', linestyle = ':', color = 'C3') a4.set_xlabel('KP [km]') a4.set_ylabel('Characteristic VAS [m]') a4_twin.set_ylabel('Buckling Probability [%]') line4, label4 = a4.get_legend_handles_labels() line4_twin, label4_twin = a4_twin.get_legend_handles_labels() a4.legend(line4 + line4_twin, label4 + label4_twin) a4.grid() fig_manager=plt.get_current_fig_manager() try: fig_manager.window.state('zoomed') except AttributeError: pass try: fig_manager.window.showMaximized() except AttributeError: pass plt.subplots_adjust( left = 0.1, bottom = 0.1, right = 0.95, top = 0.925, wspace = 0.225, hspace = 0.2) plt.savefig(plot_file_name, dpi = dpi_size) # plt.show() plt.close()
[docs] def pp_buckpy(work_dir, input_file_name, pipeline_id, scenario_no, prob_exceed_char_fric, df_pp_plot, df_vap_plot, df_pp_buckle_prop, df_scen, df_pp_set, n_sim, output_combination, bl_verbose = False): """ Post-processing of probabilistic buckling results. Parameters ---------- work_dir : str The working directory where the analysis files are located. input_file_name : str The name of the input file. pipeline_id : str Identifier of the pipeline. scenario_no : int The scenario number. prob_exceed_char_fric : float Probability of exceedance for the calculation of the characteristic friction factor. df_pp_plot : DataFrame Definition on assessed mesh of CBF and EAF in different conditions for case to plot. df_vap_plot : DataFrame Definition of virtual anchor points for case to plot. df_pp_buckle_prop : DataFrame Results for all buckles triggered. df_scen : DataFrame Dataframe containing the design data along the pipeline route (mesh) that remains constant among deterministic and Monte-Carlo simulations. df_pp_set : DataFrame Definition of element sets for post-processing outputs. n_sim : int Number of Monte-Carlo simulations to be run. output_combination : Boolean Switch to write the most frequent combination set of buckles in the result file. """ # Starting time of the post-processing module start_time = time.time() # Print in the terminal that the post-processing of the results has started if bl_verbose: print("4. Post-process results") # Calculate probabilistic outputs and save outputs to Excel file output_file_name = f"{work_dir}/{input_file_name.split('.')[0]}_{pipeline_id}_scen{scenario_no}_outputs.xlsx" df_prob_n_buckle, df_prob_set = pp_outputs(output_file_name, n_sim, output_combination, df_pp_buckle_prop, df_pp_set, df_pp_plot, prob_exceed_char_fric, df_scen) # Print in the terminal the time taken to post-process results if bl_verbose: print(f' Time taken to post-process results: {time.time() - start_time:.1f}s') # Plot post-processed results and save figure to file plot_file_name = f"{work_dir}/{input_file_name.split('.')[0]}_{pipeline_id}_scen{scenario_no}_plots-1.png" pp_plots(plot_file_name, df_scen, df_pp_plot, df_vap_plot, df_prob_n_buckle, df_prob_set, prob_exceed_char_fric)