Skip to content

Commit

Permalink
Merge pull request #52 from WISDEM/feature/floating-modules
Browse files Browse the repository at this point in the history
Feature/floating modules
  • Loading branch information
JakeNunemaker authored Jul 1, 2020
2 parents 0a950f3 + 966c213 commit 2b7ba95
Show file tree
Hide file tree
Showing 74 changed files with 3,471 additions and 51 deletions.
2 changes: 1 addition & 1 deletion ORBIT/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
__email__ = "[email protected]"


from .port import Port
from .port import Port, WetStorage
from .cargo import Cargo
from .vessel import Vessel
from .components import Crane, JackingSys
Expand Down
5 changes: 5 additions & 0 deletions ORBIT/core/_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
"blade_fasten_time": 1.5, # hr
"blade_release_time": 1, # hr
"blade_attach_time": 3.5, # hr
# Mooring System
"mooring_system_load_time": 5, # hr
"mooring_site_survey_time": 4, # hr
"suction_pile_install_time": 11, # hr
"drag_embed_install_time": 5, # hr
# Misc.
"site_position_time": 2, # hr
"rov_survey_time": 1, # hr
Expand Down
14 changes: 8 additions & 6 deletions ORBIT/core/logic/vessel_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,14 @@ def get_list_of_items_from_port(vessel, port, items, **kwargs):
for item in buffer:
action, time = item.fasten(**kwargs)
vessel.storage.put_item(item)
yield vessel.task(
action,
time,
constraints=vessel.transit_limits,
**kwargs,
)

if time > 0:
yield vessel.task(
action,
time,
constraints=vessel.transit_limits,
**kwargs,
)

else:
raise ItemNotFound(items)
16 changes: 16 additions & 0 deletions ORBIT/core/port.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,19 @@ def get_item(self, _type):
else:
res = self.get(lambda x: x == target)
return res.value


class WetStorage(simpy.Store):
"""Storage infrastructure for floating substructures."""

def __init__(self, env, capacity):
"""
Creates an instance of WetStorage.
Parameters
----------
capacity : int
Number of substructures or assemblies that can be stored.
"""

super().__init__(env, capacity)
13 changes: 7 additions & 6 deletions ORBIT/core/vessel.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,13 @@ def get_item_from_storage(
raise e

action, time = item.release(**kwargs)
yield self.task(
action,
time,
constraints=self.transit_limits,
cost=self.operation_cost(time),
)
if time > 0:
yield self.task(
action,
time,
constraints=self.transit_limits,
cost=self.operation_cost(time),
)

if release and vessel.storage.any_remaining(_type) is False:
vessel.release.succeed()
Expand Down
3 changes: 3 additions & 0 deletions ORBIT/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,10 @@ def export_library_specs(key, filename, data, file_ext="yaml"):
"spi_vessel": "vessels",
"trench_dig_vessel": "vessels",
"feeder": "vessels",
"mooring_install_vessel": "vessels",
"wtiv": "vessels",
"towing_vessel": "vessels",
"support_vessel": "vessels",
# cables
"cables": "cables",
"array_system": "cables",
Expand Down
12 changes: 12 additions & 0 deletions ORBIT/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,25 @@
from ORBIT.phases import DesignPhase, InstallPhase
from ORBIT.library import initialize_library, extract_library_data
from ORBIT.phases.design import (
SparDesign,
MonopileDesign,
ArraySystemDesign,
ExportSystemDesign,
ProjectDevelopment,
MooringSystemDesign,
ScourProtectionDesign,
SemiSubmersibleDesign,
CustomArraySystemDesign,
OffshoreSubstationDesign,
)
from ORBIT.phases.install import (
TurbineInstallation,
MonopileInstallation,
MooredSubInstallation,
ArrayCableInstallation,
ExportCableInstallation,
GravityBasedInstallation,
MooringSystemInstallation,
ScourProtectionInstallation,
OffshoreSubstationInstallation,
)
Expand All @@ -56,6 +62,9 @@ class ProjectManager:
ExportSystemDesign,
ScourProtectionDesign,
OffshoreSubstationDesign,
MooringSystemDesign,
SemiSubmersibleDesign,
SparDesign,
]

_install_phases = [
Expand All @@ -65,6 +74,9 @@ class ProjectManager:
ArrayCableInstallation,
ExportCableInstallation,
ScourProtectionInstallation,
MooredSubInstallation,
MooringSystemInstallation,
GravityBasedInstallation,
]

def __init__(self, config, library_path=None, weather=None):
Expand Down
3 changes: 3 additions & 0 deletions ORBIT/phases/design/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@

