Skip to content

Commit

Permalink
Merge pull request #124 from WISDEM/dev
Browse files Browse the repository at this point in the history
v1.0.7 Release
  • Loading branch information
JakeNunemaker authored Dec 8, 2022
2 parents 7a9ec7a + 07d965f commit f8d6ac4
Show file tree
Hide file tree
Showing 36 changed files with 3,024 additions and 99 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
if: startsWith(github.ref, 'refs/tags')
if: startsWith(github.ref, 'refs/tags/v')
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ repos:
- id: black
name: black
stages: [commit]
language_version: python3.7
exclude: ^ORBIT/api/wisdem

- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
1 change: 1 addition & 0 deletions ORBIT/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .config import load_config, save_config
from ._version import get_versions
from .parametric import ParametricManager
from .supply_chain import SupplyChainManager

__version__ = get_versions()["version"]
del get_versions
92 changes: 62 additions & 30 deletions ORBIT/api/wisdem.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
class Orbit(om.Group):
def initialize(self):
self.options.declare("floating", default=False)
self.options.declare("jacket", default=False)
self.options.declare("jacket_legs", default=0)

def setup(self):

Expand Down Expand Up @@ -43,14 +45,24 @@ def setup(self):
self.set_input_defaults("design_install_plan_cost", 2.5e6, units="USD")
self.set_input_defaults("boem_review_cost", 0.0, units="USD")

self.add_subsystem("orbit", OrbitWisdem(floating=self.options["floating"]), promotes=["*"])
self.add_subsystem(
"orbit",
OrbitWisdem(
floating=self.options["floating"],
jacket=self.options["jacket"],
jacket_legs=self.options["jacket_legs"],
),
promotes=["*"],
)


class OrbitWisdem(om.ExplicitComponent):
"""ORBIT-WISDEM Fixed Substructure API"""

def initialize(self):
self.options.declare("floating", default=False)
self.options.declare("jacket", default=False)
self.options.declare("jacket_legs", default=0)

def setup(self):
""""""
Expand Down Expand Up @@ -110,22 +122,22 @@ def setup(self):
self.add_input("tower_length", 100.0, units="m", desc="Total length of the tower.")
self.add_input(
"tower_deck_space",
0.0,
25.0,
units="m**2",
desc="Deck space required to transport the tower. Defaults to 0 in order to not be a constraint on installation.",
)
self.add_input("nacelle_mass", 500.0, units="t", desc="mass of the rotor nacelle assembly (RNA).")
self.add_input(
"nacelle_deck_space",
0.0,
25.0,
units="m**2",
desc="Deck space required to transport the rotor nacelle assembly (RNA). Defaults to 0 in order to not be a constraint on installation.",
)
self.add_discrete_input("number_of_blades", 3, desc="Number of blades per turbine.")
self.add_input("blade_mass", 50.0, units="t", desc="mass of an individual blade.")
self.add_input(
"blade_deck_space",
0.0,
100.0,
units="m**2",
desc="Deck space required to transport a blade. Defaults to 0 in order to not be a constraint on installation.",
)
Expand Down Expand Up @@ -158,20 +170,22 @@ def setup(self):
self.add_input("floating_substructure_cost", 10e6, units="USD", desc="Floating substructure unit cost.")

# Monopile
self.add_input("monopile_length", 100.0, units="m", desc="Length of monopile.")
self.add_input("monopile_length", 100.0, units="m", desc="Length of monopile (including pile).")
self.add_input("monopile_diameter", 7.0, units="m", desc="Diameter of monopile.")
self.add_input("monopile_mass", 900.0, units="t", desc="mass of an individual monopile.")
self.add_input("monopile_cost", 4e6, units="USD", desc="Monopile unit cost.")
self.add_input(
"monopile_deck_space",
0.0,
units="m**2",
desc="Deck space required to transport a monopile. Defaults to 0 in order to not be a constraint on installation.",
)

# Jacket
self.add_input("jacket_length", 65.0, units="m", desc="Length/height of jacket (including pile/buckets).")
self.add_input("jacket_mass", 900.0, units="t", desc="mass of an individual jacket.")
self.add_input("jacket_cost", 4e6, units="USD", desc="Jacket unit cost.")
self.add_input("jacket_r_foot", 10.0, units="m", desc="Radius of jacket legs at base from centeroid.")

