Skip to content

Commit 64b7354

Browse files
author
Moreno
committed
Update suction pile examples + anchor and project logic
1 parent 5b7ca8b commit 64b7354

File tree

6 files changed

+109
-71
lines changed

6 files changed

+109
-71
lines changed

examples/05_Anchors/anchor_soil_layered.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,17 @@
1010
soil_mode='layered',
1111
profile_source='inputs/GulfOfMaine_soil_profiles.yaml')
1212

13-
for label, props in proj.soilProps.items():
14-
print(f"{label}: {props}")
15-
1613
# Step 2: Create and register an anchor at a known position in the grid
1714
anchor = Anchor(
1815
dd = {'type': 'suction', 'design': {'D': 3.5, 'L': 12.0, 'zlug': 9.67}},
1916
r = [54.0, -4450.0, 0.0])
2017

2118
# Step 3: Assign local soil profile from project (nearest neighbor lookup)
22-
soil_id, soil_profile = proj.getSoilAtLocation(anchor.r[0], anchor.r[1])
23-
anchor.soilProps = {soil_id: soil_profile}
24-
anchor.setSoilProfile([{'name': soil_id, 'layers': soil_profile}]) # ensures `anchor.soil_profile` is set
19+
proj.getSoilAtLocation(anchor.r[0], anchor.r[1])
20+
anchor.setSoilProfile(proj.profile_map)
2521

2622
# Step 4: Assign loads and line
27-
anchor.loads = {'Hm': 2e6, 'Vm': 1.5e6}
23+
anchor.loads = {'Hm': 1e6, 'Vm': 1.5e6}
2824
anchor.line_type = 'chain'
2925
anchor.d = 0.16
3026
anchor.w = 5000.0
@@ -52,7 +48,7 @@
5248
loads = None,
5349
lambdap_con = [3, 6],
5450
zlug_fix = False,
55-
safety_factor = {'SF_combined': 1},
51+
safety_factor = {'SF_combined': 2},
5652
plot = True)
5753

5854
# Step 6: Report

examples/05_Anchors/anchor_soil_uniform.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@
66
proj = Project()
77
proj.loadSoil(
88
filename='inputs/GulfOfMaine_soil_layered_100x100.txt',
9-
soil_mode='uniform') # 'uniform' soil does not need from the profile_source yaml file
10-
11-
for label, props in proj.soilProps.items():
12-
print(f"{label}: {props}")
9+
soil_mode='uniform') # 'uniform' soil mode does not need from the profile_source yaml file
1310

14-
# Convert to profile_map format so anchor capacity models can use it
15-
proj.convertUniformToLayered(default_layer=50.0)
16-
17-
for label, props in proj.profile_map.items():
18-
print(f"{label}: {props}")
19-
2011
# Step 2: Create and register an anchor at a known position in the grid
2112
anchor = Anchor(
2213
dd = {'type': 'suction', 'design': {'D': 3.5, 'L': 12.0, 'zlug': 9.67}},
2314
r = [54.0, -4450.0, 0.0])
2415

25-
# Step 3: Assign local soil profile from project (nearest neighbor lookup)
26-
soil_id, _ = proj.getSoilAtLocation(anchor.r[0], anchor.r[1])
27-
soil_profile = proj.profile_map[soil_id] # get compatible layered format
28-
anchor.soilProps = {soil_id: soil_profile}
29-
anchor.setSoilProfile([{'name': soil_id, 'layers': soil_profile}]) # ensures `anchor.soil_profile` is set
16+
# SELECT option 3a or 3b
17+
# ======================
18+
# Step 3a: Convert to profile_map format from uniform soil data
19+
# proj.convertUniformToLayered(default_layer=50.0)
20+
# profile_map = proj.profile_map[3] # manually take relevant soil profile from the list
21+
# anchor.setSoilProfile(profile_map) # automatically turns dict to list
22+
23+
# Step 3b: Assign local soil profile from project (nearest neighbor lookup)
24+
proj.getSoilAtLocation(anchor.r[0], anchor.r[1])
25+
anchor.assignSoilProfile(proj.profile_map)
3026

