buckpy-dev 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- buckpy/__init__.py +13 -0
- buckpy/_static/logo.png +0 -0
- buckpy/_static/logo.svg +4 -0
- buckpy/buckfast_input_file_writer_.py +1233 -0
- buckpy/buckpy.py +132 -0
- buckpy/buckpy_gui.py +359 -0
- buckpy/buckpy_postprocessing.py +1305 -0
- buckpy/buckpy_preprocessing_current.py +1142 -0
- buckpy/buckpy_preprocessing_legacy.py +900 -0
- buckpy/buckpy_solver.py +777 -0
- buckpy/buckpy_variables.py +98 -0
- buckpy/buckpy_visualisation.py +419 -0
- buckpy_dev-0.0.1.dist-info/METADATA +51 -0
- buckpy_dev-0.0.1.dist-info/RECORD +18 -0
- buckpy_dev-0.0.1.dist-info/WHEEL +5 -0
- buckpy_dev-0.0.1.dist-info/entry_points.txt +2 -0
- buckpy_dev-0.0.1.dist-info/licenses/LICENSE +674 -0
- buckpy_dev-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the pre-processing functions of BuckPy.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from scipy.stats import lognorm
|
|
9
|
+
import pysubsea as ss
|
|
10
|
+
from .buckpy_variables import KP_TO
|
|
11
|
+
|
|
12
|
+
def calc_expand_kp(df):
|
|
13
|
+
|
|
14
|
+
'''
|
|
15
|
+
Function to expand the KP array with 1000 intervals from 1000 to nearest maximum KP.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
df : pandas Dataframe
|
|
20
|
+
Dataframe containing the original KP values.
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
df : pandas Dataframe
|
|
25
|
+
Dataframe containing the expanded KP values.
|
|
26
|
+
'''
|
|
27
|
+
|
|
28
|
+
# Rename 'KP To' to 'KP From'
|
|
29
|
+
df = df.rename(columns = {'KP To': 'KP From'})
|
|
30
|
+
|
|
31
|
+
# Expand the KP array with 1000 intervals from 1000 to nearest maximum KP
|
|
32
|
+
max_kp = np.floor(df['KP From'].max() / 1000.0) * 1000.0
|
|
33
|
+
kp_array = np.arange(1000, max_kp + 1.0, 1000)
|
|
34
|
+
|
|
35
|
+
# Create a dataframe for the expanded kp
|
|
36
|
+
df_expand = pd.DataFrame({'Point ID From': [np.nan] * len(kp_array), 'KP From': kp_array})
|
|
37
|
+
df = pd.concat([df, df_expand], ignore_index = True).sort_values(
|
|
38
|
+
by = 'KP From').drop_duplicates('KP From').reset_index(drop = True).ffill()
|
|
39
|
+
|
|
40
|
+
# Calculate relative length between KP and KP To
|
|
41
|
+
df['KP To'] = df['KP From'].shift(-1)
|
|
42
|
+
df = df.dropna()
|
|
43
|
+
df['Length'] = df['KP To'] - df['KP From']
|
|
44
|
+
|
|
45
|
+
# Calculate element number and element size
|
|
46
|
+
df['Elem No.'] = np.ceil(df['Length'] / 100.0)
|
|
47
|
+
df['Elem Size'] = df['Length'] / df['Elem No.']
|
|
48
|
+
|
|
49
|
+
return df
|
|
50
|
+
|
|
51
|
+
def calc_element_array(df):
|
|
52
|
+
|
|
53
|
+
'''
|
|
54
|
+
Function to create element array based on KP, KP TO and element number.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
df : pandas Dataframe
|
|
59
|
+
Dataframe containing the expanded KP values.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
df : pandas Dataframe
|
|
64
|
+
Dataframe containing the elements between each KP value.
|
|
65
|
+
'''
|
|
66
|
+
|
|
67
|
+
# Create the elements between each KP points
|
|
68
|
+
elem_array = np.empty(0)
|
|
69
|
+
elem_array = df.apply(lambda x: pd.Series(np.append(elem_array, np.linspace(
|
|
70
|
+
x['KP From'], x['KP To'], int(x['Elem No.'] + 1.0)))), axis = 1)
|
|
71
|
+
|
|
72
|
+
# Convert the element dataframe to np array and flatten
|
|
73
|
+
elem_array = elem_array.to_numpy().flatten()
|
|
74
|
+
|
|
75
|
+
# Remove duplicated values at 1000*n and np.nan
|
|
76
|
+
elem_array = np.unique(elem_array)
|
|
77
|
+
elem_array = elem_array[~np.isnan(elem_array)]
|
|
78
|
+
|
|
79
|
+
return elem_array
|
|
80
|
+
|
|
81
|
+
def calc_kp_interpolation(elem_array, df_oper):
|
|
82
|
+
|
|
83
|
+
'''
|
|
84
|
+
Function to interpolate the RLT, pressure and temperature using KP and operating profile.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
elem_array : np Array
|
|
89
|
+
Array containing the kp value of the elements.
|
|
90
|
+
df_oper : pandas Dataframe
|
|
91
|
+
Dataframe containing the original operating profiles data.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
df : pandas Dataframe
|
|
96
|
+
Dataframe containing the interpolated operating profiles data.
|
|
97
|
+
'''
|
|
98
|
+
|
|
99
|
+
# Interpolate operating profile based on KP
|
|
100
|
+
df = pd.DataFrame({'KP': elem_array})
|
|
101
|
+
df['Pressure Installation'] = np.interp(
|
|
102
|
+
df['KP'], df_oper['KP'], df_oper['Pressure Installation'])
|
|
103
|
+
df['Pressure Hydrotest'] = np.interp(
|
|
104
|
+
df['KP'], df_oper['KP'], df_oper['Pressure Hydrotest'])
|
|
105
|
+
df['Pressure Operation'] = np.interp(
|
|
106
|
+
df['KP'], df_oper['KP'], df_oper['Pressure Operation'])
|
|
107
|
+
df['Temperature Installation'] = np.interp(
|
|
108
|
+
df['KP'], df_oper['KP'], df_oper['Temperature Installation'])
|
|
109
|
+
df['Temperature Hydrotest'] = np.interp(
|
|
110
|
+
df['KP'], df_oper['KP'], df_oper['Temperature Hydrotest'])
|
|
111
|
+
df['Temperature Operation'] = np.interp(
|
|
112
|
+
df['KP'], df_oper['KP'], df_oper['Temperature Operation'])
|
|
113
|
+
df['RLT'] = np.interp(df['KP'], df_oper['KP'], df_oper['RLT'])
|
|
114
|
+
|
|
115
|
+
return df
|
|
116
|
+
|
|
117
|
+
def calc_operating_profiles(df, df_route, pipeline_set, loadcase_set):
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
Calculate operating profiles data and process it.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
df : pandas.DataFrame
|
|
125
|
+
DataFrame containing the operating profiles data.
|
|
126
|
+
df_route : pandas.DataFrame
|
|
127
|
+
DataFrame containing route data and calculated route data.
|
|
128
|
+
pipeline_set : str
|
|
129
|
+
Identifier of the pipeline set.
|
|
130
|
+
loadcase_set : str
|
|
131
|
+
Identifier of the loadcase set.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
df : pandas.DataFrame
|
|
136
|
+
DataFrame containing the operating profiles data and calculated operating data.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
# Filter df DataFrame based on pipeline_set and loadcase_set
|
|
140
|
+
df_profile = df.loc[(df['Pipeline'] == pipeline_set) & (df['Loadcase Set'] == loadcase_set)]
|
|
141
|
+
|
|
142
|
+
# Select the 'Point ID From' and 'KP To' columns
|
|
143
|
+
df_route = df_route[['Point ID From', 'KP To']].reset_index(drop = True)
|
|
144
|
+
|
|
145
|
+
# Add the end row of route and the start KP
|
|
146
|
+
end_row = pd.DataFrame({'Point ID From': 'End', 'KP To': np.nan}, index = [99999])
|
|
147
|
+
df_route = pd.concat([df_route, end_row], ignore_index = True)
|
|
148
|
+
|
|
149
|
+
# Shift KP column 1 downwards and assign 0.0 to the first KP
|
|
150
|
+
df_route['KP To'] = df_route['KP To'].shift().fillna(0.0)
|
|
151
|
+
|
|
152
|
+
# Expand the KP array with 1000 intervals from 1000 to nearest maximum KP
|
|
153
|
+
df_route = calc_expand_kp(df_route)
|
|
154
|
+
|
|
155
|
+
# Create the elements between each KP points
|
|
156
|
+
elem_array = calc_element_array(df_route)
|
|
157
|
+
|
|
158
|
+
# Interpolate the RLT, pressure and temperature using KP and operating profile
|
|
159
|
+
df = calc_kp_interpolation(elem_array, df_profile)
|
|
160
|
+
|
|
161
|
+
# Insert pipeline_set and loadcase_set columns as the first and second columns
|
|
162
|
+
df.insert(0, 'Pipeline', [pipeline_set] * df.shape[0])
|
|
163
|
+
df.insert(1, 'Loadcase Set', [loadcase_set] * df.shape[0])
|
|
164
|
+
|
|
165
|
+
return df
|
|
166
|
+
|
|
167
|
+
def calc_route_data(df, layout_set, pipeline_set):
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
Extract and process route data for calculations.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
df : pandas.DataFrame
|
|
175
|
+
DataFrame containing route data.
|
|
176
|
+
layout_set : str
|
|
177
|
+
Identifier of the layout set.
|
|
178
|
+
pipeline_set : str
|
|
179
|
+
Identifier of the pipeline set.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
df : pandas.DataFrame
|
|
184
|
+
DataFrame containing route data and calculated route data.
|
|
185
|
+
df_ends : pandas.DataFrame
|
|
186
|
+
DataFrame containing end boundary conditions.
|
|
187
|
+
|
|
188
|
+
Notes
|
|
189
|
+
-----
|
|
190
|
+
This function extracts route ends and route data based on pipeline_set and layout_set. It
|
|
191
|
+
selects specific columns for route ends data. Route Type is converted from string to
|
|
192
|
+
float for numerical representation. Route ends data is converted to a NumPy array for
|
|
193
|
+
efficient processing.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
# Extract route ends and route data based on pipeline_set and layout_set
|
|
197
|
+
df_ends = df.loc[(df['Pipeline'] == pipeline_set) &
|
|
198
|
+
(df['Layout Set'] == layout_set)].iloc[[0, -1]]
|
|
199
|
+
df = df.loc[(df['Pipeline'] == pipeline_set) &
|
|
200
|
+
(df['Layout Set'] == layout_set)].iloc[1:-1]
|
|
201
|
+
|
|
202
|
+
# Select specific columns for route ends data
|
|
203
|
+
df_ends = df_ends[['Route Type', 'KP From', 'KP To', 'Reaction Installation',
|
|
204
|
+
'Reaction Hydrotest', 'Reaction Operation']]
|
|
205
|
+
|
|
206
|
+
# Convert 'Route Type' from string to float for numerical representation
|
|
207
|
+
df_ends.loc[df_ends['Route Type'] == 'Spool', 'Route Type'] = 1
|
|
208
|
+
df_ends.loc[df_ends['Route Type'] == 'Fixed', 'Route Type'] = 2
|
|
209
|
+
df_ends['Route Type'] = df_ends['Route Type'].astype(float)
|
|
210
|
+
|
|
211
|
+
# Convert KP From and KP to to float
|
|
212
|
+
df[['KP From', 'KP To']] = df[['KP From', 'KP To']].astype(float)
|
|
213
|
+
|
|
214
|
+
return df, df_ends
|
|
215
|
+
|
|
216
|
+
def calc_pipe_data(df, pipeline_set):
|
|
217
|
+
|
|
218
|
+
"""
|
|
219
|
+
Calculate properties of pipes for a specific pipeline set.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
df : pandas.DataFrame
|
|
224
|
+
DataFrame containing the pipe data.
|
|
225
|
+
pipeline_set : str
|
|
226
|
+
Identifier of the pipeline set.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
df : pandas.DataFrame
|
|
231
|
+
DataFrame containing the pipe data and calculated pipe properties.
|
|
232
|
+
|
|
233
|
+
Notes
|
|
234
|
+
-----
|
|
235
|
+
This function filters the df DataFrame based on the pipeline_set. It computes the
|
|
236
|
+
inner diameter (ID), cross-sectional area (As), inner area (Ai), moment of inertia (I),
|
|
237
|
+
hydrotest characteristic buckling force (SChar HT), and operation characteristic buckling
|
|
238
|
+
force (SChar OP) of the pipe.
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
# Compute the inner diameter (ID) of the pipe
|
|
242
|
+
df['ID'] = df['OD'] - 2.0 * df['WT']
|
|
243
|
+
|
|
244
|
+
# Compute the cross-sectional area (As) of the pipe
|
|
245
|
+
df['As'] = np.pi / 4.0 * (df['OD'] ** 2 - df['ID'] ** 2)
|
|
246
|
+
|
|
247
|
+
# Compute the inner area (Ai) of the pipe
|
|
248
|
+
df['Ai'] = np.pi / 4.0 * df['ID'] ** 2
|
|
249
|
+
|
|
250
|
+
# Compute the moment of inertia (I) of the pipe
|
|
251
|
+
df['I'] = np.pi / 64.0 * (df['OD'] ** 4 - df['ID'] ** 4)
|
|
252
|
+
|
|
253
|
+
# Compute the hydrotest characteristic buckling force (SChar HT) of the pipe
|
|
254
|
+
df['SChar HT'] = 2.26 * (df['E'] * df['As']) ** 0.25 * (
|
|
255
|
+
df['E'] * df['I']) ** 0.25 * df['sw Hydrotest'] ** 0.5
|
|
256
|
+
|
|
257
|
+
# Compute the operation characteristic buckling force (SChar OP) of the pipe
|
|
258
|
+
df['SChar OP'] = 2.26 * (df['E'] * df['As']) ** 0.25 * (
|
|
259
|
+
df['E'] * df['I']) ** 0.25 * df['sw Operation'] ** 0.5
|
|
260
|
+
|
|
261
|
+
# Filter df DataFrame based on pipeline_set
|
|
262
|
+
df = df.loc[(df['Pipeline'] == pipeline_set)]
|
|
263
|
+
|
|
264
|
+
return df
|
|
265
|
+
|
|
266
|
+
def calc_oper_data(df, df_route_ends, pipeline_set, loadcase_set):
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
Calculate operating data and process it.
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
df : pandas.DataFrame
|
|
274
|
+
DataFrame containing the operating data.
|
|
275
|
+
df_route_ends : pandas.DataFrame
|
|
276
|
+
DataFrame containing the end boundary conditions.
|
|
277
|
+
pipeline_set : str
|
|
278
|
+
Identifier of the pipeline set.
|
|
279
|
+
loadcase_set : str
|
|
280
|
+
Identifier of the loadcase set.
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
df : pandas.DataFrame
|
|
285
|
+
DataFrame containing the operating data and calculated operating data.
|
|
286
|
+
|
|
287
|
+
Notes
|
|
288
|
+
-----
|
|
289
|
+
This function filters df DataFrame based on pipeline_set, loadcase_set, and 'KP To'.
|
|
290
|
+
It calculates rolling mean and difference, assigns the 'Length' column, resets the index, and
|
|
291
|
+
drops rows with NaN values before returning the preprocessed DataFrame.
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
# Filter df DataFrame based on pipeline_set, loadcase_set and 'KP To'
|
|
295
|
+
df = df.loc[(df['Pipeline'] == pipeline_set) &
|
|
296
|
+
(df['Loadcase Set'] == loadcase_set) &
|
|
297
|
+
(df['KP'] <= df_route_ends['KP To'].iloc[-1])]
|
|
298
|
+
|
|
299
|
+
# Calculate the rolling mean of df grouped by Pipeline and Loadcase Set
|
|
300
|
+
df_rolling_mean = df.groupby(['Pipeline', 'Loadcase Set']).rolling(2).mean()
|
|
301
|
+
|
|
302
|
+
# Calculate the rolling difference of df grouped by Pipeline and Loadcase Set
|
|
303
|
+
df_rolling_difference = df.groupby(
|
|
304
|
+
['Pipeline', 'Loadcase Set']).rolling(2).max() - df.groupby(
|
|
305
|
+
['Pipeline', 'Loadcase Set']).rolling(2).min()
|
|
306
|
+
|
|
307
|
+
# Assign the 'Length' column in df_rolling_mean
|
|
308
|
+
df_rolling_mean['Length'] = df_rolling_difference['KP']
|
|
309
|
+
|
|
310
|
+
# Reset the index of df_rolling_mean and drop the 'level_2' index level
|
|
311
|
+
df_rolling_mean = df_rolling_mean.reset_index().drop('level_2', axis=1)
|
|
312
|
+
|
|
313
|
+
# Drop rows with NaN values
|
|
314
|
+
df_rolling_mean = df_rolling_mean.dropna()
|
|
315
|
+
|
|
316
|
+
return df_rolling_mean
|
|
317
|
+
|
|
318
|
+
def calc_soil_data(df, pipeline_set):
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
Calculate soil data and axial and lateral friction factor distributions and assign them to
|
|
322
|
+
DataFrame columns.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
df : pandas.DataFrame
|
|
327
|
+
DataFrame containing soil data.
|
|
328
|
+
pipeline_set : str
|
|
329
|
+
Identifier of the pipeline set.
|
|
330
|
+
|
|
331
|
+
Returns
|
|
332
|
+
-------
|
|
333
|
+
df : pandas.DataFrame
|
|
334
|
+
DataFrame containing soil data and calculated friction factor distributions.
|
|
335
|
+
|
|
336
|
+
Notes
|
|
337
|
+
-----
|
|
338
|
+
This function filters df DataFrame based on pipeline_set value. It computes lognormal
|
|
339
|
+
distributions for axial and lateral friction factors and assigns them to DataFrame columns.
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
# Compute lognormal or normal distributions for axial friction and assign arrays to DataFrame columns
|
|
343
|
+
df['muax Array'], df['muax CDF Array'] = zip(
|
|
344
|
+
*df.apply(
|
|
345
|
+
lambda x: calc_lognorm_soil(x['Axial Mean'], x['Axial STD']),
|
|
346
|
+
axis=1
|
|
347
|
+
).apply(np.array)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Compute lognormal distributions for lateral hydrotest friction and assign arrays to DataFrame columns
|
|
351
|
+
df['mul HT Array'], df['mul HT CDF Array'] = zip(
|
|
352
|
+
*df.apply(
|
|
353
|
+
lambda x: calc_lognorm_soil(x['Lateral Hydrotest Mean'], x['Lateral Hydrotest STD']),
|
|
354
|
+
axis=1
|
|
355
|
+
).apply(np.array)
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Compute lognormal distributions for lateral operation friction and assign arrays to DataFrame columns
|
|
359
|
+
df['mul OP Array'], df['mul OP CDF Array'] = zip(
|
|
360
|
+
*df.apply(
|
|
361
|
+
lambda x: calc_lognorm_soil(x['Lateral Operation Mean'], x['Lateral Operation STD']),
|
|
362
|
+
axis=1
|
|
363
|
+
).apply(np.array)
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Filter soil data based on pipeline set
|
|
367
|
+
df = df[df['Pipeline'] == pipeline_set]
|
|
368
|
+
|
|
369
|
+
return df
|
|
370
|
+
|
|
371
|
+
def calc_scenario_data(df_scen, df_route, df_pipe, df_oper, df_soil):
|
|
372
|
+
|
|
373
|
+
"""
|
|
374
|
+
Calculate scenario data based on route, pipe, operating, and soil data.
|
|
375
|
+
|
|
376
|
+
Parameters
|
|
377
|
+
----------
|
|
378
|
+
df_scen : pandas.DataFrame
|
|
379
|
+
DataFrame containing scenario data.
|
|
380
|
+
df_route : pandas.DataFrame
|
|
381
|
+
DataFrame containing route data.
|
|
382
|
+
df_pipe : pandas.DataFrame
|
|
383
|
+
DataFrame containing pipe data.
|
|
384
|
+
df_oper : pandas.DataFrame
|
|
385
|
+
DataFrame containing operating data.
|
|
386
|
+
df_soil : pandas.DataFrame
|
|
387
|
+
DataFrame containing soil data.
|
|
388
|
+
|
|
389
|
+
Returns
|
|
390
|
+
-------
|
|
391
|
+
df: pandas.DataFrame
|
|
392
|
+
DataFrame containing the calculated scenario data.
|
|
393
|
+
|
|
394
|
+
Notes
|
|
395
|
+
-----
|
|
396
|
+
This function merges route, pipe, operating, and soil data to compute various scenario
|
|
397
|
+
parameters. It calculates various attributes such as lognormal distributions, buckling forces,
|
|
398
|
+
and section counts. The resulting DataFrame includes a subset of calculated columns and is
|
|
399
|
+
filled with 0 for missing values.
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
# Merge operating data with route data based on 'KP'
|
|
403
|
+
df = pd.merge_asof(left=df_oper, right=df_route, left_on='KP', right_on='KP From',
|
|
404
|
+
direction='backward', left_by='Pipeline', right_by='Pipeline')
|
|
405
|
+
|
|
406
|
+
# Merge resulting DataFrame with pipe data
|
|
407
|
+
df = pd.merge(left=df, right=df_pipe, left_on=['Pipeline', 'Pipe Set'],
|
|
408
|
+
right_on=['Pipeline', 'Pipe Set'])
|
|
409
|
+
|
|
410
|
+
# Merge resulting DataFrame with soil data
|
|
411
|
+
df = pd.merge(left=df, right=df_soil, left_on=['Pipeline', 'Friction Set'],
|
|
412
|
+
right_on=['Pipeline', 'Friction Set'])
|
|
413
|
+
|
|
414
|
+
# Compute lognormal distributions for soil properties and assign to DataFrame columns
|
|
415
|
+
df['HOOS X Array'], df['HOOS CDF Array'] = zip(*df.apply(
|
|
416
|
+
lambda x: calc_lognorm_hoos(x['Route Type'], x['Length'], x['HOOS Mean'],
|
|
417
|
+
x['HOOS STD'], x['HOOS Reference Length'], x['RCM Buckling Force']), axis=1)
|
|
418
|
+
.apply(np.array))
|
|
419
|
+
|
|
420
|
+
# Compute various buckling forces based on calculated parameters
|
|
421
|
+
df['FRF HT'] = df['RLT'] + df['E'] * df['Alpha'] * df['As'] * (
|
|
422
|
+
df['Temperature Hydrotest'] - df['Temperature Installation']) + (
|
|
423
|
+
1 - 2 * df['Poisson']) * (
|
|
424
|
+
df['Pressure Hydrotest'] - df['Pressure Installation']) * df['Ai']
|
|
425
|
+
df['FRF OP'] = df['RLT'] + df['E'] * df['Alpha'] * df['As'] * (
|
|
426
|
+
df['Temperature Operation'] - df['Temperature Installation']) + (
|
|
427
|
+
1 - 2 * df['Poisson']) * (
|
|
428
|
+
df['Pressure Operation'] - df['Pressure Installation']) * df['Ai']
|
|
429
|
+
df['FRF OP Pressure'] = df['RLT'] + (
|
|
430
|
+
1 - 2 * df['Poisson']) * df['Pressure Operation'] * df['Ai']
|
|
431
|
+
df['FRF OP Temperature'] = df['E'] * df['As'] * df['Alpha'] * (
|
|
432
|
+
df['Temperature Operation'] - df['Temperature Installation'])
|
|
433
|
+
df['Sv HT'] = 4.0 * np.sqrt(df['E'] * df['I'] * df['sw Hydrotest'] / df['Sleeper Height'])
|
|
434
|
+
df['Sv OP'] = 4.0 * np.sqrt(df['E'] * df['I'] * df['sw Operation'] / df['Sleeper Height'])
|
|
435
|
+
|
|
436
|
+
# Calculate section-related parameters
|
|
437
|
+
df['KP Section'] = df['KP'] - df['KP From']
|
|
438
|
+
df['Reference Section'] = (df['KP Section'] / df['HOOS Reference Length']).apply(np.floor)
|
|
439
|
+
df['Section Count'] = 0.0
|
|
440
|
+
df.loc[
|
|
441
|
+
(df['Route Type'] != df['Route Type'].shift()) |
|
|
442
|
+
(df['Reference Section'] != df['Reference Section'].shift()), 'Section Count'] = 1.0
|
|
443
|
+
df['Section Count'] = df['Section Count'].cumsum()
|
|
444
|
+
|
|
445
|
+
# Select relevant columns and rename them for clarity
|
|
446
|
+
df = df[[
|
|
447
|
+
'KP', 'Length', 'Route Type', 'KP From', 'KP To', 'Point ID From', 'Point ID To',
|
|
448
|
+
'Bend Radius', 'muax Array', 'muax CDF Array',
|
|
449
|
+
'mul HT Array', 'mul HT CDF Array', 'mul OP Array', 'mul OP CDF Array',
|
|
450
|
+
'HOOS X Array', 'HOOS CDF Array', 'sw Installation', 'sw Hydrotest', 'sw Operation',
|
|
451
|
+
'SChar HT', 'SChar OP', 'Sv HT', 'Sv OP', 'RCM Buckling Force', 'RLT', 'FRF HT',
|
|
452
|
+
'FRF OP Pressure', 'FRF OP Temperature', 'FRF OP', 'Residual Buckle Length Hydrotest',
|
|
453
|
+
'Residual Buckle Force Hydrotest', 'Residual Buckle Length Operation',
|
|
454
|
+
'Residual Buckle Force Operation', 'Section Count', 'KP Section', 'Reference Section',
|
|
455
|
+
'Axial Mean', 'Lateral Hydrotest Mean', 'Lateral Operation Mean', 'HOOS Mean'
|
|
456
|
+
]]
|
|
457
|
+
|
|
458
|
+
df = df.rename(columns={'sw Installation': 'sw IN',
|
|
459
|
+
'sw Hydrotest': 'sw HT',
|
|
460
|
+
'sw Operation': 'sw OP',
|
|
461
|
+
'Residual Buckle Length Hydrotest': 'buckleLength HT',
|
|
462
|
+
'Residual Buckle Force Hydrotest': 'buckleEAF HT',
|
|
463
|
+
'Residual Buckle Length Operation': 'buckleLength OP',
|
|
464
|
+
'Residual Buckle Force Operation': 'buckleEAF OP'})
|
|
465
|
+
|
|
466
|
+
# Convert route type strings to numerical representation
|
|
467
|
+
df.loc[df['Route Type'] == 'Straight', 'Route Type'] = 1
|
|
468
|
+
df.loc[df['Route Type'] == 'Bend', 'Route Type'] = 2
|
|
469
|
+
df.loc[df['Route Type'] == 'Sleeper', 'Route Type'] = 3
|
|
470
|
+
df.loc[df['Route Type'] == 'RCM', 'Route Type'] = 4
|
|
471
|
+
df['Route Type'] = df['Route Type'].astype(float)
|
|
472
|
+
|
|
473
|
+
# Fill missing values with 0
|
|
474
|
+
df = df.fillna(0)
|
|
475
|
+
|
|
476
|
+
# Add scenario parameters to the DataFrame
|
|
477
|
+
df["Pipeline"] = df_scen["Pipeline"].values[0]
|
|
478
|
+
df["Scenario"] = df_scen["Scenario"].values[0]
|
|
479
|
+
df["Layout Set"] = df_scen["Layout Set"].values[0]
|
|
480
|
+
df["Simulations"] = df_scen["Simulations"].values[0]
|
|
481
|
+
df["Friction Sampling"] = df_scen["Friction Sampling"].values[0]
|
|
482
|
+
df["Char. Friction Prob."] = df_scen["Char. Friction Prob."].values[0]
|
|
483
|
+
|
|
484
|
+
return df
|
|
485
|
+
|
|
486
|
+
def calc_monte_carlo_data(df, df_ends):
|
|
487
|
+
|
|
488
|
+
"""
|
|
489
|
+
Convert the scenario data and pipeline end boundary conditions data to NumPy arrays for
|
|
490
|
+
Monte Carlo simulations.
|
|
491
|
+
|
|
492
|
+
Parameters
|
|
493
|
+
----------
|
|
494
|
+
df : pandas.DataFrame
|
|
495
|
+
DataFrame containing the scenario data.
|
|
496
|
+
df_ends : pandas.DataFrame
|
|
497
|
+
DataFrame containing the pipeline end boundary conditions data.
|
|
498
|
+
|
|
499
|
+
Returns
|
|
500
|
+
-------
|
|
501
|
+
np_distr : numpy.ndarray
|
|
502
|
+
2D array with probabilistic distributions (rows) along the route mesh (columns).
|
|
503
|
+
np_scen : numpy.ndarray
|
|
504
|
+
2D array with scenario properties (rows) along the route mesh (columns).
|
|
505
|
+
np_ends : numpy.ndarray
|
|
506
|
+
2D array with end properties (rows) for the pipeline ends.
|
|
507
|
+
|
|
508
|
+
Notes
|
|
509
|
+
-----
|
|
510
|
+
The arrays have the following row layout (index : meaning):
|
|
511
|
+
|
|
512
|
+
np_distr:
|
|
513
|
+
- 0 : MUAX_ARRAY
|
|
514
|
+
- 1 : MUAX_CDF_ARRAY
|
|
515
|
+
- 2 : MULAT_ARRAY_HT
|
|
516
|
+
- 3 : MULAT_CDF_ARRAY_HT
|
|
517
|
+
- 4 : MULAT_ARRAY_OP
|
|
518
|
+
- 5 : MULAT_CDF_ARRAY_OP
|
|
519
|
+
- 6 : HOOS_ARRAY
|
|
520
|
+
- 7 : HOOS_CDF_ARRAY
|
|
521
|
+
|
|
522
|
+
np_scen:
|
|
523
|
+
- 0 : KP
|
|
524
|
+
- 1 : LENGTH
|
|
525
|
+
- 2 : ROUTE_TYPE
|
|
526
|
+
- 3 : BEND_RADIUS
|
|
527
|
+
- 4 : SW_INST
|
|
528
|
+
- 5 : SW_HT
|
|
529
|
+
- 6 : SW_OP
|
|
530
|
+
- 7 : SCHAR_HT
|
|
531
|
+
- 8 : SCHAR_OP
|
|
532
|
+
- 9 : SV_HT
|
|
533
|
+
- 10 : SV_OP
|
|
534
|
+
- 11 : CBF_RCM
|
|
535
|
+
- 12 : RLT
|
|
536
|
+
- 13 : FRF_HT
|
|
537
|
+
- 14 : FRF_P_OP
|
|
538
|
+
- 15 : FRF_T_OP
|
|
539
|
+
- 16 : FRF_OP
|
|
540
|
+
- 17 : L_BUCKLE_HT
|
|
541
|
+
- 18 : EAF_BUCKLE_HT
|
|
542
|
+
- 19 : L_BUCKLE_OP
|
|
543
|
+
- 20 : EAF_BUCKLE_OP
|
|
544
|
+
- 21 : SECTION_ID
|
|
545
|
+
- 22 : SECTION_KP
|
|
546
|
+
- 23 : SECTION_REF
|
|
547
|
+
- 24 : MUAX_MEAN
|
|
548
|
+
- 25 : MULAT_HT_MEAN
|
|
549
|
+
- 26 : MULAT_OP_MEAN
|
|
550
|
+
- 27 : HOOS_MEAN
|
|
551
|
+
|
|
552
|
+
np_ends:
|
|
553
|
+
- 0 : ROUTE_TYPE
|
|
554
|
+
- 1 : KP_FROM
|
|
555
|
+
- 2 : KP_TO
|
|
556
|
+
- 3 : REAC_INST
|
|
557
|
+
- 4 : REAC_HT
|
|
558
|
+
- 5 : REAC_OP
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
# Convert probabilistic distributions to numpy array
|
|
562
|
+
list_temp1 = []
|
|
563
|
+
prob_label_list = [
|
|
564
|
+
'muax Array', 'muax CDF Array', 'mul HT Array', 'mul HT CDF Array',
|
|
565
|
+
'mul OP Array', 'mul OP CDF Array', 'HOOS X Array', 'HOOS CDF Array'
|
|
566
|
+
]
|
|
567
|
+
for array_label in prob_label_list:
|
|
568
|
+
list_temp2 = []
|
|
569
|
+
for i in range(df[array_label].size):
|
|
570
|
+
list_temp2.append(df[array_label][i])
|
|
571
|
+
list_temp1.append(list_temp2)
|
|
572
|
+
np_distr = np.array(list_temp1, dtype='float64')
|
|
573
|
+
|
|
574
|
+
# Add extra columns to remove
|
|
575
|
+
columns_drop = [
|
|
576
|
+
"Pipeline", "Scenario", "Simulations", "Friction Sampling", "Char. Friction Prob.",
|
|
577
|
+
'KP From', 'KP To', 'Point ID From', 'Point ID To'
|
|
578
|
+
]
|
|
579
|
+
columns_drop = np.append(columns_drop, prob_label_list)
|
|
580
|
+
|
|
581
|
+
# Convert scenario properties to numpy array
|
|
582
|
+
np_scen = df.drop(columns_drop, axis=1).to_numpy().transpose()
|
|
583
|
+
|
|
584
|
+
# Convert end properties to numpy array
|
|
585
|
+
np_ends = df_ends.to_numpy().transpose()
|
|
586
|
+
|
|
587
|
+
return np_distr, np_scen, np_ends
|
|
588
|
+
|
|
589
|
+
def calc_pp_data(df, np_array, pipeline_id, layout_set):
|
|
590
|
+
|
|
591
|
+
"""
|
|
592
|
+
Calculate post-processing data set for a given layout set.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
df : pandas.DataFrame
|
|
597
|
+
DataFrame containing post-processing data.
|
|
598
|
+
np_array : numpy.ndarray
|
|
599
|
+
NumPy array containing pipeline end boundary conditions.
|
|
600
|
+
pipeline_id : str
|
|
601
|
+
Identifier of the pipeline.
|
|
602
|
+
layout_set : str
|
|
603
|
+
Identifier of the layout set.
|
|
604
|
+
|
|
605
|
+
Returns
|
|
606
|
+
-------
|
|
607
|
+
df : pandas.DataFrame
|
|
608
|
+
DataFrame containing calculated post-processing data.
|
|
609
|
+
|
|
610
|
+
Notes
|
|
611
|
+
-----
|
|
612
|
+
This function filters the DataFrame based on the layout set. It resets the index, renames
|
|
613
|
+
columns, and selects relevant columns. Adjusts the last 'KP_to' value if it is smaller
|
|
614
|
+
than the maximum value in np_array. Converts data types of columns to appropriate numeric
|
|
615
|
+
types.
|
|
616
|
+
"""
|
|
617
|
+
|
|
618
|
+
# Filter DataFrame based on layout_set
|
|
619
|
+
df = df.loc[(df['Pipeline'] == pipeline_id) & (df['Layout Set'] == layout_set)]
|
|
620
|
+
|
|
621
|
+
# Reset index, rename columns, and select relevant columns
|
|
622
|
+
df = df.reset_index(drop=True).rename(columns={'Post-Processing Set': 'pp_set',
|
|
623
|
+
'KP From': 'KP_from',
|
|
624
|
+
'KP To': 'KP_to',
|
|
625
|
+
'Post-Processing Description': 'description'})
|
|
626
|
+
df = df[['pp_set', 'KP_from', 'KP_to', 'description', 'Characteristic VAS Probability']]
|
|
627
|
+
|
|
628
|
+
# Adjust last 'KP_to' value if necessary
|
|
629
|
+
kp_max = np_array[KP_TO, -1]
|
|
630
|
+
if kp_max > (df['KP_to'].iloc[-1]):
|
|
631
|
+
df.loc[df.index[-1], 'KP_to'] = kp_max
|
|
632
|
+
|
|
633
|
+
# Convert columns to appropriate numeric types
|
|
634
|
+
df['pp_set'] = df['pp_set'].astype(np.int64)
|
|
635
|
+
df['KP_from'] = df['KP_from'].astype(np.float64)
|
|
636
|
+
df['KP_to'] = df['KP_to'].astype(np.float64)
|
|
637
|
+
|
|
638
|
+
return df
|
|
639
|
+
|
|
640
|
+
def run(work_dir, file_name, pipeline_id, scenario_no, bl_verbose):
|
|
641
|
+
|
|
642
|
+
"""
|
|
643
|
+
Import scenario data from an Excel file and preprocess it.
|
|
644
|
+
|
|
645
|
+
Parameters
|
|
646
|
+
----------
|
|
647
|
+
work_dir : str
|
|
648
|
+
Directory where the Excel file is located.
|
|
649
|
+
file_name : str
|
|
650
|
+
Name of the Excel file.
|
|
651
|
+
pipeline_id : str
|
|
652
|
+
Identifier of the pipeline.
|
|
653
|
+
scenario_no : int
|
|
654
|
+
Identifier of the scenario.
|
|
655
|
+
|
|
656
|
+
Returns
|
|
657
|
+
-------
|
|
658
|
+
df_scen : pandas.DataFrame
|
|
659
|
+
Dataframe containing the scenario data
|
|
660
|
+
np_distr : numpy.ndarray
|
|
661
|
+
Array containing the friction factor distributions
|
|
662
|
+
np_scen : numpy.ndarray
|
|
663
|
+
Array containing the scenario data
|
|
664
|
+
np_ends : numpy.ndarray
|
|
665
|
+
Array containing the end boundary conditions
|
|
666
|
+
df_pp : pandas.DataFrame
|
|
667
|
+
Array containing the post-processing data
|
|
668
|
+
|
|
669
|
+
Notes
|
|
670
|
+
-----
|
|
671
|
+
This function reads scenario data from an Excel file and preprocesses it. It extracts layout,
|
|
672
|
+
pipeline, and loadcase sets, and the number of simulations from the Excel file. Postprocesses
|
|
673
|
+
route, pipe, operating, soil, and scenario data. Processes post-processing sets and defines
|
|
674
|
+
the NumPy arrays for Monte Carlo Simulations.
|
|
675
|
+
|
|
676
|
+
Other Parameters
|
|
677
|
+
----------------
|
|
678
|
+
bl_verbose : boolean, optional
|
|
679
|
+
True if intermediate printouts are required (False by default).
|
|
680
|
+
"""
|
|
681
|
+
|
|
682
|
+
# Starting time of the pre-processing module
|
|
683
|
+
start_time = time.time()
|
|
684
|
+
|
|
685
|
+
# Print out in the terminal that the assembly of the main dataframe has started
|
|
686
|
+
if bl_verbose:
|
|
687
|
+
print("1. Assembly of the main dataframe")
|
|
688
|
+
|
|
689
|
+
# Read scenario data from the input Excel file
|
|
690
|
+
df_sens = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name = 'Scenario')
|
|
691
|
+
scenario_no = int(scenario_no)
|
|
692
|
+
|
|
693
|
+
# Define layout, pipeline and loadcase sets and number of simulations
|
|
694
|
+
layout_set = df_sens.loc[(df_sens['Pipeline'] == pipeline_id) &
|
|
695
|
+
(df_sens['Scenario'] == scenario_no), 'Layout Set'].values[0]
|
|
696
|
+
pipeline_set = df_sens.loc[(df_sens['Pipeline'] == pipeline_id) &
|
|
697
|
+
(df_sens['Scenario'] == scenario_no), 'Pipeline'].values[0]
|
|
698
|
+
loadcase_set = df_sens.loc[(df_sens['Pipeline'] == pipeline_id) &
|
|
699
|
+
(df_sens['Scenario'] == scenario_no), 'Loadcase Set'].values[0]
|
|
700
|
+
|
|
701
|
+
# Read route data from the input Excel file and postprocess it
|
|
702
|
+
df_route = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name='Route')
|
|
703
|
+
df_route_input = df_route.copy()
|
|
704
|
+
df_route, df_route_ends = calc_route_data(df_route, layout_set, pipeline_set)
|
|
705
|
+
|
|
706
|
+
# Read pipe data from the input Excel file and postprocess it
|
|
707
|
+
df_pipe = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name = 'Pipe')
|
|
708
|
+
df_pipe = calc_pipe_data(df_pipe, pipeline_set)
|
|
709
|
+
|
|
710
|
+
# Read operating data from the input Excel file and interpolate it
|
|
711
|
+
df_oper = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name = 'Operating')
|
|
712
|
+
df_oper = calc_operating_profiles(df_oper, df_route, pipeline_set, loadcase_set)
|
|
713
|
+
df_oper = calc_oper_data(df_oper, df_route_ends, pipeline_set, loadcase_set)
|
|
714
|
+
|
|
715
|
+
# Read soil data from the input Excel file and postprocess it
|
|
716
|
+
df_soil = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name = 'Soils')
|
|
717
|
+
|
|
718
|
+
# Axial
|
|
719
|
+
df_soil['Axial Mean'], df_soil['Axial STD'] = ss.LBSoilDistributions(
|
|
720
|
+
friction_factor_le=df_soil['Axial LE'],
|
|
721
|
+
friction_factor_be=df_soil['Axial BE'],
|
|
722
|
+
friction_factor_he=df_soil['Axial HE'],
|
|
723
|
+
friction_factor_fit_type=df_soil['Axial Fit Bounds']
|
|
724
|
+
).friction_distribution_parameters()[:2]
|
|
725
|
+
|
|
726
|
+
# Lateral Hydrotest
|
|
727
|
+
df_soil['Lateral Hydrotest Mean'], df_soil['Lateral Hydrotest STD'] = ss.LBSoilDistributions(
|
|
728
|
+
friction_factor_le=df_soil['Lateral Hydrotest LE'],
|
|
729
|
+
friction_factor_be=df_soil['Lateral Hydrotest BE'],
|
|
730
|
+
friction_factor_he=df_soil['Lateral Hydrotest HE'],
|
|
731
|
+
friction_factor_fit_type=df_soil['Lateral Hydrotest Fit Bounds']
|
|
732
|
+
).friction_distribution_parameters()[:2]
|
|
733
|
+
|
|
734
|
+
# Lateral Operation
|
|
735
|
+
df_soil['Lateral Operation Mean'], df_soil['Lateral Operation STD'] = ss.LBSoilDistributions(
|
|
736
|
+
friction_factor_le=df_soil['Lateral Operation LE'],
|
|
737
|
+
friction_factor_be=df_soil['Lateral Operation BE'],
|
|
738
|
+
friction_factor_he=df_soil['Lateral Operation HE'],
|
|
739
|
+
friction_factor_fit_type=df_soil['Lateral Operation Fit Bounds']
|
|
740
|
+
).friction_distribution_parameters()[:2]
|
|
741
|
+
|
|
742
|
+
df_soil = calc_soil_data(df_soil, pipeline_set)
|
|
743
|
+
|
|
744
|
+
# Postprocess scenario data
|
|
745
|
+
df_scen = calc_scenario_data(df_sens, df_route, df_pipe, df_oper, df_soil)
|
|
746
|
+
|
|
747
|
+
# Define the NumPy arrays used in the Monte Carlo Simulations
|
|
748
|
+
np_distr, np_scen, np_ends = calc_monte_carlo_data(df_scen, df_route_ends)
|
|
749
|
+
|
|
750
|
+
# Read post-processing sets from the input Excel file and postprocess them
|
|
751
|
+
df_pp = pd.read_excel(rf'{work_dir}/{file_name}', sheet_name = 'Post-Processing')
|
|
752
|
+
df_pp = calc_pp_data(df_pp, np_ends, pipeline_id, layout_set)
|
|
753
|
+
|
|
754
|
+
# Print out in the terminal time taken to create main dataframe
|
|
755
|
+
if bl_verbose:
|
|
756
|
+
print(f' Time taken to create main dataframe: {time.time() - start_time:.1f}s')
|
|
757
|
+
|
|
758
|
+
return np_distr, np_scen, np_ends, df_scen, df_route_input, df_pp
|
|
759
|
+
|
|
760
|
+
def calc_lognorm_soil(mu_mean, mu_std):
|
|
761
|
+
|
|
762
|
+
"""
|
|
763
|
+
Compute the parameters of a lognormal distribution for friction factors (axial or lateral).
|
|
764
|
+
|
|
765
|
+
Parameters
|
|
766
|
+
----------
|
|
767
|
+
mu_mean : float
|
|
768
|
+
The mean of the friction factor distribution.
|
|
769
|
+
mu_std : float
|
|
770
|
+
The standard deviation of the friction factor distribution.
|
|
771
|
+
|
|
772
|
+
Returns
|
|
773
|
+
-------
|
|
774
|
+
mu_range : numpy.ndarray
|
|
775
|
+
An array of values representing the range of the friction factor distribution
|
|
776
|
+
between probabilities of exceedance between 0.01% and 99.99%.
|
|
777
|
+
cdf_range : numpy.ndarray
|
|
778
|
+
An array of cumulative density function (CDF) values corresponding to `mu_range`.
|
|
779
|
+
|
|
780
|
+
Notes
|
|
781
|
+
-----
|
|
782
|
+
The function calculates the shape and scale parameters of a friction factor lognormal
|
|
783
|
+
distribution based on the provided mean (`mu_mean`) and standard deviation (`mu_std`).
|
|
784
|
+
It then computes the cumulative density function (CDF) for the generated range of values.
|
|
785
|
+
|
|
786
|
+
"""
|
|
787
|
+
|
|
788
|
+
# Calculate shape and scale parameters of the lognormal distribution
|
|
789
|
+
mu_shape = np.sqrt(np.log(1 + mu_std**2 / mu_mean**2))
|
|
790
|
+
mu_scale = np.log(mu_mean**2 / np.sqrt(mu_mean**2 + mu_std**2))
|
|
791
|
+
|
|
792
|
+
# Calculate the lower and upper bounds of the distribution
|
|
793
|
+
mu_lower = lognorm(mu_shape, 0.0, np.exp(mu_scale)).ppf(0.0001)
|
|
794
|
+
mu_upper = lognorm(mu_shape, 0.0, np.exp(mu_scale)).ppf(0.9999)
|
|
795
|
+
|
|
796
|
+
# Generate a range of values within the distribution
|
|
797
|
+
mu_range = np.linspace(mu_lower, mu_upper, 10000)
|
|
798
|
+
|
|
799
|
+
# Compute the cumulative density function (CDF) for the generated range
|
|
800
|
+
cdf_range = lognorm.cdf(mu_range, mu_shape, 0.0, np.exp(mu_scale))
|
|
801
|
+
|
|
802
|
+
return mu_range, cdf_range
|
|
803
|
+
|
|
804
|
+
def calc_lognorm_hoos(type_elt, length_elt, hoos_mean, hoos_std, length_ref, rcm_charac):
|
|
805
|
+
|
|
806
|
+
"""
|
|
807
|
+
Compute the parameters of the horizontal out-of-straightness (HOOS) lognormal distribution
|
|
808
|
+
for different types of elements (e.g., Straight, Bend, Sleeper, RCM). This function takes into
|
|
809
|
+
account the scaling factor of the HOOS distribution. For RCM, the HOOS factor is not a factor
|
|
810
|
+
but the critical buckling force.
|
|
811
|
+
|
|
812
|
+
Parameters
|
|
813
|
+
----------
|
|
814
|
+
type_elt : str
|
|
815
|
+
Type of the element.
|
|
816
|
+
length_elt : float
|
|
817
|
+
Length of the element.
|
|
818
|
+
hoos_mean : float
|
|
819
|
+
Mean of the HOOS distribution.
|
|
820
|
+
hoos_std : float
|
|
821
|
+
Standard deviation of the HOOS distribution.
|
|
822
|
+
length_ref : float
|
|
823
|
+
Reference length.
|
|
824
|
+
rcm_charac : float
|
|
825
|
+
Characteristic buckling force for the Residual Curvature Method (RCM).
|
|
826
|
+
|
|
827
|
+
Returns
|
|
828
|
+
-------
|
|
829
|
+
x_range : numpy.ndarray
|
|
830
|
+
An array of values representing the range of the friction factor distribution
|
|
831
|
+
between probabilities of exceedance between 0.01% and 99.99%.
|
|
832
|
+
cdf_range : numpy.ndarray
|
|
833
|
+
An array of cumulative density function (CDF) values corresponding to `x_range`.
|
|
834
|
+
|
|
835
|
+
Notes
|
|
836
|
+
-----
|
|
837
|
+
This function computes the parameters of a lognormal distribution for different types of
|
|
838
|
+
elements such as Straight, Bend, Sleeper, and RCM (Residual Curvature Method). It
|
|
839
|
+
calculates the cumulative density function (CDF) for the generated range of values
|
|
840
|
+
based on the HOOS distribution parameters.
|
|
841
|
+
|
|
842
|
+
"""
|
|
843
|
+
|
|
844
|
+
# Extract the type of element (e.g., Straight, Bend, Sleeper, RCM)
|
|
845
|
+
type_elt_split = type_elt.split(' ')[0]
|
|
846
|
+
|
|
847
|
+
# Compute the ratio of the reference length to the element length
|
|
848
|
+
n = length_ref / length_elt
|
|
849
|
+
|
|
850
|
+
if type_elt_split == 'Straight' or type_elt_split == 'Bend':
|
|
851
|
+
# Calculate parameters for straight or bend elements
|
|
852
|
+
shape_hoos = np.sqrt(np.log(1 + hoos_std**2 / hoos_mean**2))
|
|
853
|
+
scale_hoos = np.log(hoos_mean**2 / (np.sqrt(hoos_mean**2 + hoos_std**2)))
|
|
854
|
+
|
|
855
|
+
# Define the range of the HOOS distribution
|
|
856
|
+
hoos_lower = 0.0
|
|
857
|
+
hoos_upper = 20.0
|
|
858
|
+
x = np.linspace(hoos_lower, hoos_upper, 200000)
|
|
859
|
+
|
|
860
|
+
# Calculate the cumulative density function (CDF) considering the scaling factor
|
|
861
|
+
cdf = 1-(1-lognorm.cdf(x, shape_hoos, 0.0, np.exp(scale_hoos)))**(1/n)
|
|
862
|
+
|
|
863
|
+
# Generate a range of CDF values
|
|
864
|
+
cdf_range = np.arange(0.0, 1.0, 0.0001)
|
|
865
|
+
|
|
866
|
+
# Interpolate to get the corresponding values of the distribution
|
|
867
|
+
x_range = np.interp(cdf_range, cdf, x)
|
|
868
|
+
|
|
869
|
+
elif type_elt_split == 'Sleeper':
|
|
870
|
+
# Calculate parameters for sleeper elements
|
|
871
|
+
shape_hoos = np.sqrt(np.log(1 + hoos_std**2 / hoos_mean**2))
|
|
872
|
+
scale_hoos = np.log(hoos_mean**2 / (np.sqrt(hoos_mean**2 + hoos_std**2)))
|
|
873
|
+
|
|
874
|
+
# Calculate the lower and upper bounds of the distribution for sleeper elements
|
|
875
|
+
hoos_lower = lognorm(shape_hoos, 0.0, np.exp(scale_hoos)).ppf(0.0001)
|
|
876
|
+
hoos_upper = lognorm(shape_hoos, 0.0, np.exp(scale_hoos)).ppf(0.9999)
|
|
877
|
+
|
|
878
|
+
# Generate a range of values within the distribution
|
|
879
|
+
x_range = np.linspace(hoos_lower, hoos_upper, 10000)
|
|
880
|
+
|
|
881
|
+
# Compute the cumulative density function (CDF) for the generated range
|
|
882
|
+
cdf_range = lognorm.cdf(x_range, shape_hoos, 0.0, np.exp(scale_hoos))
|
|
883
|
+
|
|
884
|
+
elif type_elt_split == 'RCM':
|
|
885
|
+
# Calculate parameters for RCM elements
|
|
886
|
+
shape_hoos = np.sqrt(np.log(1 + hoos_std**2 / hoos_mean**2))
|
|
887
|
+
scale_hoos = np.log(hoos_mean**2 / (np.sqrt(hoos_mean**2 + hoos_std**2)))
|
|
888
|
+
scale_hoos = scale_hoos + np.log(rcm_charac)
|
|
889
|
+
|
|
890
|
+
# Calculate the lower and upper bounds of the distribution for RCM elements
|
|
891
|
+
hoos_lower = lognorm(shape_hoos, 0.0, np.exp(scale_hoos)).ppf(0.0001)
|
|
892
|
+
hoos_upper = lognorm(shape_hoos, 0.0, np.exp(scale_hoos)).ppf(0.9999)
|
|
893
|
+
|
|
894
|
+
# Generate a range of values within the distribution
|
|
895
|
+
x_range = np.linspace(hoos_lower, hoos_upper, 10000)
|
|
896
|
+
|
|
897
|
+
# Compute the cumulative density function (CDF) for the generated range
|
|
898
|
+
cdf_range = lognorm.cdf(x_range, shape_hoos, 0.0, np.exp(scale_hoos))
|
|
899
|
+
|
|
900
|
+
return x_range, cdf_range
|