Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semi taut mooring update #158

Merged
merged 77 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
d080dde
Updated mooring system installation and hookup times.
rrolph575 Jan 13, 2023
1d1432e
working on mooring update
rrolph575 Jan 19, 2023
15c5c3e
Added the Semi-taut mooring system design module
rrolph575 Jan 20, 2023
5b4023f
Added SemiTaut design phase to design init
rrolph575 Jan 20, 2023
c5b72f9
fixed import so new semitaut design module is running. might be givin…
rrolph575 Jan 21, 2023
0472130
Changed water depth to match comparison project
rrolph575 Jan 24, 2023
7eb2d76
Created a Floating Offshore Substation design module that uses (for n…
rrolph575 Feb 4, 2023
2aa20b8
last commit didnt have all changes for adding floating oss design module
rrolph575 Feb 4, 2023
e2fc7a5
testing why floatingPY is called twice
rrolph575 Feb 4, 2023
3744934
adding example run scripts that show 2x print statement
rrolph575 Feb 4, 2023
ba3ab5d
corrected that all moorings from farm were being added to each substa…
rrolph575 Feb 6, 2023
9ecee38
fixed syntax errors
rrolph575 Feb 6, 2023
8209570
updated example notebooks
rrolph575 Feb 6, 2023
63203fa
Merge branch 'electrical-refactor' into SemiTaut_Mooring_Update
JakeNunemaker Feb 21, 2023
ff6ebd9
Added flag for floating OSS in ElectricalDesign (zero out monopile cost)
Feb 21, 2023
f930b22
Remove print statement
Feb 21, 2023
8ca374c
allow floating_oss to be optional
Feb 22, 2023
30795da
Suppress print statement in oss_install/floating.py
Feb 23, 2023
d91fe4a
Merge branch 'SemiTaut_Mooring_Update' of https://github.com/WISDEM/O…
Feb 23, 2023
48f84b0
Make delay messages more informative
Mar 3, 2023
866c8d2
Add electrical commissioning process (and update nacelle lift time) i…
Mar 15, 2023
44e29b6
Merge branch 'SemiTaut_Mooring_Update' of https://github.com/WISDEM/O…
Mar 15, 2023
53559c3
Update tower section process time in TurbineAssemblyLine
Mar 15, 2023
5b400ab
Added constraint for O&M activities at port to TurbineAssemblyLine.mo…
Mar 15, 2023
5cc306d
Feature/phase dates rework (#135)
JakeNunemaker Mar 14, 2023
e58c886
change default value to boolean
Mar 15, 2023
04009f2
implementation for transfering and installing
Mar 16, 2023
841b4d0
Revised port_in_use constraint.
JakeNunemaker Mar 28, 2023
41bc013
Merge branch 'electrical-refactor' into SemiTaut_Mooring_Update
JakeNunemaker Apr 4, 2023
5e27d67
Merge branch 'electrical-refactor' into SemiTaut_Mooring_Update
JakeNunemaker Apr 7, 2023
f66affc
improve delay message and formatting
Apr 9, 2023
9684fec
use user-defined wave height and wind speed limits
Apr 9, 2023
25fafaf
Added dynamic cables (20% cost adder), cable lay/bury times, and cabl…
Apr 14, 2023
2cd2098
Update CLV carousel specs to match Leonardo da
Apr 14, 2023
347fcf2
change delay message
Apr 17, 2023
b3150a5
change vessel speeds
Apr 18, 2023
65bfb0d
remove duplicate transit back to port
Apr 20, 2023
d118855
Added project progress debug logs to MooredSubInstallation and Floati…
JakeNunemaker Apr 21, 2023
c540a76
allow separate cable lay burial rates
Apr 22, 2023
50144ca
account for multiple assembly lines in installation costs
May 1, 2023
5a38ca4
update vessel specs
May 26, 2023
3baed36
Revert solo monopile install to original get items from port method
JakeNunemaker Oct 20, 2023
9cd1244
Added semitaut arrays features to mooringsystem. Created rope/chain d…
nRiccobo Apr 30, 2024
671bd2a
Added anchor cost and total line cost of semitaut option.
nRiccobo May 1, 2024
9f5ea2e
Merge dev into SemiTaut_Mooring_Update
nRiccobo May 1, 2024
da759ea
Cleaned up interp objects and repetitive lines.
nRiccobo May 1, 2024
7413a00
renamed semitaut dict to semitaut_params for clarity
nRiccobo May 1, 2024
0f56d8e
Cleaned up pre-commit warnings/errors like unused kwargs and line len…
nRiccobo May 2, 2024
4e0f5df
Check if ahts_vessel is None rather than a KeyError.
nRiccobo May 2, 2024
6b8c2c7
Made ahts_vessel optional temporarily to raise warnings rather than a…
nRiccobo May 2, 2024
6573b45
Adjusted floating true/false logic in electrical export. Eliminate re…
nRiccobo May 2, 2024
3e2b6c8
Updated semitaut examples.
nRiccobo May 21, 2024
fab774b
Updated electrical_design testing to check floating/fixed offshore su…
nRiccobo May 31, 2024
c10f253
Updated oss_design test for floating/fixed options
nRiccobo May 31, 2024
3aa5aa5
Adjusted floating/standard oss install to count for floating and fix …
nRiccobo May 31, 2024
6d8e411
Cleaned up line length and mass calc and included TLP option
nRiccobo May 31, 2024
b36453f
Rearranged floating or fixed logic. Set substructure length=0 for flo…
nRiccobo May 31, 2024
1bf2472
Rearranged mooring type logic to default to catenary
nRiccobo May 31, 2024
c358c3d
Made interp1d extrapolate outside range and connected mooring_list_co…
nRiccobo May 31, 2024
eaee68a
Updated floating example and config yaml
nRiccobo May 31, 2024
adc1671
cleaned up fixed added length for semitaut. Updated test
nRiccobo Jun 3, 2024
959e46b
Updated yaml configs and example notebooks
nRiccobo Jun 3, 2024
f628d04
Updated moored deprecated warnings and a test
nRiccobo Jun 3, 2024
909ee37
WISDEM api test failed because of new ahts vessel logic.
nRiccobo Jun 4, 2024
d990832
Updated test yaml configs to deal with ahts and support vessel calls
nRiccobo Jun 4, 2024
b352386
adjusted the name and comments for this temporary class.
nRiccobo Jun 5, 2024
411e45b
Added comment for oss substructure mass calcs.
nRiccobo Jun 5, 2024
50ae6a1
Updated electrical design tests and fixed duplicate line in Electrica…
nRiccobo Jun 6, 2024
5e27035
Updating floating example and cleaned comments in oss_floating.
nRiccobo Jun 7, 2024
65728e8
Added reference for semitaut line constants
nRiccobo Jun 10, 2024
8749076
Removed commented anchor install cost method. Added to process_time
nRiccobo Jun 10, 2024
90bb8c5
Updated tests to verify defaults. Cleaned up single character object …
nRiccobo Jun 10, 2024
bdcff41
Updated changelog and docs.
nRiccobo Jun 10, 2024
08cae48
changelog and floating.py clean up and update after file save.
nRiccobo Jun 10, 2024
41e57c2
Addressed the deprecated support_vessel call in gravity-base install.…
nRiccobo Jun 14, 2024
90dd2cf
Had to remove a print statement that I left.
nRiccobo Jun 17, 2024
a133c01
Updated sim setup test to sweep different configs.
nRiccobo Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ORBIT/api/wisdem.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ def setup(self):
3,
desc="Number of station keeping vessels that attach to floating platforms under tow-out.",
)
self.add_discrete_input(
"ahts_vessels",
1,
desc="Number of ahts vessels that attach to floating platforms under tow-out.",
)
self.add_discrete_input(
"oss_install_vessel",
"example_heavy_lift_vessel",
Expand Down Expand Up @@ -378,11 +383,13 @@ def compile_orbit_config_file(self, inputs, outputs, discrete_inputs, discrete_o
if floating_flag:
vessels = {
"support_vessel": "example_support_vessel",
"ahts_vessel" : "example_ahts_vessel",
"towing_vessel": "example_towing_vessel",
"mooring_install_vessel": "example_support_vessel",
"towing_vessel_groups": {
"towing_vessels": int(discrete_inputs["num_towing"]),
"station_keeping_vessels": int(discrete_inputs["num_station_keeping"]),
"ahts_vessels" : int(discrete_inputs["ahts_vessels"])
},
}
else:
Expand Down
2 changes: 1 addition & 1 deletion ORBIT/core/defaults/process_times.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"mooring_system_load_time": 5 # hr
"mooring_site_survey_time": 4 # hr
"suction_pile_install_time": 11 # hr
"drag_embed_install_time": 5 # hr
"drag_embed_install_time": 12 # hr # Increased from 5h so it now includes proof loading. This time increases with depth (in mooring.py)
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved

# Misc.
"site_position_time": 2 # hr
Expand Down
3 changes: 2 additions & 1 deletion ORBIT/core/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@
import yaml
import pandas as pd
from yaml import Dumper

from ORBIT.core.exceptions import LibraryItemNotFoundError

ROOT = os.path.abspath(os.path.join(os.path.abspath(__file__), "../../.."))
default_library = os.path.join(ROOT, "library")


# Need a custom loader to read in scientific notation correctly
class CustomSafeLoader(yaml.SafeLoader):
def construct_python_tuple(self, node):
Expand Down Expand Up @@ -277,6 +277,7 @@ def export_library_specs(key, filename, data, file_ext="yaml"):
"wtiv": "vessels",
"towing_vessel": "vessels",
"support_vessel": "vessels",
"ahts_vessel": "vessels",
# cables
"cables": "cables",
"array_system": "cables",
Expand Down
4 changes: 4 additions & 0 deletions ORBIT/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
ArraySystemDesign,
ExportSystemDesign,
MooringSystemDesign,
SemiTautMooringSystemDesign,
ScourProtectionDesign,
SemiSubmersibleDesign,
CustomArraySystemDesign,
OffshoreSubstationDesign,
OffshoreFloatingSubstationDesign,
)
from ORBIT.phases.install import (
JacketInstallation,
Expand Down Expand Up @@ -69,7 +71,9 @@ class ProjectManager:
ExportSystemDesign,
ScourProtectionDesign,
OffshoreSubstationDesign,
OffshoreFloatingSubstationDesign,
MooringSystemDesign,
SemiTautMooringSystemDesign,
SemiSubmersibleDesign,
SparDesign,
ElectricalDesign,
Expand Down
167 changes: 167 additions & 0 deletions ORBIT/phases/design/SemiTaut_mooring_system_design.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""`MooringSystemDesign` and related functionality."""

__author__ = "Jake Nunemaker, modified by Becca F."
__copyright__ = "Copyright 2020, National Renewable Energy Laboratory"
__maintainer__ = "Jake Nunemaker"
__email__ = "[email protected] & [email protected]"

from scipy.interpolate import interp1d
import numpy as np

from ORBIT.phases.design import DesignPhase


class SemiTautMooringSystemDesign(DesignPhase):
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
"""SemiTaut Mooring System and Anchor Design."""

expected_config = {
"site": {"depth": "float"},
"turbine": {"turbine_rating": "int | float"},
"plant": {"num_turbines": "int"},
"mooring_system_design": {
"num_lines": "int | float (optional, default: 4)",
"anchor_type": "str (optional, default: 'Drag Embedment')",
"mooring_line_cost_rate": "int | float (optional)",
"drag_embedment_fixed_length": "int | float (optional, default: .5km)",
},
}

output_config = {
"mooring_system": {
"num_lines": "int",
#"line_diam": "m, float", # this is not needed for mooring.py
"line_mass": "t", # you need this for mooring.py (mooring installation module)
"line_cost": "USD", # you can calculate this based on each rope&chain length & diameter.
"line_length": "m", # this can be calculated from rope length and chain length (which you get from an empirical eqn as function of depth)
"anchor_mass": "t", # you need this for mooring.py (mooring installation module)
"anchor_type": "str", # keep, changed default to drag embedment.
"anchor_cost": "USD", # this can be calculated also as a function of (depth?) from the empirical data you have.
}
}

def __init__(self, config, **kwargs):
"""
Creates an instance of SemiTautMooringSystemDesign.

Parameters
----------
config : dict
"""

config = self.initialize_library(config, **kwargs)
self.config = self.validate_config(config)
self.num_turbines = self.config["plant"]["num_turbines"]

self._design = self.config.get("mooring_system_design", {})
self.num_lines = self._design.get("num_lines", 4)
self.anchor_type = self._design.get("anchor_type", "Drag Embedment")

self._outputs = {}

def run(self):
"""
Main run function.
"""

self.calculate_line_length_mass()
self.determine_mooring_line_cost()
self.calculate_anchor_mass_cost()

self._outputs["mooring_system"] = {**self.design_result}

def calculate_line_length_mass(self):
"""
Returns the mooring line length and mass.
"""

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

# Input hybrid mooring system design from Cooperman et al. (2022), https://www.nrel.gov/docs/fy22osti/82341.pdf 'Assessment of Offshore Wind Energy Leasing Areas for Humboldt and Moorow Bay Wind Energy Areas, California
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
depths = np.array([500, 750, 1000, 1250, 1500])
rope_lengths = np.array([478.41, 830.34, 1229.98, 1183.93, 1079.62])
rope_diameters = np.array([0.2, 0.2, 0.2, 0.2, 0.2]) # you need the diameter for the cost data
chain_lengths = np.array([917.11, 800.36, 609.07, 896.42, 1280.57])
chain_diameters = np.array([0.13, 0.17, 0.22, 0.22, 0.22])

# Interpolate
finterp_rope = interp1d(depths, rope_lengths)
finterp_chain = interp1d(depths, chain_lengths)
finterp_rope_diam = interp1d(depths, rope_diameters)
finterp_chain_diam = interp1d(depths, chain_diameters)

# Rope and chain length at project depth
self.chain_length = finterp_chain(depth)
self.rope_length = finterp_rope(depth)
# Rope and chain diameter at project depth
self.rope_diameter = finterp_rope_diam(depth)
self.chain_diameter = finterp_chain_diam(depth)

self.line_length = self.rope_length + self.chain_length

chain_kg_per_m = 19900 * (self.chain_diameter**2) # 19,900 kg/m^2 (diameter)/m (length)
rope_kg_per_m = 797.8 * (self.rope_diameter**2) # 797.8 kg/ m^2 (diameter) / m (length)
self.line_mass = (self.chain_length * chain_kg_per_m) + (self.rope_length * rope_kg_per_m) # kg
#print('total hybrid line mass is ' + str(self.line_mass) + 'kg')
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
# convert kg to metric tonnes
self.line_mass = self.line_mass/1e3

def calculate_anchor_mass_cost(self):
"""
Returns the mass and cost of anchors.

TODO: Anchor masses are rough estimates based on initial literature
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
review. Should be revised when this module is overhauled in the future.
"""

if self.anchor_type == "Drag Embedment":
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
self.anchor_mass = 20 # t. This should be updated, but I don't have updated data right now for this.
# Input hybrid mooring system design from Cooperman et al. (2022), https://www.nrel.gov/docs/fy22osti/82341.pdf 'Assessment of Offshore Wind Energy Leasing Areas for Humboldt and Moorow Bay Wind Energy Areas, California
depths = np.array([500, 750, 1000, 1250, 1500])
anchor_costs = np.array([112766, 125511, 148703, 204988, 246655]) # [USD]
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved

# interpolate anchor cost to project depth
depth = self.config["site"]["depth"]
finterp_anchor_cost = interp1d(depths, anchor_costs)
self.anchor_cost = finterp_anchor_cost(depth) # replace this with interp. function based on depth of hybrid mooring line
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved

def determine_mooring_line_cost(self):
"""Returns cost of one line mooring line."""
# Input hybrid mooring system design from Cooperman et al. (2022), https://www.nrel.gov/docs/fy22osti/82341.pdf 'Assessment of Offshore Wind Energy Leasing Areas for Humboldt and Moorow Bay Wind Energy Areas, California
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
depths = np.array([500, 750, 1000, 1250, 1500]) # [m]
total_line_costs = np.array([826598, 1221471, 1682208, 2380035, 3229700]) # [USD]
finterp_total_line_cost = interp1d(depths, total_line_costs)
depth = self.config["site"]["depth"]
self.line_cost = finterp_total_line_cost(depth)
return self.line_cost

@property
def total_cost(self):
"""Returns the total cost of the mooring system."""

return (
self.num_lines
* self.num_turbines
* (self.anchor_cost + self.line_cost)
)

@property
def detailed_output(self):
"""Returns detailed phase information."""

return {
"num_lines": self.num_lines,
#"line_diam": self.line_diam,
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
"line_mass": self.line_mass,
"line_length": self.line_length,
"line_cost": self.line_cost,
"anchor_type": self.anchor_type,
"anchor_mass": self.anchor_mass,
"anchor_cost": self.anchor_cost,
"system_cost": self.total_cost,
}

@property
def design_result(self):
"""Returns the results of the design phase."""

return {"mooring_system": self.detailed_output}
2 changes: 2 additions & 0 deletions ORBIT/phases/design/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from .monopile_design import MonopileDesign
from .electrical_export import ElectricalDesign
from .array_system_design import ArraySystemDesign, CustomArraySystemDesign
from .oss_design_floating import OffshoreFloatingSubstationDesign
from .export_system_design import ExportSystemDesign
from .mooring_system_design import MooringSystemDesign
from .scour_protection_design import ScourProtectionDesign
from .semi_submersible_design import SemiSubmersibleDesign
from .SemiTaut_mooring_system_design import SemiTautMooringSystemDesign
61 changes: 42 additions & 19 deletions ORBIT/phases/design/electrical_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ElectricalDesign(CableSystem):
"converter_cost": "USD (optional)",
"onshore_converter_cost": "USD (optional)",
"topside_assembly_factor": "float (optional)",
"oss_substructure_type": "str (optional, default: Monopile)",
"oss_substructure_cost_rate": "USD/t (optional)",
"oss_pile_cost_rate": "USD/t (optional)",
},
Expand Down Expand Up @@ -126,20 +127,24 @@ def __init__(self, config, **kwargs):
"interconnection_distance", 3
)

# SUBSTATION
self._outputs = {}

def run(self):
"""Main run function."""

self.export_system_design = self.config["export_system_design"]
self.offshore_substation_design = self.config.get(
"substation_design", {}
)

self.substructure_type = self.offshore_substation_design.get(
"oss_substructure_type", "Monopile"
)

self.onshore_substation_design = self.config.get(
"onshore_substation_design", {}
)

self._outputs = {}

def run(self):
"""Main run function."""

# CABLES
self._initialize_cables()
self.cable = self.cables[[*self.cables][0]]
Expand Down Expand Up @@ -183,7 +188,7 @@ def run(self):
self.calc_onshore_cost()

self._outputs["offshore_substation_substructure"] = {
"type": "Monopile", # Substation install only supports monopiles
"type": self.substructure_type,
"deck_space": self.substructure_deck_space,
"mass": self.substructure_mass,
"length": self.substructure_length,
Expand Down Expand Up @@ -346,19 +351,18 @@ def calc_num_substations(self):
----------
substation_capacity : int | float
"""
self._design = self.config.get("substation_design", {})

# HVAC substation capacity
_substation_capacity = self._design.get(
_substation_capacity = self.offshore_substation_design.get(
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved
"substation_capacity", 1200
) # MW

if "HVDC" in self.cable.cable_type:
self.num_substations = self._design.get(
self.num_substations = self.offshore_substation_design.get(
"num_substations", int(self.num_cables / 2)
)
else:
self.num_substations = self._design.get(
self.num_substations = self.offshore_substation_design.get(
"num_substations",
int(np.ceil(self._plant_capacity / _substation_capacity)),
)
Expand Down Expand Up @@ -494,8 +498,10 @@ def calc_assembly_cost(self):
topside_assembly_factor : int | float
"""

_design = self.config.get("substation_design", {})
topside_assembly_factor = _design.get("topside_assembly_factor", 0.075)
topside_assembly_factor = self.offshore_substation_design.get(
"topside_assembly_factor", 0.075
)

self.land_assembly_cost = (
self.switchgear_cost
+ self.shunt_reactor_cost
Expand All @@ -512,14 +518,27 @@ def calc_substructure_mass_and_cost(self):
oss_pile_cost_rate : int | float
"""

_design = self.config.get("substation_design", {})
substructure_mass = 0.4 * self.topside_mass
oss_pile_cost_rate = _design.get("oss_pile_cost_rate", 0)
oss_substructure_cost_rate = _design.get(
oss_pile_cost_rate = self.offshore_substation_design.get(
"oss_pile_cost_rate", 0
)
oss_substructure_cost_rate = self.offshore_substation_design.get(
"oss_substructure_cost_rate", 3000
)

substructure_pile_mass = 8 * substructure_mass**0.5574
# Substructure mass components calculated by curve fits in
# equations 81-84 from Maness et al. 2017
# https://www.nrel.gov/docs/fy17osti/66874.pdf
#
# TODO: Determine a better method to calculate substructure mass
# for different substructure types
substructure_mass = 0.4 * self.topside_mass

if self.substructure_type == "Floating":
substructure_pile_mass = 0 # No piles used for floating platform

else:
substructure_pile_mass = 8 * substructure_mass**0.5574
nRiccobo marked this conversation as resolved.
Show resolved Hide resolved

self.substructure_cost = (
substructure_mass * oss_substructure_cost_rate
+ substructure_pile_mass * oss_pile_cost_rate
Expand All @@ -532,7 +551,11 @@ def calc_substructure_length(self):
Calculates substructure length as the site depth + 10m
"""

self.substructure_length = self.config["site"]["depth"] + 10
if self.substructure_type == "Floating":
self.substructure_length = 0

else:
self.substructure_length = self.config["site"]["depth"] + 10

def calc_substructure_deck_space(self):
"""
Expand Down
Loading
Loading