Skip to content

Commit eae9888

Browse files
committed
Switching to SI units and enabling unit converter:
- Enabled the unit converter when loading YAMLs in Irma.py. - capabilities.yaml: - - changed spec names to use fundamental SI units. - - commented out some weight/dimension specs that may not be necessary. - - added "type" values to denote how each spec should be treated (this is for upcoming changes not yet implemented) - Changed associated keys/calculations in action.py to mainly use SI units. - added a printStruct function to nicely display nested lists/dicts.
1 parent 47eb99a commit eae9888

File tree

7 files changed

+149
-139
lines changed

7 files changed

+149
-139
lines changed

famodel/irma/action.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
configureAdjuster, route_around_anchors)
3131

3232

33+
t2N = 9806.7 # conversion factor from t to N
34+
3335
def incrementer(text):
3436
'''
3537
Increments the last integer found in a string.
@@ -267,7 +269,7 @@ def printNotSupported(st):
267269
except:
268270
pass
269271

270-
req['bollard_pull']['max_force_t'] = 0.01*mass/1e4 # <<< can add a better calculation for towing force required
272+
req['bollard_pull']['max_force'] = 0.0001*mass*t2N # <<< can add a better calculation for towing force required
271273

272274

273275
elif reqname == 'chain_storage': # Storage specifically for chain
@@ -283,8 +285,8 @@ def printNotSupported(st):
283285
chain_vol += sec['L'] * np.pi * (sec['type']['d_nom'] / 2) ** 2 * (2) # volume [m^3]
284286
chain_L += sec['L'] # length [m]
285287

286-
req['chain_locker']['volume_m3'] += chain_vol # <<< replace with proper estimate
287-
req['deck_space']['area_m2'] += chain_L*0.205 # m^2
288+
req['chain_locker']['volume'] += chain_vol # <<< replace with proper estimate
289+
req['deck_space']['area'] += chain_L*0.205 # m^2
288290

289291

290292
elif reqname == 'rope_storage': # Storage specifically for chain
@@ -300,8 +302,8 @@ def printNotSupported(st):
300302
rope_vol += sec['L'] * np.pi * (sec['type']['d_nom'] / 2) ** 2 # volume [m^3]
301303
rope_L += sec['L'] # length [m]
302304

303-
req['line_reel']['volume_m3'] += rope_vol
304-
req['deck_space']['area_m2'] += np.ceil((0.0184*rope_L)/13.5)*13.5 # m^2
305+
req['line_reel']['volume'] += rope_vol
306+
req['deck_space']['area'] += np.ceil((0.0184*rope_L)/13.5)*13.5 # m^2
305307

306308

307309
elif reqname == 'storage': # Generic storage, such as for anchors
@@ -315,32 +317,32 @@ def printNotSupported(st):
315317
# if the suction piles are to be standing up # <<<<<< how to implement this? Depends on the asset assignment
316318
# A = (obj.dd['design']['D']+(10/3.28084))**2
317319

318-
req['deck_space']['area_m2'] += A
320+
req['deck_space']['area'] += A
319321

320322
elif reqname == 'anchor_overboarding' or reqname == 'anchor_lowering':
321323
for obj in self.objectList:
322324
if isinstance(obj, Anchor):
323325

324326
if obj.mass:
325-
mass = obj.mass / 1e3 # tonnes
327+
mass = obj.mass # [kg]
326328
else: # rough estimate based on size
327329
wall_thickness = (6.35 + obj.dd['design']['D']*20)/1e3 # Suction pile wall thickness (m), API RP2A-WSD. It changes for different anchor concepts
328-
mass = (np.pi * ((obj.dd['design']['D']/2)**2 - (obj.dd['design']['D']/2 - wall_thickness)**2) * obj.dd['design']['L'] * 7850) / 1e3 # rough mass estimate [tonne]
329-
req['crane']['capacity_t'] = mass * 1.2 # <<< replace with proper estimate
330-
req['crane']['hook_height_m'] = obj.dd['design']['L'] * 1.2 # <<< replace with proper estimate
330+
mass = (np.pi * ((obj.dd['design']['D']/2)**2 - (obj.dd['design']['D']/2 - wall_thickness)**2) * obj.dd['design']['L'] * 7850) # rough mass estimate [kg]
331+
req['crane']['capacity'] = mass * 1.2 # <<< replace with proper estimate
332+
req['crane']['hook_height'] = obj.dd['design']['L'] * 1.2 # <<< replace with proper estimate
331333
if reqname == 'anchor_overboarding':
332-
req['stern_roller']['width_m'] = obj.dd['design']['D'] * 1.2 # <<< replace with proper estimate
334+
req['stern_roller']['width'] = obj.dd['design']['D'] * 1.2 # <<< replace with proper estimate
333335
else: # anchor lowering
334-
req['winch']['max_line_pull_t'] = mass * 1.2 # <<< replace with proper estimate
335-
req['winch']['speed_mpm'] = 18 # meters per minute
336+
req['winch']['max_line_pull'] = mass * 1.2 # <<< replace with proper estimate
337+
req['winch']['speed'] = 0.3 # [m/s]
336338

337339
elif reqname == 'anchor_orienting':
338340
for obj in self.objectList:
339341
if isinstance(obj, Anchor):
340342

341343
# req['winch']['max_line_pull_t'] =
342-
req['rov']['depth_rating_m'] = abs(obj.r[-1]) * 1.2 # <<< replace with proper estimate
343-
req['divers']['max_depth_m'] = abs(obj.r[-1]) * 1.2 # <<< replace with proper estimate / basically, if anchor is too deep, divers might not be an option
344+
req['rov']['depth_rating'] = abs(obj.r[-1]) * 1.2 # <<< replace with proper estimate
345+
req['divers']['max_depth'] = abs(obj.r[-1]) * 1.2 # <<< replace with proper estimate / basically, if anchor is too deep, divers might not be an option
344346

345347
elif reqname == 'anchor_embedding':
346348

@@ -349,18 +351,18 @@ def printNotSupported(st):
349351

350352
if obj.dd['type'] == 'DEA':
351353

352-
req['bollard_pull']['max_force_t'] = 270 # <<< replace with proper estimate
354+
req['bollard_pull']['max_force'] = 270*t2N # <<< replace with proper estimate
353355

354356
elif obj.dd['type'] == 'suction':
355357

356-
req['pump_subsea']['pressure_bar'] = 12 # <<< replace with proper estimate
358+
req['pump_subsea']['pressure'] = 1.2e5 # <<< replace with proper estimate
357359

358360
else:
359361
printNotSupported(f"Anchor type {obj.dd['type']}")
360362

361363
elif reqname == 'line_handling':
362-
req['winch']['max_line_pull_t'] = 1
363-
req['crane']['capacity_t'] = 27 # should set to mooring weight <<<
364+
req['winch']['max_line_pull'] = 1*t2N
365+
req['crane']['capacity'] = 27*t2N # should set to mooring weight <<<
364366
#req['']['']
365367

366368
elif reqname == 'subsea_connection':
@@ -369,9 +371,9 @@ def printNotSupported(st):
369371
if isinstance(obj, Mooring):
370372

371373
depth = abs(obj.rA[2]) # depth assumed needed for the connect/disconnect work
372-
req['rov']['depth_rating_m'] = depth
374+
req['rov']['depth_rating'] = depth
373375
if depth < 200: # don't consider divers if deeper than this
374-
req['divers']['max_depth_m'] = depth #
376+
req['divers']['max_depth'] = depth #
375377

376378
else:
377379
printNotSupported(f"Requirement {reqname}")
@@ -1035,9 +1037,9 @@ def checkAssets(self, assets):
10351037
# Sum up the asset capabilities and their specs (not sure this is useful/valid)
10361038

10371039
# Here's a list of specs we might want to take the max of instead of sum: Add more as needed
1038-
specs_to_max = ['hook_height_m', 'depth_rating_m',
1039-
'max_depth_m', 'accuracy_m',
1040-
'speed_mpm', 'capacity_t'] # capacity_t is here because it doesn't make sense to have two cranes to lift a single anchor.
1040+
specs_to_max = ['hook_height', 'depth_rating',
1041+
'max_depth', 'accuracy',
1042+
'speed', 'capacity'] # capacity_t is here because it doesn't make sense to have two cranes to lift a single anchor.
10411043
asset_caps = {}
10421044
for asset in assets:
10431045
for cap, specs in asset['capabilities'].items():
@@ -1193,7 +1195,11 @@ def checkAsset(self, asset):
11931195

11941196
def calcDurationAndCost(self):
11951197
'''
1196-
Calculates duration and cost for the action. The structure here is dependent on `actions.yaml`.
1198+
Calculates duration and cost for the action, based on the time for
1199+
each requirement to be performed based on the selected capability
1200+
and the assigned asset(s) that meeting that capability.
1201+
The durations of each requirement are assumed to add (i.e. each is
1202+
done in series rather than parallel). <<< MH: is this okay? <<<
11971203
TODO: finish description
11981204
11991205
Inputs
@@ -1277,7 +1283,7 @@ def calcDurationAndCost(self):
12771283