from .design_phase import DesignPhase # isort:skip
from .oss_design import OffshoreSubstationDesign
from .spar_design import SparDesign
from .monopile_design import MonopileDesign
from .array_system_design import ArraySystemDesign, CustomArraySystemDesign
from .project_development import ProjectDevelopment
from .export_system_design import ExportSystemDesign
from .mooring_system_design import MooringSystemDesign
from .scour_protection_design import ScourProtectionDesign
from .semi_submersible_design import SemiSubmersibleDesign
67 changes: 67 additions & 0 deletions ORBIT/phases/design/_cables.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from collections import Counter, OrderedDict

import numpy as np
from scipy.optimize import fsolve

from ORBIT.library import extract_library_specs
from ORBIT.phases.design import DesignPhase
Expand Down Expand Up @@ -310,6 +311,72 @@ def _initialize_cables(self):
name = specs["name"]
self.cables[name] = Cable(specs)

def _get_touchdown_distance(self):
"""
Returns the cable touchdown distance measured from the centerpoint of
the substructure.
If depth <= 60, default is 0km (straight down assumed for fixed bottom).
If depth > 60, default is 0.3 * depth.
"""

_design = f"{self.cable_type}_system_design"
depth = self.config["site"]["depth"]
touchdown = self.config[_design].get("touchdown_distance", None)

if touchdown is not None:
self.touchdown = touchdown

else:
if depth <= 60:
self.touchdown = 0

else:
self.touchdown = depth * 0.3

@staticmethod
def _catenary(a, *data):
"""Simple catenary equation."""

d, h = data
res = a * np.cosh(h / a) - (d + a)
return res

def _get_catenary_length(self, d, h):
"""
Returns the catenary length of a cable that touches down at depth `d`
and horizontal distance `h`.
Returns
-------
float
Catenary length.
"""

a = fsolve(self._catenary, 8, (d, h))

x = np.linspace(0, h)
y = a * np.cosh(x / a) - a

if not np.isclose(y[-1], d):
print(
"Warning: Catenary calculation failed. Reverting to simple vertical profile."
)
return d

return np.trapz(np.sqrt(1 + np.gradient(y, x) ** 2), x)

@property
def free_cable_length(self):
"""Returns the length of the vertical portion of a cable section in km."""

depth = self.config["site"]["depth"]

if not self.touchdown:
return depth / 1000

return self._get_catenary_length(depth, self.touchdown) / 1000

@property
def cable_lengths_by_type(self):
"""
Expand Down
5 changes: 4 additions & 1 deletion ORBIT/phases/design/array_system_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class ArraySystemDesign(CableSystem):
"array_system_design": {
"design_time": "hrs (optional)",
"cables": "list | str",
"touchdown_distance": "m (optional, default: 0)",
"average_exclusion_percent": "float (optional)",
},
}
Expand All @@ -102,6 +103,7 @@ def __init__(self, config, **kwargs):
self.exclusion = 1 + self.config["array_system_design"].get(
"average_exclusion_percent", 0.0
)
self._get_touchdown_distance()
self.extract_phase_kwargs(**kwargs)
self.system = Plant(self.config)

Expand Down Expand Up @@ -331,7 +333,8 @@ def _create_cable_section_lengths(self):
if getattr(self, "sections_cable_lengths", np.zeros(1)).sum() == 0:
self.sections_cable_lengths = (
self.sections_distance * self.exclusion
+ (2 * self.system.site_depth)
+ (2 * self.free_cable_length)
- (2 * self.touchdown / 1000)
)
self.sections_cables = np.full(
(self.num_strings, self.num_turbines_full_string), None
Expand Down
6 changes: 4 additions & 2 deletions ORBIT/phases/design/export_system_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ExportSystemDesign(CableSystem):
"export_system_design": {
"cables": "str",
"num_redundant": "int (optional)",
"touchdown_distance": "m (optional, default: 0)",
"percent_added_length": "float (optional)",
},
}
Expand Down Expand Up @@ -78,6 +79,7 @@ def __init__(self, config, **kwargs):
self._depth = config["site"]["depth"]
self._plant_capacity = self.config["plant"]["capacity"]
self._distance_to_landfall = config["site"]["distance_to_landfall"]
self._get_touchdown_distance()
try:
self._distance_to_interconnection = config["landfall"][
"interconnection_distance"
Expand Down Expand Up @@ -136,8 +138,8 @@ def compute_cable_length(self):
added_length = 1.0 + self._design.get("percent_added_length", 0.0)
self.length = round(
(
(self._depth / 1000.0) # convert to km
+ self._distance_to_landfall
self.free_cable_length
+ (self._distance_to_landfall - self.touchdown / 1000)
+ self._distance_to_interconnection
)
* added_length,
Expand Down
Loading

0 comments on commit 2b7ba95

Please sign in to comment.