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

New feature: Custom Semisubmersible design #172

Open
wants to merge 12 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions ORBIT/core/defaults/common_costs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ semisubmersible_design:
truss_CR: 6250 # USD/t
heave_plate_CR: 6250 # USD/t
secondary_steel_CR: 7250 # USD/t
steel_CR: 4500 # USD/t *CustomSemiSub*
ballast_material_CR: 150
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have a units comment as well?


# Mooring system component cost rates
mooring_system_design: # USD/m
Expand Down
2 changes: 2 additions & 0 deletions ORBIT/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
SemiSubmersibleDesign,
CustomArraySystemDesign,
OffshoreSubstationDesign,
CustomSemiSubmersibleDesign,
SemiTautMooringSystemDesign,
OffshoreFloatingSubstationDesign,
)
Expand Down Expand Up @@ -80,6 +81,7 @@ class ProjectManager:
MooringSystemDesign,
SemiTautMooringSystemDesign,
SemiSubmersibleDesign,
CustomSemiSubmersibleDesign,
SparDesign,
ElectricalDesign,
)
Expand Down
5 changes: 4 additions & 1 deletion ORBIT/phases/design/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
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 .semi_submersible_design import (
SemiSubmersibleDesign,
CustomSemiSubmersibleDesign,
)
from .SemiTaut_mooring_system_design import SemiTautMooringSystemDesign
271 changes: 271 additions & 0 deletions ORBIT/phases/design/semi_submersible_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
__maintainer__ = "Jake Nunemaker"
__email__ = "[email protected]"

import numpy as np

from ORBIT.phases.design import DesignPhase

"""
Expand Down Expand Up @@ -220,3 +222,272 @@ def detailed_output(self):
}

return _outputs


"""Provides the `CustomSemiSubmersibleDesign` class."""

__author__ = "Matt Shields"
__copyright__ = "Copyright 2020, National Renewable Energy Laboratory"
__maintainer__ = "Nick Riccobono"
__email__ = "[email protected]"

"""
Based on semisub design from 15 MW RWT