12781284
distance = 2500 # <<< need to eventually compute distances based on positions
12791285

1280-
speed = req['assigned_assets'][0]['capabilities']['bollard_pull']['site_speed_mps']
1286+
speed = req['assigned_assets'][0]['capabilities']['bollard_pull']['site_speed']
12811287

12821288
self.durations['tow'] = distance / speed / 60 / 60 # duration [hr]
12831289

@@ -1439,7 +1445,7 @@ def calcDurationAndCost(self):
14391445

14401446
# onsite speed from capabilities.engine (SI)
14411447
cap_eng = vessel.get('capabilities', {}).get('engine', {})
1442-
speed_mps = float(cap_eng['site_speed_mps'])
1448+
speed_mps = float(cap_eng['site_speed'])
14431449

14441450
self.duration += dist_m/speed_mps/3600.0
14451451

@@ -1546,7 +1552,7 @@ def calcDurationAndCost(self):
15461552
raise ValueError('transit_onsite_tug: operator (barge) missing.')
15471553

15481554
cap_eng = operator.get('capabilities', {}).get('bollard_pull', {})
1549-
speed_mps = float(cap_eng['site_speed_mps'])
1555+
speed_mps = float(cap_eng['site_speed'])
15501556

15511557
self.duration += dist_m/speed_mps/3600.0
15521558