3127
# Step 4: Assign loads and line
3228
anchor.loads = {'Hm': 1e6, 'Vm': 5e4}

examples/05_Anchors/anchor_suction.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
r = [250.0, 250.0, 000.0])
5353

5454
# --- Step 0: Create anchor based grid CPTs ---
55-
anchor.interpolateSoilProfile(profile_map)
55+
anchor.assignSoilProfile(profile_map)
5656

5757
# --- Step 1: Plot suction pile and soil profile ---
5858
# Access anchor geometrical properties
@@ -67,8 +67,8 @@
6767

6868
# Assign loads manually
6969
anchor.loads = {
70-
'Hm': 3.0e6, # Horizontal mudline load (N)
71-
'Vm': 1.0e6 # Vertical mudline load (N)
70+
'Hm': 2.0e6, # Horizontal mudline load (N)
71+
'Vm': 8.0e5 # Vertical mudline load (N)
7272
}
7373

7474
# Assign line properties manually
@@ -109,7 +109,6 @@
109109
anchor.getCost()
110110

111111
print(f"Mass: {anchor.anchorCapacity['Weight pile']/9.81:.2f} kg")
112-
print(f"Material unit cost: {anchor.cost['unit_cost']:.2f} USD/kg")
113112
print(f'Material cost: {anchor.cost["Material cost"]:.2f} USD [2024]')
114113

115114
#%%
@@ -121,7 +120,7 @@
121120
loads = None,
122121
lambdap_con = [3, 6],
123122
zlug_fix = False,
124-
safety_factor = {'SF_horizontal': 2, 'SF_vertical': 3},
123+
safety_factor = {'SF_combined': 2},
125124
plot = True)
126125

127126
print('\nFinal Optimized Anchor:')
@@ -132,8 +131,5 @@
132131
anchor.getCost()
133132

134133
print(f"Mass: {anchor.anchorCapacity['Weight pile']/9.81:.2f} kg")
135-
print(f"Material unit cost: {anchor.cost['unit_cost']:.2f} USD/kg")
136134
print(f'Material cost: {anchor.cost["Material cost"]:.2f} USD [2024]')
137135

138-
# --- Step 7: Visualize Anchor Geometry ---
139-
# anchor.getCombinedPlot()

examples/05_Anchors/example_suction.ipynb

Lines changed: 26 additions & 30 deletions
Large diffs are not rendered by default.

famodel/anchors/anchor.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ def setSoilProfile(self, profile_map):
130130
Assign a soil profile directly from a single CPT.
131131
Assumes profile_map is a list with only one entry.
132132
'''
133+
134+
if isinstance(profile_map, dict):
135+
profile_map = [profile_map]
136+
elif isinstance(profile_map, list):
137+
pass # leave it as it is
138+
else:
139+
raise TypeError('setSoilProfile expects a dict or a list/tuple with one dict.')
140+
133141
if len(profile_map) != 1:
134142
raise ValueError("setSoilProfile expects a profile_map with exactly one CPT.")
135143

@@ -215,6 +223,42 @@ def interpolateSoilProfile(self, profile_map):
215223

216224
if self.display > 0: print(f"[Anchor] Interpolated soil profile: {self.profile_name} with soil types {self.soil_type_list}")
217225

226+
def assignSoilProfile(self, profile_map):
227+
'''
228+
High-level soil assignment:
229+
230+
- If a single CPT is provided → call setSoilProfile
231+
- If 4 or more CPTs are provided → call interpolateSoilProfile
232+
- Otherwise → complain (not enough info for interpolation)
233+
234+
Accepts:
235+
- dict (single CPT)
236+
- list of CPT dicts
237+
'''
238+
# Normalize input
239+
if isinstance(profile_map, dict):
240+
profile_list = [profile_map]
241+
elif isinstance(profile_map, list):
242+
profile_list = profile_map
243+
else:
244+
raise TypeError('applySoilProfile expects a dict or a list of dicts.')
245+
246+
n = len(profile_list)
247+
248+
if n == 1:
249+
# delegate to existing set logic
250+
return self.setSoilProfile(profile_list)
251+
252+
elif n >= 4:
253+
# delegate to existing interpolation logic
254+
return self.interpolateSoilProfile(profile_list)
255+
256+
else:
257+
# 2 or 3 CPTs: ambiguous use-case
258+
raise ValueError(
259+
f'applySoilProfile received {n} CPTs: '
260+
'need exactly 1 for direct assignment or ≥4 for interpolation.')
261+
218262
def makeMoorPyAnchor(self, ms):
219263
'''
220264
Create a MoorPy anchor object in a MoorPy system.