# Generic fixed-bottom
self.add_input("transition_piece_mass", 250.0, units="t", desc="mass of an individual transition piece.")
self.add_input(
"transition_piece_deck_space",
0.0,
25.0,
units="m**2",
desc="Deck space required to transport a transition piece. Defaults to 0 in order to not be a constraint on installation.",
)
Expand Down Expand Up @@ -213,11 +227,12 @@ def setup(self):
def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_outputs):
""""""

floating = self.options["floating"]
floating_flag = self.options["floating"]
jacket_flag = self.options["jacket"]

config = {
# Vessels
"wtiv": "floating_heavy_lift_vessel" if floating else discrete_inputs["wtiv"],
"wtiv": "floating_heavy_lift_vessel" if floating_flag else discrete_inputs["wtiv"],
"array_cable_install_vessel": "example_cable_lay_vessel",
"array_cable_bury_vessel": "example_cable_lay_vessel",
"export_cable_install_vessel": "example_cable_lay_vessel",
Expand Down Expand Up @@ -281,8 +296,8 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
},
# Phase Specific
"OffshoreSubstationInstallation": {
"oss_install_vessel": "floating_heavy_lift_vessel" if floating else "example_heavy_lift_vessel",
"feeder": "floating_barge" if floating else "future_feeder",
"oss_install_vessel": "floating_heavy_lift_vessel" if floating_flag else "example_heavy_lift_vessel",
"feeder": "floating_barge" if floating_flag else "future_feeder",
"num_feeders": int(discrete_inputs["num_feeders"]),
},
# Project development costs
Expand Down Expand Up @@ -312,7 +327,7 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
}

# Unique design phases
if floating:
if floating_flag:
config["install_phases"] = {
"ExportCableInstallation": 0,
"OffshoreSubstationInstallation": 0,
Expand All @@ -321,18 +336,25 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
"ArrayCableInstallation": ("MooredSubInstallation", 0.25),
}
else:
fixedStr = "JacketInstallation" if jacket_flag else "MonopileInstallation"

if jacket_flag:
monopile = config.get("monopile", {})
monopile["diameter"] = 10
config["monopile"] = monopile

config["design_phases"] += ["ScourProtectionDesign"]
config["install_phases"] = {
"ExportCableInstallation": 0,
"OffshoreSubstationInstallation": 0,
"ArrayCableInstallation": 0,
"MonopileInstallation": 0,
fixedStr: 0,
"ScourProtectionInstallation": 0,
"TurbineInstallation": ("MonopileInstallation", 0.25),
"TurbineInstallation": (fixedStr, 0.25),
}

# Unique vessels
if floating:
if floating_flag:
vessels = {
"support_vessel": "example_support_vessel",
"towing_vessel": "example_towing_vessel",
Expand All @@ -351,7 +373,7 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
config.update(vessels)

# Unique support structure design/assembly
if floating:
if floating_flag:
config["port"] = {
"sub_assembly_lines": int(discrete_inputs["num_assembly_lines"]),
"turbine_assembly_cranes": int(discrete_inputs["num_port_cranes"]),
Expand Down Expand Up @@ -386,19 +408,29 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
"monthly_rate": float(inputs["port_cost_per_month"]),
}

config["monopile"] = {
"type": "Monopile",
"length": float(inputs["monopile_length"]),
"diameter": float(inputs["monopile_diameter"]),
"deck_space": float(inputs["monopile_deck_space"]),
"mass": float(inputs["monopile_mass"]),
"unit_cost": float(inputs["monopile_cost"]),
}

config["scour_protection_design"] = {
"cost_per_tonne": 20,
}

if jacket_flag:
config["jacket"] = {
"type": "Jacket",
"height": float(inputs["jacket_length"]),
"num_legs": int(self.options["jacket_legs"]),
"deck_space": 4 * float(inputs["jacket_r_foot"]) ** 2,
"mass": float(inputs["jacket_mass"]),
"unit_cost": float(inputs["jacket_cost"]),
}
else:
config["monopile"] = {
"type": "Monopile",
"length": float(inputs["monopile_length"]),
"diameter": float(inputs["monopile_diameter"]),
"deck_space": 0.25*float(inputs["monopile_diameter"]*inputs["monopile_length"]),
"mass": float(inputs["monopile_mass"]),
"unit_cost": float(inputs["monopile_cost"]),
}

self._orbit_config = config
return config

Expand Down
1 change: 1 addition & 0 deletions ORBIT/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from .library import loader
from .components import Crane, JackingSys
from .environment import OrbitEnvironment as Environment
from .supply_chain import SubstructureDelivery
12 changes: 12 additions & 0 deletions ORBIT/core/defaults/process_times.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
"grout_cure_time": 24 # hr
"grout_pump_time": 2 # hr

# Jackets
"jacket_fasten_time": 12 # hr
"jacket_release_time": 6 # hr
"jacket_lift_time": 4 # hr
"jacket_lower_time": 8 # hr
"jacket_grout_time": 8 # hr
"jacket_pin_template_time": 4 # hr
"jacket_pile_drive_time": 6 # hr, per leg
"jacket_position_pile": 6 # hr, per leg
"jacket_vessel_reposition": 4 # hr, per leg
"jacket_suction_bucket": 12 # hr, per leg

# Scour Protection
"drop_rocks_time": 10 # hr
"load_rocks_time": 4 # hr
Expand Down
2 changes: 2 additions & 0 deletions ORBIT/core/logic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
shuttle_items_to_queue,
prep_for_site_operations,
get_list_of_items_from_port,
shuttle_items_to_queue_wait,
get_list_of_items_from_port_wait,
)
121 changes: 121 additions & 0 deletions ORBIT/core/logic/vessel_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,124 @@ def get_list_of_items_from_port(vessel, port, items, **kwargs):