@@ -1586,7 +1592,7 @@ def calcDurationAndCost(self):
15861592

15871593
if 'winch' in req['selected_capability']: # using a winch to load
15881594

1589-
speed = req['assigned_assets'][0]['capabilities']['winch']['speed_mpm']
1595+
speed = req['assigned_assets'][0]['capabilities']['winch']['speed']*60 # [m/min]
15901596

15911597
L = sum([mooring['length'] for mooring in moorings])
15921598

@@ -1625,9 +1631,9 @@ def calcDurationAndCost(self):
16251631

16261632
v_mpm = None
16271633
if 'winch' in req['selected_capability']: # using a winch to lower
1628-
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed_mpm']
1634+
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed']*60 # [m/min]
16291635
elif 'crane' in req['selected_capability']: # using a crane to lower
1630-
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed_mpm']
1636+
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed']*60 # [m/min]
16311637

16321638
if v_mpm: # there is only a lowering time if a winch or crane is involved
16331639
self.durations['anchor lowering'] = depth_m/v_mpm /60 # [h]
@@ -1637,7 +1643,7 @@ def calcDurationAndCost(self):
16371643
req = self.requirements['anchor_embedding']
16381644
if 'pump_subsea' in req['selected_capability']: # using a winch to lower
16391645
specs = req['assigned_assets'][0]['capabilities']['pump_subsea'] # pump specs
1640-
embed_speed = 0.1*specs['power_kW']/(np.pi/4*anchor.dd['design']['D']**2) # <<< example of more specific calculation
1646+
embed_speed = 1E-4*specs['power']/(np.pi/4*anchor.dd['design']['D']**2) # <<< example of more specific calculation
16411647
else:
16421648
embed_speed = 0.07 # embedment rate [m/min]
16431649
self.durations['anchor embedding'] = L*embed_speed / 60
@@ -1663,9 +1669,9 @@ def calcDurationAndCost(self):
16631669
# note: some of this code is repeated and could be put in a function
16641670
v_mpm = None
16651671
if 'winch' in req['selected_capability']: # using a winch to lower
1666-
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed_mpm']
1672+
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed']*60 # [m/min]
16671673
elif 'crane' in req['selected_capability']: # using a crane to lower
1668-
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed_mpm']
1674+
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed']*60 # [m/min]
16691675

16701676
if v_mpm: # there is only a lowering time if a winch or crane is involved
16711677
self.durations['mooring line lowering'] = depth/v_mpm /60 # [h]
@@ -1694,9 +1700,9 @@ def calcDurationAndCost(self):
16941700
# note: some of this code is repeated and could be put in a function
16951701
v_mpm = None
16961702
if 'winch' in req['selected_capability']: # using a winch to lower
1697-
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed_mpm']
1703+
v_mpm = req['assigned_assets'][0]['capabilities']['winch']['speed']*60 # [m/min]
16981704
elif 'crane' in req['selected_capability']: # using a crane to lower
1699-
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed_mpm']
1705+
v_mpm = req['assigned_assets'][0]['capabilities']['crane']['speed']*60 # [m/min]
17001706

17011707
if v_mpm: # there is only a lowering time if a winch or crane is involved
17021708
self.durations['mooring line retrieval'] = depth/v_mpm /60 # [h]

0 commit comments

Comments
 (0)