famodel/project.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ def loadSoil(self, filename=None, yaml=None, soil_mode='uniform', profile_source
12571257
else:
12581258
raise ValueError("Invalid combination of filename/yaml inputs")
12591259

1260-
# --- Set defaults only for uniform mode ---
1260+
# --- Set defaults only for uniform mode (when values are missing) ---
12611261
if soil_mode == 'uniform':
12621262
for key, props in soilProps.items():
12631263
props['Su0'] = getFromDict(props, 'Su0', shape=-1, dtype=list, default=[2.39])
@@ -1298,24 +1298,34 @@ def getSoilAtLocation(self, x, y):
12981298
-------
12991299
(str, dict or list): soil name or profile ID, and associated soil properties or layered profile
13001300
'''
1301+
self.profile_map = []
1302+
13011303
if self.soil_x is not None:
13021304
ix = np.argmin([abs(x - sx) for sx in self.soil_x])
13031305
iy = np.argmin([abs(y - sy) for sy in self.soil_y])
13041306
soil_id = self.soil_names[iy, ix] # could be label or profile_id
13051307

13061308
if self.soil_mode == 'uniform':
1307-
soil_info = self.soilProps[soil_id]
1308-
return soil_id, soil_info
1309-
1309+
soil_info = self.soilProps[soil_id]
1310+
if not hasattr(self, 'profile_map') or not self.profile_map:
1311+
self.convertUniformToLayered(default_layer=50.0)
1312+
1313+
# Replace with a single entry corresponding to this soil_id
1314+
self.profile_map = [
1315+
next(e for e in self.profile_map if e['name'] == str(soil_id))]
1316+
self.profile_map[0]['layers']
1317+
13101318
elif self.soil_mode == 'layered':
1311-
profile_layers = self.soilProps[soil_id] # list of layer dicts
1312-
return soil_id, profile_layers
1313-
1319+
layers = self.soilProps[soil_id] # list of layer dicts
1320+
profile_entry = {'name': str(soil_id), 'layers': layers}
1321+
self.profile_map.append(profile_entry)
1322+
13141323
else:
13151324
raise ValueError(f"Unknown soil_mode: {self.soil_mode}")
13161325

13171326
print(f"[DEBUG] soil_id at location ({x}, {y}) is: {soil_id}")
13181327
print(f"[DEBUG] Available soilProps keys: {list(self.soilProps.keys())}")
1328+
13191329
else:
13201330
raise ValueError("No soil grid defined")
13211331

@@ -1331,8 +1341,8 @@ def convertUniformToLayered(self, default_layer=50.0):
13311341
name = str(name)
13321342

13331343
gamma = float(props['gamma'][0])
1334-
Su0 = float(props['Su0'][0])
1335-
k = float(props['k'][0])
1344+
Su0 = float(props['Su0'][0])
1345+
k = float(props['k'][0])
13361346

13371347
layer = {
13381348
'soil_type': 'clay',
@@ -1341,7 +1351,7 @@ def convertUniformToLayered(self, default_layer=50.0):
13411351
'gamma_top': gamma,
13421352
'gamma_bot': gamma,
13431353
'Su_top': Su0,
1344-
'Su_bot': Su0 + k * default_layer}
1354+
'Su_bot': Su0 + k*default_layer}
13451355

13461356
profile_entry = {'name': name, 'layers': [layer]}
13471357
self.profile_map.append(profile_entry)

0 commit comments

Comments
 (0)