else:
raise ItemNotFound(items)


@process
def shuttle_items_to_queue_wait(
vessel, port, queue, distance, items, per_trip, assigned, **kwargs
):
"""
Shuttles a list of items from port to queue.
Parameters
----------
env : Environemt
vessel : Vessel
port : Port
queue : simpy.Resource
Queue object to shuttle to.
distance : int | float
Distance between port and site (km).
items : list
List of components stored as tuples to shuttle.
- ('key', 'value')
per_trip : int
assigned : int
"""

transit_time = vessel.transit_time(distance)

n = 0
while n < assigned:

vessel.submit_debug_log(message=f"{vessel} is at port.")

# Get list of items
per_trip = max([per_trip, 1])
yield get_list_of_items_from_port_wait(
vessel, port, items * per_trip, **kwargs
)

# Transit to site
vessel.update_trip_data()
yield vessel.task(
"Transit", transit_time, constraints=vessel.transit_limits
)
yield stabilize(vessel, **kwargs)

vessel.submit_debug_log(message=f"{vessel} is at site.")

# Join queue to be active feeder at site
with queue.request() as req:
queue_start = vessel.env.now
yield req

queue_time = vessel.env.now - queue_start
if queue_time > 0:
vessel.submit_action_log("Queue", queue_time, location="Site")

queue.vessel = vessel
active_start = vessel.env.now
queue.activate.succeed()

# Released by WTIV when objects are depleted
vessel.release = vessel.env.event()
yield vessel.release
active_time = vessel.env.now - active_start

vessel.submit_action_log(
"ActiveFeeder", active_time, location="Site"
)

queue.vessel = None
queue.activate = vessel.env.event()

# Transit back to port
vessel.at_site = False
yield jackdown_if_required(vessel, **kwargs)
yield vessel.task(
"Transit", transit_time, constraints=vessel.transit_limits
)

n += per_trip


@process
def get_list_of_items_from_port_wait(vessel, port, items, **kwargs):
"""
Retrieves multiples of 'items' from port until full.
Parameters
----------
vessel : Vessel
port : Port
items : list
List of tuples representing items to get from port.
- ('key': 'value')
port : Port
Port object to get items from.
"""

with port.crane.request() as req:
# Join queue to be active vessel at port
queue_start = vessel.env.now
yield req
queue_time = vessel.env.now - queue_start
if queue_time > 0:
vessel.submit_action_log("Queue", queue_time)

for i in items:
wait_start = vessel.env.now
item = yield port.get(lambda x: x.type == i)
wait_time = vessel.env.now - wait_start

if wait_time > 0:
vessel.submit_action_log(f"Wait for {item}", wait_time)

action, time = item.fasten(**kwargs)
vessel.storage.put_item(item)

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

0 comments on commit f8d6ac4

Please sign in to comment.