[1] C. Allen et al., “Definition of the UMaine VolturnUS-S Reference Platform
Developed for the IEA Wind 15-Megawatt Offshore Reference Wind Turbine,”
NREL/TP--5000-76773, 1660012, MainId:9434, Jul. 2020. doi: 10.2172/1660012.
[2] K. L. Roach, M. A. Lackner, and J. F. Manwell, “A New Methodology for
Upscaling Semi-submersible Platforms for Floating Offshore Wind Turbines,”
Wind Energy Science Discussions, pp. 1–33, Feb. 2023,
doi: 10.5194/wes-2023-18.
"""


class CustomSemiSubmersibleDesign(DesignPhase):
"""Customized Semi-Submersible Substructure Design."""

expected_config = {
"site": {"depth": "m"},
"plant": {"num_turbines": "int"},
"turbine": {"turbine_rating": "MW"},
"semisubmersible_design": {
"towing_speed": "km/h (optional, default: 6)",
"column_diameter": "m (optional, default: 12.5)",
"wall_thickness": "m (optional, default: 0.045)",
"column_height": "m (optional, default: 35)",
"pontoon_length": "m (optional, default: 51.75)",
"pontoon_width": "m (optional, default: 12.5)",
"pontoon_height": "m (optional, default: 7)",
"strut_diameter": "m (optional, 0.9)",
"steel_density": "kg/m^3 (optional, default: 7980)",
"ballast_mass": "tonnes (optional, default 2540)",
"tower_interface_mass": "tonnes (optional, default 100)",
"steel_CR": "$/tonne (optional, default: 4500)",
"ballast_material_CR": "$/tonne (optional, default: 150)",
},
}

output_config = {
"substructure": {
"mass": "t",
"unit_cost": "USD",
"towing_speed": "km/h",
}
}

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

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

config = self.initialize_library(config, **kwargs)
self.config = self.validate_config(config)
self._design = self.config.get("semisubmersible_design", {})

self.num_columns = kwargs.get("number_columns", 3)

self._outputs = {}

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

self.calc_geometric_scale_factor()

substructure = {
"mass": self.substructure_mass,
"unit_cost": self.substructure_unit_cost,
"towing_speed": self._design.get("towing_speed", 6),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a general comment, and I'm recognizing this is an engrained pattern in the code we've already noted, but it'd be really great to move the default values out of the methods themselves.

}

self._outputs["substructure"] = substructure

def calc_geometric_scale_factor(self, **kwargs):
"""Calculates the geometric factor to scale the size of the semi-
submersible. Upscaling methodology and parameters used are found on
Lines 335-340 [2].
"""

turbine_diameter = self.config["turbine"].get("rotor_diameter")

if turbine_diameter is None:
raise KeyError("Turbine rotor diameter not specified.")

# IEA-15MW 120m radius
ref_radius = kwargs.get("ref_radius", 120.0)

# power-law parameter
alpha = kwargs.get("alpha", 0.72)

self.geom_scale_factor = (
float(turbine_diameter) / 2 / ref_radius
) ** alpha

@property
def bouyant_column_volume(self):
"""
Returns the volume of a capped, hollow, cylindrical column,
assuming the wall-thickness remains constant [2].
"""

dc = self._design.get("column_diameter", 12.5) * self.geom_scale_factor
hc = self._design.get("column_height", 35) * self.geom_scale_factor
tc = self._design.get("wall_thickness", 0.045)

return (np.pi / 4) * (hc * dc**2 - (hc - 2 * tc) * (dc - 2 * tc) ** 2)

@property
def center_column_volume(self):
"""
Returns the volume of a hollow cylindrical column between turbine and
pontoons, assuming wall-thickness remains constant [2].
"""

dc = 10.0 # fixed tower diameter
hc = self._design.get("column_height", 35) * self.geom_scale_factor
hp = self._design.get("pontoon_height", 7) * self.geom_scale_factor
tc = self._design.get("wall_thickness", 0.045)

return (np.pi / 4) * (
(hc - hp) * dc**2 - (hc - hp) * (dc - 2 * tc) ** 2
)

@property
def pontoon_volume(self):
"""
Returns the volume of a single hollow rectangular pontoon that connects
the central column to the outer columns, assuming wall-thickness
reamins constant [2].
"""
# TODO: Subtract semi-circular area from fairlead column?

lp = self._design.get("pontoon_length", 51.75) * self.geom_scale_factor
wp = self._design.get("pontoon_width", 12.5) * self.geom_scale_factor
hp = self._design.get("pontoon_height", 7) * self.geom_scale_factor
tp = self._design.get("wall_thickness", 0.045)

return (hp * wp - (hp - 2 * tp) * (wp - 2 * tp)) * lp

@property
def strut_volume(self):
"""
Returns the volume of a single solid strut that connects
the central column to the outer columns.
"""

lp = self._design.get("pontoon_length", 51.75) * self.geom_scale_factor
ds = self._design.get("strut_diameter", 0.9) * self.geom_scale_factor

return (np.pi / 4) * (ds**2) * lp

@property
def substructure_steel_mass(self):
"""Returns the total mass of structural steel in the substructure."""

# TODO: Separate out different steels for each component

density = self._design.get("steel_density", 7980)

return (density / 1000) * (
self.num_columns * self.bouyant_column_volume
+ self.center_column_volume
+ self.num_columns * self.pontoon_volume
+ self.num_columns * self.strut_volume
)

@property
def ballast_mass(self):
"""Returns the mass of fixed ballast. Default value from [1]."""
# TODO: Scale ballast mass with some factor?
# Fixed/Fluid needs to be addressed because 11,300t of seawater is used
# to submerge the platform.

return self._design.get("ballast_mass", 2540)

@property
def tower_interface_mass(self):
"""Returns the mass of tower interface. Default value from [1]."""

# TODO: Find a model to estimate the mass for a tower interface

return self._design.get("tower_interface_mass", 100)

@property
def substructure_steel_cost(self):
"""Returns the total cost of structural steel of the substructure
in $USD.
"""

_key = "steel_CR"
self.steel_cr = self._design.get(
_key, self.get_default_cost("semisubmersible_design", _key)
)
return self.steel_cr * self.substructure_steel_mass

@property
def substructure_mass(self):
"""
Returns the total mass of structural steel and iron ore ballast
in the substructure.
"""

return sum(
[
self.substructure_steel_mass,
self.ballast_mass,
self.tower_interface_mass,
]
)

@property
def substructure_unit_cost(self):
"""
Returns the total material cost of a single substructure.
Does not include final assembly or transportation costs.
"""

_key = "ballast_material_CR"
ballast_cr = self._design.get(
_key, self.get_default_cost("semisubmersible_design", _key)
)

return (
self.substructure_steel_cost
+ ballast_cr * self.ballast_mass
+ self.steel_cr * self.tower_interface_mass
)

@property
def design_result(self):
"""Returns the result of `self.run()`."""

if not self._outputs:
raise Exception("Has `CustomSemiSubmersibleDesign` been ran yet?")

return self._outputs

@property
def total_cost(self):
"""Returns total phase cost in $USD."""

num = self.config["plant"]["num_turbines"]
return num * self.substructure_unit_cost

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

_outputs = {
"substructure_steel_mass": self.substructure_steel_mass,
"substructure_steel_cost": self.substructure_steel_cost,
"substructure_mass": self.substructure_mass,
"substructure_cost": self.substructure_unit_cost,
"ballast_mass": self.ballast_mass,
"tower_interface_mass": self.tower_interface_mass,
}

return _outputs
12 changes: 12 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ ORBIT Changelog

Unreleased (TBD)
----------------
Custom Semi-Submersible Design
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The ``CustomSemiSubmersibleDesign`` class was added to semi_submersible_design.py.
- Based on VolturnUS-S by U-Maine. `https://www.nrel.gov/docs/fy20osti/76773.pdf`
- All design defaults are set to be consistent with 15MW IEA turbine
- Scaling logic added based on U-Mass paper. `https://wes.copernicus.org/preprints/wes-2023-18/wes-2023-18.pdf`
- Added tests to confirm that volumns and masses are calculated appropriately
- Added Example - Custom Semisubmersible notebook and yaml config file.
- Updated docs with citations.

Relocate-get-costs
~~~~~~~~~~~~~~~~~~
- Relocated all the get design costs in each design class to `common_cost.yaml`. Spar, Semisub, monopile, electrical, offshore substation, and mooring designs all recieved this update.

Merged SemiTaut Moorings
Expand Down
30 changes: 29 additions & 1 deletion docs/source/phases/design/doc_SemiSubmersibleDesign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,36 @@ The semi-submersible design module in ORBIT is based on previous modeling
efforts undertaken by NREL, [#maness2017]_. The technical documentation for
this tool can be found `here <https://www.nrel.gov/docs/fy17osti/66874.pdf>_`.

Custom Semi-Submersible Design Methodology
==========================================
For details of the code implementation, please see
:doc:`Semi-Submersible Design API <api_SemiSubmersibleDesign>`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be referencing the custom version, not the standard version"


(Custom) Overview
-----------------

This new addition to ORBIT's semi-submersible design capabilities by letting
the user specify all the dimensions of the platform. IEAt was designed based on
the IEA-15MW reference turbine and the VolturnUS-S Reference Platform developed
by the University of Maine.[#allen2020]_.The triangular base uses three steel
columns equipped with ballast material. Each column is connected to a
central-column, where the turbine is afixed, by pontoons and spars.
Additionally, this new model uses the methodology proposed in [#roach2023]_ to
scale the platform to other turbine sizes.

References
----------

.. [#maness2017] Michael Maness, Benjamin Maples, Aaron Smith,
NREL Offshore Balance-of-System Model, 2017
NREL Offshore Balance-of-System Model, 2017.
`https://www.nrel.gov/docs/fy17osti/66874.pdf`

.. [#allen2020] Christopher Allen, Anthony Viselli, Habib Dagher,
Andrew Goupee, Evan Gaertner, Nikhar Abbas, Matthew Hall,
and Garrett Barter. Definition of the UMaine VolturnUS-S Reference Platform
Developed for the IEA Wind 15-Megawatt Offshore Reference Wind Turbine.
`https://www.nrel.gov/docs/fy20osti/76773.pdf`

.. [#roach2023] Kaylie Roach, Matthew Lackner, James Manwell. A New Method for
Upscaling Semi-submersible Platforms for Floating Offshore Wind Turbines.
`https://wes.copernicus.org/preprints/wes-2023-18/wes-2023-18.pdf`
Loading
Loading