From 180d9b242650b05a7c179fec8b83a2eb5381054c Mon Sep 17 00:00:00 2001 From: Kiefer Date: Fri, 21 Apr 2023 10:42:39 -0500 Subject: [PATCH 1/6] Added Floating Platform Model and testsl --- hopp/offshore/floating_platform.py | 345 ++++++++++++++++++ .../test_offshore/test_offshore_floating.py | 46 +++ 2 files changed, 391 insertions(+) create mode 100644 hopp/offshore/floating_platform.py create mode 100644 tests/hopp/test_offshore/test_offshore_floating.py diff --git a/hopp/offshore/floating_platform.py b/hopp/offshore/floating_platform.py new file mode 100644 index 000000000..c73d40567 --- /dev/null +++ b/hopp/offshore/floating_platform.py @@ -0,0 +1,345 @@ +""" +Author:Charles Kiefer +Date: 4/11/2023 +Institution: National Renewable Energy Lab +Description: This file shall handle costing and sizing of offshore floating platforms deicated to hydrogen production. It uses the + same foundation as fixed_platform.py. Both have been modeled off of existing BOS cost/sizing calculations fond in ORBIT. + It can be run as standalone functions or as appended ORBIT project phases. + + + +Sources: + - [1] ORBIT: https://github.com/WISDEM/ORBIT electrical_refactor branch & SemiTaut_mooring branch +Args: + - tech_required_area: (float): area needed for combination of all tech (m^2), not including buffer or working space + - tech_combined_mass: (float): mass of all tech being placed on the platform (kg or tonnes)year + + + - depth: (float): bathometry at the platform location (m) + - distance_to_port: (float): distance ships must travel from port to site location (km) + + Future arguments: (Not used at this time) + - construction year (int): + - lifetime (int): lifetime of the plant in years (may not be needed) + +Returns: + - platform_mass (float): Adjusted mass of platform + substructure + - design_capex (float): capital expenditures (platform design + substructure fabrication) + - installation_capex (float): capital expenditures (installation cost) + - platform_opex (float): the OPEX (annual, fixed) in USD for the platform + +""" +''' +Notes: + Thank you Jake Nunemaker's oswh2 repository and Rebecca Fuchs SemiTaut_mooring repository!!! + pile_cost=0 $US/tonne for monopile construction. Not a bug, this # is consistent with the rest of ORBIT +''' + +import os +import math +# +from ORBIT import ProjectManager, load_config +from ORBIT.core import Vessel +from ORBIT.core.library import initialize_library +from ORBIT.phases.design import DesignPhase +from ORBIT.phases.install import InstallPhase +from scipy.interpolate import interp1d +import numpy as np + + +class FloatingPlatformDesign(DesignPhase): + ''' + This is a modified class based on ORBIT's design phase + ''' + + #phase = "H2 Floating Platform Design" + + # Expected inputs from config yaml file + expected_config = { + "site": { + "distance" : "int | float", + "depth" : "int | float", + }, + + "equipment": { + "tech_required_area" : "float", + "tech_combined_mass" : "float", + "topside_design_cost": "USD (optional, default:4.5e6)", + "fabrication_cost_rate": "USD/t (optional, default: 14500.)", + "substructure_steel_rate": "USD/t (optional, default: 3000.)", + } + + } + + # Takes in arguments and initialize library files + def __init__(self, config, **kwargs): + + self.phase = "H2 Floating Platform Design" + + config = self.initialize_library(config, **kwargs) + self.config = self.validate_config(config) + + self._outputs = {} + # Runs the design cost models + + def run(self): + + #print("Floating Platform Design run() is working!!!") + + self.distance = self.config['site']['distance'] # km + self.depth = self.config['site']['depth'] # m + + _platform = self.config.get('equipment',{}) + + self.mass = _platform.get('tech_combined_mass',999) # t + self.area = _platform.get('tech_required_area', 1000) # m**2 + + design_cost = _platform.get('topside_design_cost', 4.5e6) # USD + fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_cost', 3000) # USD/t + ##NEED updated version + # Add individual calcs/functions in the run() method + total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area, + self.depth, fab_cost, design_cost, steel_cost + ) + + # Create an ouput dict + self._outputs['floating_platform'] = { + "mass" : total_mass, + "area" : self.area, + "total_cost" : total_cost + } + + # A design object needs to have attribute design_result and detailed_output + @property + def design_result(self): + + return { + "platform_design":{ + "mass" : self._outputs['floating_platform']['mass'], + "area" : self._outputs['floating_platform']['area'], + "total_cost": self._outputs['floating_platform']['total_cost'], + } + } + + @property + def detailed_output(self): + + return {} + +class FloatingPlatformInstallation(InstallPhase): + ''' + This is a modified class based on ORBIT's install phase + ''' + + #phase = "H2 Floating Platform Installation" + + # Expected inputs from config yaml file + expected_config = { + "site": { + "distance" : "int | float", + "depth" : "int | float", + }, + + "equipment": { + "tech_required_area" : "float", + "tech_combined_mass" : "float", + "install_duration": "days (optional, default: 14)", + }, + + "oss_install_vessel" : "str | dict", + } + + # Need to initialize arguments and weather files + def __init__(self, config, weather=None, **kwargs): + + super().__init__(weather, **kwargs) + + config = self.initialize_library(config, **kwargs) + self.config = self.validate_config(config) + + self.initialize_port() + self.setup_simulation(**kwargs) + + # Setup simulation seems to be the install phase's equivalent run() module + def setup_simulation(self, **kwargs): + + #print("Floating Platform Install setup_sim() is working!!!") + + self.distance = self.config['site']['distance'] + self.depth = self.config['site']['depth'] + self.mass = self.config['equipment']['tech_combined_mass'] + self.area = self.config['equipment']['tech_required_area'] + + _platform = self.config.get('equipment', {}) + design_cost = _platform.get('topside_design_cost', 4.5e6) # USD + fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_cost', 3000) # USD/t + #NEED Updated version for floating + install_duration = _platform.get("install_duration", 14) # days + + # Initialize vessel + vessel_specs = self.config.get("oss_install_vessel", None) + name = vessel_specs.get("name","Offshore Substation Install Vessel") + + vessel = Vessel(name, vessel_specs) + self.env.register(vessel) + + vessel.initialize() + self.install_vessel = vessel + + # Add in the mass of the substructure to total mass (may or may not impact the final install cost) + _, substructure_mass = calc_substructure_mass_and_cost(self.mass, self.area, + self.depth, fab_cost, design_cost, steel_cost + ) + + total_mass = self.mass + substructure_mass # t + + # Call the install_platform function + self.install_capex = install_platform(total_mass, self.area, self.distance, \ + install_duration, self.install_vessel) + + # An install object needs to have attribute system_capex, installation_capex, and detailed output + @property + def system_capex(self): + + return {} + + @property + def installation_capex(self): + + return self.install_capex + + @property + def detailed_output(self): + + return {} + +# Define individual calculations and functions to use outside or with ORBIT +def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_cost=4.5e6, sub_cost=3000, pile_cost=0): + ''' + Platform is substructure and topside combined + All funstions are based off NREL's ORBIT (oss_design) + default values are specified in ORBIT + ''' + #Inputs needed + topside_mass = mass + topside_fab_cost_rate = fab_cost + topside_design_cost = design_cost + + '''Topside Cost & Mass + Topside Mass is the required Mass the platform will hold + Topside Cost is a function of topside mass, fab cost and design cost''' + topside_cost = topside_mass *topside_fab_cost_rate +topside_design_cost + + '''Substructure + Substructure Mass is a function of the topside mass + Substructure Cost is a function of of substructure mass pile mass and cost rates for each''' + + substructure_cost_rate = sub_cost # USD/t + + substructure_mass = 0.4 * topside_mass # t + substructure_cost = (substructure_mass *substructure_cost_rate) # USD + substructure_total_mass = substructure_mass # t + + '''These arrays were pulled from Rebecca Fuchs SemitTaut_mooring cost models''' + depths = np.array([0, 500, 750, 1000, 1250, 1500]) # [m] + + anchor_costs = np.array([28823, 112766, 125511, 148703, 204988, 246655]) # [USD] + finterp_anchor_cost = interp1d(depths, anchor_costs) + anchor_cost = finterp_anchor_cost(depth) + + total_line_costs = np.array([430315, 826598, 1221471, 1682208, 2380035, 3229700]) # [USD] + finterp_total_line_cost = interp1d(depths, total_line_costs) + line_cost = finterp_total_line_cost(depth) + + mooring_cost = line_cost + anchor_cost + + '''Total Platform capex = capex Topside + capex substructure''' + total_capex = (topside_cost + substructure_cost + mooring_cost) + platform_capex = total_capex # USD + platform_mass = substructure_total_mass + topside_mass # t + + return platform_capex, platform_mass + +#@process +def install_platform(mass, area, distance, install_duration=14, vessel=None): + ''' + A simplified platform installation costing model. + Total Cost = install_cost * duration + Compares the mass and/or deck space of equipment to the vessel limits to determine + the number of trips. Add an additional "at sea" install duration + ''' + # print("Install process worked!") + # If no ORBIT vessel is defined set default values (based on ORBIT's floating_heavy_lift_vessel) + if vessel == None: + vessel_cargo_mass = 7999 # t + vessel_deck_space = 3999 # m**2 + vessel_day_rate = 500001 # USD/day + vessel_speed = 7 # km/hr + else: + vessel_cargo_mass = vessel.storage.max_cargo_mass # t + vessel_deck_space = vessel.storage.max_deck_space # m**2 + vessel_day_rate = vessel.day_rate # USD/day + vessel_speed = vessel.transit_speed # km/hr + + #print("Max Vessel Cargo and Mass:", vessel_cargo_mass, vessel_deck_space) + + # Get the # of trips based on ships cargo/space limits + num_of_trips = math.ceil(max((mass / vessel_cargo_mass), (area / vessel_deck_space))) + #print("Number of trips: ", num_of_trips) + + # Total duration = double the trips + install_duration + duration = (2 * num_of_trips * distance) / (vessel_speed * 24) + install_duration # days + #print("Duration (days): %0.2f" % duration) + + # Final install cost is obtained by using the vessel's daily rate + install_cost = vessel_day_rate * duration # USD + + return install_cost + +def calc_platform_opex(capex, opex_rate=0.011): + ''' + Simple opex calculation based on a capex + https://www.acm.nl/sites/default/files/documents/study-on-estimation-method-for-additional-efficient-offshore-grid-opex.pdf + + Output in $USD/year + ''' + + opex = capex * opex_rate # USD/year + + #print("OpEx of platform:", opex) + + return opex + + +# Standalone test sections +if __name__ == '__main__': + print("\n*** New FloatingPlatform Standalone test section ***\n") + + orbit_libpath = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, os.pardir, 'ORBIT', 'library')) + print(orbit_libpath) + initialize_library(orbit_libpath) + + config_path = os.path.abspath(__file__) + config_fname = load_config(os.path.join(config_path, os.pardir, "example_floating_project.yaml")) + + + ProjectManager._design_phases.append(FloatingPlatformDesign) + ProjectManager._install_phases.append(FloatingPlatformInstallation) + + platform = ProjectManager(config_fname) + platform.run() + + design_capex = platform.design_results['platform_design']['total_cost'] + install_capex = platform.installation_capex + + #print("Project Params", h2platform.project_params.items()) + platform_opex = calc_platform_opex((design_capex + install_capex)) + + print("ORBIT Phases: ", platform.phases.keys()) + print(f"\tH2 Platform Design Capex: {design_capex:.0f} USD") + print(f"\tH2 Platform Install Capex: {install_capex:.0f} USD") + print('') + print(f"\tTotal H2 Platform Capex: {(design_capex+install_capex)/1e6:.0f} mUSD") + print(f"\tH2 Platform Opex: {platform_opex:.0f} USD/year") diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py new file mode 100644 index 000000000..1cff7017f --- /dev/null +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -0,0 +1,46 @@ +import pytest +import os + +from ORBIT import load_config +from hopp.offshore.fixed_platform import install_platform, calc_platform_opex, calc_substructure_mass_and_cost + +@pytest.fixture +def config(): + offshore_path = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, os.pardir,'hopp','offshore')) + + return load_config(os.path.join(offshore_path,"example_fixed_project.yaml")) + +def test_install_platform(config): + ''' + Test the code that calculates the platform installation cost + ''' + distance = 24 + mass = 2100 + area = 500 + + cost = install_platform(mass, area, distance, install_duration=14) + + assert pytest.approx(cost) == 7142871 + +def test_calc_substructure_mass_and_cost(config): + ''' + Test the code that calculates the CapEx from fixed_platform.py + ''' + topmass = 200 + toparea = 1000 + depth = 45 + + cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) + + assert pytest.approx(cost) == 8142358.32 + assert pytest.approx(mass, 1.) == 280.0 + +def test_calc_platform_opex(): + ''' + Test the code that calculates the OpEx from fixed_platform_h2.py + ''' + capex = 28e6 + opex_rate = 0.01 + cost = calc_platform_opex(capex, opex_rate) + + assert cost == 28e4 \ No newline at end of file From c7b525467758849fcd2b4b95a24ad57b8fb98a07 Mon Sep 17 00:00:00 2001 From: Kiefer Date: Thu, 1 Jun 2023 10:30:34 -0600 Subject: [PATCH 2/6] Updated Floating Platform Model and tests --- hopp/offshore/fixed_platform.py | 42 ++++++----- hopp/offshore/floating_platform.py | 72 +++++++++++-------- .../test_offshore/test_offshore_floating.py | 12 ++-- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/hopp/offshore/fixed_platform.py b/hopp/offshore/fixed_platform.py index d5bd6ee60..8d257cde4 100644 --- a/hopp/offshore/fixed_platform.py +++ b/hopp/offshore/fixed_platform.py @@ -53,16 +53,16 @@ class FixedPlatformDesign(DesignPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", - "topside_design_cost": "USD (optional, default:4.5e6)", - "fabrication_cost_rate": "USD/t (optional, default: 14500.)", - "substructure_steel_rate": "USD/t (optional, default: 3000.)", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", + "topside_design_cost_USD": "USD (optional, default:4.5e6)", + "fabrication_cost_rate_USD": "USD/t (optional, default: 14500.)", + "substructure_steel_rate_USD": "USD/t (optional, default: 3000.)", } } @@ -101,9 +101,9 @@ def run(self): # Create an ouput dict self._outputs['fixed_platform'] = { - "mass" : total_mass, - "area" : self.area, - "total_cost" : total_cost + "mass_t" : total_mass, + "area_m^2" : self.area, + "total_cost_USD" : total_cost } # A design object needs to have attribute design_result and detailed_output @@ -112,9 +112,9 @@ def design_result(self): return { "platform_design":{ - "mass" : self._outputs['fixed_platform']['mass'], - "area" : self._outputs['fixed_platform']['area'], - "total_cost": self._outputs['fixed_platform']['total_cost'], + "mass_t" : self._outputs['fixed_platform']['mass'], + "area_m^2" : self._outputs['fixed_platform']['area'], + "total_cost_USD": self._outputs['fixed_platform']['total_cost'], } } @@ -133,13 +133,13 @@ class FixedPlatformInstallation(InstallPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", "install_duration": "days (optional, default: 14)", }, @@ -214,6 +214,14 @@ def detailed_output(self): # Define individual calculations and functions to use outside or with ORBIT def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_cost=4.5e6, sub_cost=3000, pile_cost=0): ''' + calc_substructure_mass_and_cost returns the total mass including substructure, topside and equipment. Also returns the cost of the substructure and topside + Inputs: mass | Mass of equipment on platform (tonnes) + area | Area needed for equipment (meter^2) (not necessary) + depth | Ocean depth at platform location (meters) (not necessary) + fab_cost_rate | Cost rate to fabricate topside (USD/tonne) + design_cost | Design cost to design structural components (USD) from ORBIT + sub_cost_rate | Steel cost rate (USD/tonne) from ORBIT''' + ''' Platform is substructure and topside combined All funstions are based off NREL's ORBIT (oss_design) default values are specified in ORBIT diff --git a/hopp/offshore/floating_platform.py b/hopp/offshore/floating_platform.py index c73d40567..161503f05 100644 --- a/hopp/offshore/floating_platform.py +++ b/hopp/offshore/floating_platform.py @@ -57,16 +57,16 @@ class FloatingPlatformDesign(DesignPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", - "topside_design_cost": "USD (optional, default:4.5e6)", - "fabrication_cost_rate": "USD/t (optional, default: 14500.)", - "substructure_steel_rate": "USD/t (optional, default: 3000.)", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", + "topside_design_cost_USD": "USD (optional, default:4.5e6)", + "fabrication_cost_rate_USD": "USD/t (optional, default: 14500.)", + "substructure_steel_rate_USD": "USD/t (optional, default: 3000.)", } } @@ -105,9 +105,9 @@ def run(self): # Create an ouput dict self._outputs['floating_platform'] = { - "mass" : total_mass, - "area" : self.area, - "total_cost" : total_cost + "mass_t" : total_mass, + "area_m^2" : self.area, + "total_cost_USD" : total_cost } # A design object needs to have attribute design_result and detailed_output @@ -116,9 +116,9 @@ def design_result(self): return { "platform_design":{ - "mass" : self._outputs['floating_platform']['mass'], - "area" : self._outputs['floating_platform']['area'], - "total_cost": self._outputs['floating_platform']['total_cost'], + "mass_t" : self._outputs['floating_platform']['mass'], + "area_m^2" : self._outputs['floating_platform']['area'], + "total_cost_USD": self._outputs['floating_platform']['total_cost'], } } @@ -137,13 +137,13 @@ class FloatingPlatformInstallation(InstallPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", "install_duration": "days (optional, default: 14)", }, @@ -173,9 +173,9 @@ def setup_simulation(self, **kwargs): _platform = self.config.get('equipment', {}) design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t + fab_cost_rate = _platform.get('fabrication_cost_rate', 14500.) # USD/t steel_cost = _platform.get('substructure_steel_cost', 3000) # USD/t - #NEED Updated version for floating + install_duration = _platform.get("install_duration", 14) # days # Initialize vessel @@ -190,7 +190,7 @@ def setup_simulation(self, **kwargs): # Add in the mass of the substructure to total mass (may or may not impact the final install cost) _, substructure_mass = calc_substructure_mass_and_cost(self.mass, self.area, - self.depth, fab_cost, design_cost, steel_cost + self.depth, fab_cost_rate, design_cost, steel_cost ) total_mass = self.mass + substructure_mass # t @@ -216,31 +216,39 @@ def detailed_output(self): return {} # Define individual calculations and functions to use outside or with ORBIT -def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_cost=4.5e6, sub_cost=3000, pile_cost=0): +def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., design_cost=4.5e6, sub_cost_rate=3000): + ''' + calc_substructure_mass_and_cost returns the total mass including substructure, topside and equipment. Also returns the cost of the substructure and topside + Inputs: mass | Mass of equipment on platform (tonnes) + area | Area needed for equipment (meter^2) (not necessary) + depth | Ocean depth at platform location (meters) + fab_cost_rate | Cost rate to fabricate topside (USD/tonne) + design_cost | Design cost to design structural components (USD) from ORBIT + sub_cost_rate | Steel cost rate (USD/tonne) from ORBIT''' + ''' Platform is substructure and topside combined - All funstions are based off NREL's ORBIT (oss_design) + All functions are based off NREL's ORBIT (oss_design) default values are specified in ORBIT ''' - #Inputs needed topside_mass = mass - topside_fab_cost_rate = fab_cost + topside_fab_cost_rate = fab_cost_rate topside_design_cost = design_cost '''Topside Cost & Mass Topside Mass is the required Mass the platform will hold Topside Cost is a function of topside mass, fab cost and design cost''' - topside_cost = topside_mass *topside_fab_cost_rate +topside_design_cost + topside_cost = topside_mass*topside_fab_cost_rate + topside_design_cost '''Substructure Substructure Mass is a function of the topside mass Substructure Cost is a function of of substructure mass pile mass and cost rates for each''' - substructure_cost_rate = sub_cost # USD/t + substructure_cost_rate = sub_cost_rate # USD/t - substructure_mass = 0.4 * topside_mass # t - substructure_cost = (substructure_mass *substructure_cost_rate) # USD - substructure_total_mass = substructure_mass # t + substructure_mass = 0.4*topside_mass # t + substructure_cost = (substructure_mass*substructure_cost_rate) # USD + substructure_total_mass = substructure_mass # t '''These arrays were pulled from Rebecca Fuchs SemitTaut_mooring cost models''' depths = np.array([0, 500, 750, 1000, 1250, 1500]) # [m] @@ -258,7 +266,8 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_c '''Total Platform capex = capex Topside + capex substructure''' total_capex = (topside_cost + substructure_cost + mooring_cost) platform_capex = total_capex # USD - platform_mass = substructure_total_mass + topside_mass # t + platform_mass = substructure_total_mass + topside_mass # t + #mass of equipment and floating substructure for substation return platform_capex, platform_mass @@ -268,7 +277,8 @@ def install_platform(mass, area, distance, install_duration=14, vessel=None): A simplified platform installation costing model. Total Cost = install_cost * duration Compares the mass and/or deck space of equipment to the vessel limits to determine - the number of trips. Add an additional "at sea" install duration + the number of trips. Add an additional "at sea" install duration + ''' # print("Install process worked!") # If no ORBIT vessel is defined set default values (based on ORBIT's floating_heavy_lift_vessel) diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py index 1cff7017f..8273adc4f 100644 --- a/tests/hopp/test_offshore/test_offshore_floating.py +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -14,9 +14,9 @@ def test_install_platform(config): ''' Test the code that calculates the platform installation cost ''' - distance = 24 - mass = 2100 - area = 500 + distance = 24 #km + mass = 2100 #tonnes + area = 500 #m^2 cost = install_platform(mass, area, distance, install_duration=14) @@ -26,9 +26,9 @@ def test_calc_substructure_mass_and_cost(config): ''' Test the code that calculates the CapEx from fixed_platform.py ''' - topmass = 200 - toparea = 1000 - depth = 45 + topmass = 200 #tonnes + toparea = 1000 #m^2 + depth = 45 #m cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) From bdf9643e3e72ffbfeb365712a28e95e117204251 Mon Sep 17 00:00:00 2001 From: Kiefer Date: Wed, 7 Jun 2023 10:53:23 -0600 Subject: [PATCH 3/6] Update to fixed and floating platform models, Add test for Floating, Add example yaml file for floating --- hopp/offshore/example_fixed_project.yaml | 12 +- hopp/offshore/example_floating_project.yaml | 14 + hopp/offshore/fixed_platform.py | 70 ++-- hopp/offshore/floating_platform.py | 357 ++++++++++++++++++ tests/hopp/test_offshore/test_offshore.py | 10 +- .../test_offshore/test_offshore_floating.py | 60 +++ 6 files changed, 478 insertions(+), 45 deletions(-) create mode 100644 hopp/offshore/example_floating_project.yaml create mode 100644 hopp/offshore/floating_platform.py create mode 100644 tests/hopp/test_offshore/test_offshore_floating.py diff --git a/hopp/offshore/example_fixed_project.yaml b/hopp/offshore/example_fixed_project.yaml index 24e215259..24cabe15b 100644 --- a/hopp/offshore/example_fixed_project.yaml +++ b/hopp/offshore/example_fixed_project.yaml @@ -1,14 +1,14 @@ # Modified orbit configuration file for a single platform to carry "X technology" design_phases: -- FixedPlatformDesign # Appended Design Phase +- FixedPlatformDesign # Register Design Phase install_phases: - FixedPlatformInstallation: 0 # Appended Install Phase + FixedPlatformInstallation: 0 # Register Install Phase oss_install_vessel: example_heavy_lift_vessel site: - depth: 22.5 # site depth [m] - distance: 124 # distance to port [km] + depth_m: 22.5 # site depth [m] + distance_m: 124 # distance to port [km] equipment: - tech_required_area: 300. # equipment area [m**2] - tech_combined_mass: 1000 # equipment mass [t] + tech_required_area_m^2: 300. # equipment area [m**2] + tech_combined_mass_t: 1000 # equipment mass [t] topside_design_cost: 4500000 # topside design cost [USD] installation_duration: 14 # time at sea [days] \ No newline at end of file diff --git a/hopp/offshore/example_floating_project.yaml b/hopp/offshore/example_floating_project.yaml new file mode 100644 index 000000000..9a27b64b6 --- /dev/null +++ b/hopp/offshore/example_floating_project.yaml @@ -0,0 +1,14 @@ +# Modified orbit configuration file for a single platform to carry "X technology" +design_phases: +- FloatingPlatformDesign # Resgister Design Phase +install_phases: + FloatingPlatformInstallation: 0 # Register Install Phase +oss_install_vessel: example_heavy_lift_vessel +site: + depth_m: 22.5 # site depth [m] + distance_m: 124 # distance to port [km] +equipment: + tech_required_area_m^2: 300. # equipment area [m**2] + tech_combined_mass_t: 1000 # equipment mass [t] + topside_design_cost: 4500000 # topside design cost [USD] + installation_duration: 14 # time at sea [days] \ No newline at end of file diff --git a/hopp/offshore/fixed_platform.py b/hopp/offshore/fixed_platform.py index d5bd6ee60..36b7a18fe 100644 --- a/hopp/offshore/fixed_platform.py +++ b/hopp/offshore/fixed_platform.py @@ -10,12 +10,12 @@ Sources: - [1] ORBIT: https://github.com/WISDEM/ORBIT electrical_refactor branch Args: - - tech_required_area: (float): area needed for combination of all tech (m^2), not including buffer or working space + - tech_required_area_m^2: (float): area needed for combination of all tech (m^2), not including buffer or working space - tech_combined_mass: (float): mass of all tech being placed on the platform (kg or tonnes)year - - depth: (float): bathometry at the platform location (m) - - distance_to_port: (float): distance ships must travel from port to site location (km) + - depth_m: (float): bathometry at the platform location (m) + - distance_m: (float): distance ships must travel from port to site location (km) Future arguments: (Not used at this time) - construction year (int): @@ -53,16 +53,16 @@ class FixedPlatformDesign(DesignPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", "topside_design_cost": "USD (optional, default:4.5e6)", - "fabrication_cost_rate": "USD/t (optional, default: 14500.)", - "substructure_steel_rate": "USD/t (optional, default: 3000.)", + "fabrication_cost_rate_USD/t": "USD/t (optional, default: 14500.)", + "substructure_steel_rate_USD/t": "USD/t (optional, default: 3000.)", } } @@ -82,17 +82,17 @@ def run(self): #print("Fixed Platform Design run() is working!!!") - self.distance = self.config['site']['distance'] # km - self.depth = self.config['site']['depth'] # m + self.distance = self.config['site']['distance_m'] # km + self.depth = self.config['site']['depth_m'] # m _platform = self.config.get('equipment',{}) - self.mass = _platform.get('tech_combined_mass',999) # t - self.area = _platform.get('tech_required_area', 1000) # m**2 + self.mass = _platform.get('tech_comnined_mass_t',999) # t + self.area = _platform.get('tech_required_area_m^2', 1000) # m**2 design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_cost', 3000) # USD/t + fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t # Add individual calcs/functions in the run() method total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area, @@ -101,9 +101,9 @@ def run(self): # Create an ouput dict self._outputs['fixed_platform'] = { - "mass" : total_mass, - "area" : self.area, - "total_cost" : total_cost + "mass_t" : total_mass, + "area_m^2" : self.area, + "total_cost_USD" : total_cost } # A design object needs to have attribute design_result and detailed_output @@ -112,9 +112,9 @@ def design_result(self): return { "platform_design":{ - "mass" : self._outputs['fixed_platform']['mass'], - "area" : self._outputs['fixed_platform']['area'], - "total_cost": self._outputs['fixed_platform']['total_cost'], + "mass_t" : self._outputs['fixed_platform']['mass_t'], + "area_m^2" : self._outputs['fixed_platform']['area_m^2'], + "total_cost_USD": self._outputs['fixed_platform']['total_cost_USD'], } } @@ -133,13 +133,13 @@ class FixedPlatformInstallation(InstallPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance" : "int | float", - "depth" : "int | float", + "distance_m" : "int | float", + "depth_m" : "int | float", }, "equipment": { - "tech_required_area" : "float", - "tech_combined_mass" : "float", + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", "install_duration": "days (optional, default: 14)", }, @@ -162,15 +162,15 @@ def setup_simulation(self, **kwargs): #print("Fixed Platform Install setup_sim() is working!!!") - self.distance = self.config['site']['distance'] - self.depth = self.config['site']['depth'] - self.mass = self.config['equipment']['tech_combined_mass'] - self.area = self.config['equipment']['tech_required_area'] + self.distance = self.config['site']['distance_m'] + self.depth = self.config['site']['depth_m'] + self.mass = self.config['equipment']['tech_combined_mass_t'] + self.area = self.config['equipment']['tech_required_area_m^2'] _platform = self.config.get('equipment', {}) design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_cost', 3000) # USD/t + fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t install_duration = _platform.get("install_duration", 14) # days @@ -313,13 +313,15 @@ def calc_platform_opex(capex, opex_rate=0.011): config_fname = load_config(os.path.join(config_path, os.pardir, "example_fixed_project.yaml")) - ProjectManager._design_phases.append(FixedPlatformDesign) - ProjectManager._install_phases.append(FixedPlatformInstallation) + #ProjectManager._design_phases.append(FixedPlatformDesign) + ProjectManager.register_design_phase(FixedPlatformDesign) + #ProjectManager._install_phases.append(FixedPlatformInstallation) + ProjectManager.register_install_phase(FixedPlatformInstallation) platform = ProjectManager(config_fname) platform.run() - design_capex = platform.design_results['platform_design']['total_cost'] + design_capex = platform.design_results['platform_design']['total_cost_USD'] install_capex = platform.installation_capex #print("Project Params", h2platform.project_params.items()) diff --git a/hopp/offshore/floating_platform.py b/hopp/offshore/floating_platform.py new file mode 100644 index 000000000..f06e66af7 --- /dev/null +++ b/hopp/offshore/floating_platform.py @@ -0,0 +1,357 @@ +""" +Author:Charles Kiefer +Date: 4/11/2023 +Institution: National Renewable Energy Lab +Description: This file shall handle costing and sizing of offshore floating platforms deicated to hydrogen production. It uses the + same foundation as fixed_platform.py. Both have been modeled off of existing BOS cost/sizing calculations fond in ORBIT. + It can be run as standalone functions or as appended ORBIT project phases. + + + +Sources: + - [1] ORBIT: https://github.com/WISDEM/ORBIT electrical_refactor branch & SemiTaut_mooring branch +Args: + - tech_required_area: (float): area needed for combination of all tech (m^2), not including buffer or working space + - tech_combined_mass: (float): mass of all tech being placed on the platform (kg or tonnes)year + + + - depth: (float): bathometry at the platform location (m) + - distance_to_port: (float): distance ships must travel from port to site location (km) + + Future arguments: (Not used at this time) + - construction year (int): + - lifetime (int): lifetime of the plant in years (may not be needed) + +Returns: + - platform_mass (float): Adjusted mass of platform + substructure + - design_capex (float): capital expenditures (platform design + substructure fabrication) + - installation_capex (float): capital expenditures (installation cost) + - platform_opex (float): the OPEX (annual, fixed) in USD for the platform + +""" +''' +Notes: + Thank you Jake Nunemaker's oswh2 repository and Rebecca Fuchs SemiTaut_mooring repository!!! + pile_cost=0 $US/tonne for monopile construction. Not a bug, this # is consistent with the rest of ORBIT +''' + +import os +import math +# +from ORBIT import ProjectManager, load_config +from ORBIT.core import Vessel +from ORBIT.core.library import initialize_library +from ORBIT.phases.design import DesignPhase +from ORBIT.phases.install import InstallPhase +from scipy.interpolate import interp1d +import numpy as np + + +class FloatingPlatformDesign(DesignPhase): + ''' + This is a modified class based on ORBIT's design phase + ''' + + #phase = "H2 Floating Platform Design" + + # Expected inputs from config yaml file + expected_config = { + "site": { + "distance_m" : "int | float", + "depth_m" : "int | float", + }, + + "equipment": { + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", + "topside_design_cost_USD": "USD (optional, default:4.5e6)", + "fabrication_cost_rate_USD/t": "USD/t (optional, default: 14500.)", + "substructure_steel_rate_USD/t": "USD/t (optional, default: 3000.)", + } + + } + + # Takes in arguments and initialize library files + def __init__(self, config, **kwargs): + + self.phase = "H2 Floating Platform Design" + + config = self.initialize_library(config, **kwargs) + self.config = self.validate_config(config) + + self._outputs = {} + # Runs the design cost models + + def run(self): + + #print("Floating Platform Design run() is working!!!") + + self.distance = self.config['site']['distance_m'] # km + self.depth = self.config['site']['depth_m'] # m + + _platform = self.config.get('equipment',{}) + + self.mass = _platform.get('tech_combined_mass_t',999) # t + self.area = _platform.get('tech_required_area_m^2', 1000) # m**2 + + design_cost = _platform.get('topside_design_cost', 4.5e6) # USD + fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + ##NEED updated version + # Add individual calcs/functions in the run() method + total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area, + self.depth, fab_cost, design_cost, steel_cost + ) + + # Create an ouput dict + self._outputs['floating_platform'] = { + "mass_t" : total_mass, + "area_m^2" : self.area, + "total_cost_USD" : total_cost + } + + # A design object needs to have attribute design_result and detailed_output + @property + def design_result(self): + + return { + "platform_design":{ + "mass_t" : self._outputs['floating_platform']['mass_t'], + "area_m^2" : self._outputs['floating_platform']['area_m^2'], + "total_cost_USD": self._outputs['floating_platform']['total_cost_USD'], + } + } + + @property + def detailed_output(self): + + return {} + +class FloatingPlatformInstallation(InstallPhase): + ''' + This is a modified class based on ORBIT's install phase + ''' + + #phase = "H2 Floating Platform Installation" + + # Expected inputs from config yaml file + expected_config = { + "site": { + "distance_m" : "int | float", + "depth_m" : "int | float", + }, + + "equipment": { + "tech_required_area_m^2" : "float", + "tech_combined_mass_t" : "float", + "install_duration": "days (optional, default: 14)", + }, + + "oss_install_vessel" : "str | dict", + } + + # Need to initialize arguments and weather files + def __init__(self, config, weather=None, **kwargs): + + super().__init__(weather, **kwargs) + + config = self.initialize_library(config, **kwargs) + self.config = self.validate_config(config) + + self.initialize_port() + self.setup_simulation(**kwargs) + + # Setup simulation seems to be the install phase's equivalent run() module + def setup_simulation(self, **kwargs): + + #print("Floating Platform Install setup_sim() is working!!!") + + self.distance = self.config['site']['distance_m'] + self.depth = self.config['site']['depth_m'] + self.mass = self.config['equipment']['tech_combined_mass_t'] + self.area = self.config['equipment']['tech_required_area_m^2'] + + _platform = self.config.get('equipment', {}) + design_cost = _platform.get('topside_design_cost', 4.5e6) # USD + fab_cost_rate = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + + install_duration = _platform.get("install_duration", 14) # days + + # Initialize vessel + vessel_specs = self.config.get("oss_install_vessel", None) + name = vessel_specs.get("name","Offshore Substation Install Vessel") + + vessel = Vessel(name, vessel_specs) + self.env.register(vessel) + + vessel.initialize() + self.install_vessel = vessel + + # Add in the mass of the substructure to total mass (may or may not impact the final install cost) + _, substructure_mass = calc_substructure_mass_and_cost(self.mass, self.area, + self.depth, fab_cost_rate, design_cost, steel_cost + ) + + total_mass = self.mass + substructure_mass # t + + # Call the install_platform function + self.install_capex = install_platform(total_mass, self.area, self.distance, \ + install_duration, self.install_vessel) + + # An install object needs to have attribute system_capex, installation_capex, and detailed output + @property + def system_capex(self): + + return {} + + @property + def installation_capex(self): + + return self.install_capex + + @property + def detailed_output(self): + + return {} + +# Define individual calculations and functions to use outside or with ORBIT +def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., design_cost=4.5e6, sub_cost_rate=3000): + ''' + calc_substructure_mass_and_cost returns the total mass including substructure, topside and equipment. Also returns the cost of the substructure and topside + Inputs: mass | Mass of equipment on platform (tonnes) + area | Area needed for equipment (meter^2) (not necessary) + depth | Ocean depth at platform location (meters) + fab_cost_rate | Cost rate to fabricate topside (USD/tonne) + design_cost | Design cost to design structural components (USD) from ORBIT + sub_cost_rate | Steel cost rate (USD/tonne) from ORBIT''' + + ''' + Platform is substructure and topside combined + All functions are based off NREL's ORBIT (oss_design) + default values are specified in ORBIT + ''' + topside_mass = mass + topside_fab_cost_rate = fab_cost_rate + topside_design_cost = design_cost + + '''Topside Cost & Mass + Topside Mass is the required Mass the platform will hold + Topside Cost is a function of topside mass, fab cost and design cost''' + topside_cost = topside_mass*topside_fab_cost_rate + topside_design_cost + + '''Substructure + Substructure Mass is a function of the topside mass + Substructure Cost is a function of of substructure mass pile mass and cost rates for each''' + + substructure_cost_rate = sub_cost_rate # USD/t + + substructure_mass = 0.4*topside_mass # t + substructure_cost = (substructure_mass*substructure_cost_rate) # USD + substructure_total_mass = substructure_mass # t + + '''These arrays were pulled from Rebecca Fuchs SemitTaut_mooring cost models''' + depths = np.array([0, 500, 750, 1000, 1250, 1500]) # [m] + + anchor_costs = np.array([28823, 112766, 125511, 148703, 204988, 246655]) # [USD] + finterp_anchor_cost = interp1d(depths, anchor_costs) + anchor_cost = finterp_anchor_cost(depth) + + total_line_costs = np.array([430315, 826598, 1221471, 1682208, 2380035, 3229700]) # [USD] + finterp_total_line_cost = interp1d(depths, total_line_costs) + line_cost = finterp_total_line_cost(depth) + + mooring_cost = line_cost + anchor_cost + + '''Total Platform capex = capex Topside + capex substructure''' + total_capex = (topside_cost + substructure_cost + mooring_cost) + platform_capex = total_capex # USD + platform_mass = substructure_total_mass + topside_mass # t + #mass of equipment and floating substructure for substation + + return platform_capex, platform_mass + +#@process +def install_platform(mass, area, distance, install_duration=14, vessel=None): + ''' + A simplified platform installation costing model. + Total Cost = install_cost * duration + Compares the mass and/or deck space of equipment to the vessel limits to determine + the number of trips. Add an additional "at sea" install duration + + ''' + # print("Install process worked!") + # If no ORBIT vessel is defined set default values (based on ORBIT's floating_heavy_lift_vessel) + if vessel == None: + vessel_cargo_mass = 7999 # t + vessel_deck_space = 3999 # m**2 + vessel_day_rate = 500001 # USD/day + vessel_speed = 7 # km/hr + else: + vessel_cargo_mass = vessel.storage.max_cargo_mass # t + vessel_deck_space = vessel.storage.max_deck_space # m**2 + vessel_day_rate = vessel.day_rate # USD/day + vessel_speed = vessel.transit_speed # km/hr + + #print("Max Vessel Cargo and Mass:", vessel_cargo_mass, vessel_deck_space) + + # Get the # of trips based on ships cargo/space limits + num_of_trips = math.ceil(max((mass / vessel_cargo_mass), (area / vessel_deck_space))) + #print("Number of trips: ", num_of_trips) + + # Total duration = double the trips + install_duration + duration = (2 * num_of_trips * distance) / (vessel_speed * 24) + install_duration # days + #print("Duration (days): %0.2f" % duration) + + # Final install cost is obtained by using the vessel's daily rate + install_cost = vessel_day_rate * duration # USD + + return install_cost + +def calc_platform_opex(capex, opex_rate=0.011): + ''' + Simple opex calculation based on a capex + https://www.acm.nl/sites/default/files/documents/study-on-estimation-method-for-additional-efficient-offshore-grid-opex.pdf + + Output in $USD/year + ''' + + opex = capex * opex_rate # USD/year + + #print("OpEx of platform:", opex) + + return opex + + +# Standalone test sections +if __name__ == '__main__': + print("\n*** New FloatingPlatform Standalone test section ***\n") + + orbit_libpath = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, os.pardir, 'ORBIT', 'library')) + print(orbit_libpath) + initialize_library(orbit_libpath) + + config_path = os.path.abspath(__file__) + config_fname = load_config(os.path.join(config_path, os.pardir, "example_floating_project.yaml")) + + + #ProjectManager._design_phases.append(FloatingPlatformDesign) + ProjectManager.register_design_phase(FloatingPlatformDesign) + #ProjectManager._install_phases.append(FloatingPlatformInstallation) + ProjectManager.register_install_phase(FloatingPlatformInstallation) + + platform = ProjectManager(config_fname) + platform.run() + + design_capex = platform.design_results['platform_design']['total_cost_USD'] + install_capex = platform.installation_capex + + #print("Project Params", h2platform.project_params.items()) + platform_opex = calc_platform_opex((design_capex + install_capex)) + + print("ORBIT Phases: ", platform.phases.keys()) + print(f"\tH2 Platform Design Capex: {design_capex:.0f} USD") + print(f"\tH2 Platform Install Capex: {install_capex:.0f} USD") + print('') + print(f"\tTotal H2 Platform Capex: {(design_capex+install_capex)/1e6:.0f} mUSD") + print(f"\tH2 Platform Opex: {platform_opex:.0f} USD/year") diff --git a/tests/hopp/test_offshore/test_offshore.py b/tests/hopp/test_offshore/test_offshore.py index 3ad38f135..6832b4236 100644 --- a/tests/hopp/test_offshore/test_offshore.py +++ b/tests/hopp/test_offshore/test_offshore.py @@ -18,7 +18,7 @@ def test_install_platform(config): distance = 24 mass = 2100 area = 500 - + cost = install_platform(mass, area, distance, install_duration=14) assert pytest.approx(cost) == 7200014 @@ -30,7 +30,7 @@ def test_calc_substructure_mass_and_cost(config): topmass = 200 toparea = 1000 depth = 45 - + cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) assert pytest.approx(cost) == 7640000 @@ -38,10 +38,10 @@ def test_calc_substructure_mass_and_cost(config): def test_calc_platform_opex(): ''' - Test the code that calculates the OpEx from fixed_platform_h2.py + Test the code that calculates the OpEx from fixed_platform.py ''' capex = 28e6 opex_rate = 0.01 cost = calc_platform_opex(capex, opex_rate) - - assert cost == 28e4 \ No newline at end of file + + assert cost == 28e4 diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py new file mode 100644 index 000000000..a6e675969 --- /dev/null +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -0,0 +1,60 @@ +from turtle import distance +import pytest +import os +from pathlib import Path + +from ORBIT import load_config +from hopp.offshore.floating_platform import install_platform, calc_platform_opex, calc_substructure_mass_and_cost, DesignPhase, InstallPhase + +from ORBIT import ProjectManager, load_config +from ORBIT.core import Vessel +from ORBIT.core.library import initialize_library +from ORBIT.phases.design import DesignPhase +from ORBIT.phases.install import InstallPhase +#from hopp.offshore.floating_platform import FloatingPlatformDesign + +@pytest.fixture +def config(): + offshore_path = Path(__file__).parents[3] / "hopp" / "offshore" + + return load_config(os.path.join(offshore_path, "example_floating_project.yaml")) + +def test_install_platform(config): + ''' + Test the code that calculates the platform installation cost + ''' + distance = 24 + mass = 2100 + area = 500 + + cost = install_platform(mass, area, distance, install_duration=14) + + assert pytest.approx(cost) == 7142871 + +def test_calc_substructure_mass_and_cost(config): + ''' + Test the code that calculates the CapEx from floating_platform.py + ''' + topmass = 200 + toparea = 1000 + depth = 45 + + cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) + + assert pytest.approx(cost) == 8142358.34 + assert pytest.approx(mass, 1.) == 280 + + +def test_calc_platform_opex(): + ''' + Test the code that calculates the OpEx from floating_platform.py + ''' + capex = 28e6 + opex_rate = 0.01 + cost = calc_platform_opex(capex, opex_rate) + + assert cost == 28e4 + + +#def test_FloatingPlatform_Design(): + # DesignPhase = FloatingPlatformDesign(DesignPhase) From f26ee0d3b3bf58719e7c2f26f73aa55fe3dbf9ca Mon Sep 17 00:00:00 2001 From: Kiefer Date: Thu, 29 Jun 2023 16:11:59 -0600 Subject: [PATCH 4/6] Call to SemiTaut Branch in Orbit to pull mooring costs, Update to the variable names to match Orbit Nomenclature in yamls in .py --- hopp/offshore/example_fixed_project.yaml | 8 +- hopp/offshore/example_floating_project.yaml | 10 +- hopp/offshore/fixed_platform.py | 79 +++++++------ hopp/offshore/floating_platform.py | 109 ++++++++++-------- tests/hopp/test_offshore/test_offshore.py | 20 +++- .../test_offshore/test_offshore_floating.py | 33 ++++-- 6 files changed, 144 insertions(+), 115 deletions(-) diff --git a/hopp/offshore/example_fixed_project.yaml b/hopp/offshore/example_fixed_project.yaml index 24cabe15b..8bafc6a03 100644 --- a/hopp/offshore/example_fixed_project.yaml +++ b/hopp/offshore/example_fixed_project.yaml @@ -5,10 +5,10 @@ install_phases: FixedPlatformInstallation: 0 # Register Install Phase oss_install_vessel: example_heavy_lift_vessel site: - depth_m: 22.5 # site depth [m] - distance_m: 124 # distance to port [km] + depth: 22.5 # site depth [m] + distance: 124 # distance to port [km] equipment: - tech_required_area_m^2: 300. # equipment area [m**2] - tech_combined_mass_t: 1000 # equipment mass [t] + tech_required_area: 300. # equipment area [m**2] + tech_combined_mass: 1000 # equipment mass [t] topside_design_cost: 4500000 # topside design cost [USD] installation_duration: 14 # time at sea [days] \ No newline at end of file diff --git a/hopp/offshore/example_floating_project.yaml b/hopp/offshore/example_floating_project.yaml index 9a27b64b6..7944b12d5 100644 --- a/hopp/offshore/example_floating_project.yaml +++ b/hopp/offshore/example_floating_project.yaml @@ -5,10 +5,10 @@ install_phases: FloatingPlatformInstallation: 0 # Register Install Phase oss_install_vessel: example_heavy_lift_vessel site: - depth_m: 22.5 # site depth [m] - distance_m: 124 # distance to port [km] + depth: 500.5 # site depth [m] + distance: 124 # distance to port [km] equipment: - tech_required_area_m^2: 300. # equipment area [m**2] - tech_combined_mass_t: 1000 # equipment mass [t] + tech_required_area: 300. # equipment area [m**2] + tech_combined_mass: 1000 # equipment mass [t] topside_design_cost: 4500000 # topside design cost [USD] - installation_duration: 14 # time at sea [days] \ No newline at end of file + installation_duration: 14 # time at sea [days] diff --git a/hopp/offshore/fixed_platform.py b/hopp/offshore/fixed_platform.py index 90793c3d5..f61f20a32 100644 --- a/hopp/offshore/fixed_platform.py +++ b/hopp/offshore/fixed_platform.py @@ -10,12 +10,12 @@ Sources: - [1] ORBIT: https://github.com/WISDEM/ORBIT electrical_refactor branch Args: - - tech_required_area_m^2: (float): area needed for combination of all tech (m^2), not including buffer or working space + - tech_required_area: (float): area needed for combination of all tech (m^2), not including buffer or working space - tech_combined_mass: (float): mass of all tech being placed on the platform (kg or tonnes)year - - depth_m: (float): bathometry at the platform location (m) - - distance_m: (float): distance ships must travel from port to site location (km) + - depth: (float): bathometry at the platform location (m) + - distance: (float): distance ships must travel from port to site location (km) Future arguments: (Not used at this time) - construction year (int): @@ -53,16 +53,16 @@ class FixedPlatformDesign(DesignPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance_m" : "int | float", - "depth_m" : "int | float", + "distance" : "int | float", + "depth" : "int | float", }, "equipment": { - "tech_required_area_m^2" : "float", - "tech_combined_mass_t" : "float", + "tech_required_area" : "float", + "tech_combined_mass" : "float", "topside_design_cost": "USD (optional, default:4.5e6)", - "fabrication_cost_rate_USD/t": "USD/t (optional, default: 14500.)", - "substructure_steel_rate_USD/t": "USD/t (optional, default: 3000.)", + "fabrication_cost_rate": "USD/t (optional, default: 14500.)", + "substructure_steel_rate": "USD/t (optional, default: 3000.)", } } @@ -82,17 +82,17 @@ def run(self): #print("Fixed Platform Design run() is working!!!") - self.distance = self.config['site']['distance_m'] # km - self.depth = self.config['site']['depth_m'] # m + self.distance = self.config['site']['distance'] # km + self.depth = self.config['site']['depth'] # m _platform = self.config.get('equipment',{}) - self.mass = _platform.get('tech_comnined_mass_t',999) # t - self.area = _platform.get('tech_required_area_m^2', 1000) # m**2 + self.mass = _platform.get('tech_comnined_mass',999) # t + self.area = _platform.get('tech_required_area', 1000) # m**2 design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate', 3000) # USD/t # Add individual calcs/functions in the run() method total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area, @@ -101,9 +101,9 @@ def run(self): # Create an ouput dict self._outputs['fixed_platform'] = { - "mass_t" : total_mass, - "area_m^2" : self.area, - "total_cost_USD" : total_cost + "mass" : total_mass, + "area" : self.area, + "total_cost" : total_cost } # A design object needs to have attribute design_result and detailed_output @@ -112,9 +112,9 @@ def design_result(self): return { "platform_design":{ - "mass_t" : self._outputs['fixed_platform']['mass_t'], - "area_m^2" : self._outputs['fixed_platform']['area_m^2'], - "total_cost_USD": self._outputs['fixed_platform']['total_cost_USD'], + "mass" : self._outputs['fixed_platform']['mass'], + "area" : self._outputs['fixed_platform']['area'], + "total_cost": self._outputs['fixed_platform']['total_cost'], } } @@ -133,13 +133,13 @@ class FixedPlatformInstallation(InstallPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance_m" : "int | float", - "depth_m" : "int | float", + "distance" : "int | float", + "depth" : "int | float", }, "equipment": { - "tech_required_area_m^2" : "float", - "tech_combined_mass_t" : "float", + "tech_required_area" : "float", + "tech_combined_mass" : "float", "install_duration": "days (optional, default: 14)", }, @@ -162,15 +162,15 @@ def setup_simulation(self, **kwargs): #print("Fixed Platform Install setup_sim() is working!!!") - self.distance = self.config['site']['distance_m'] - self.depth = self.config['site']['depth_m'] - self.mass = self.config['equipment']['tech_combined_mass_t'] - self.area = self.config['equipment']['tech_required_area_m^2'] + self.distance = self.config['site']['distance'] + self.depth = self.config['site']['depth'] + self.mass = self.config['equipment']['tech_combined_mass'] + self.area = self.config['equipment']['tech_required_area'] _platform = self.config.get('equipment', {}) design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + fab_cost = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate', 3000) # USD/t install_duration = _platform.get("install_duration", 14) # days @@ -189,8 +189,7 @@ def setup_simulation(self, **kwargs): self.depth, fab_cost, design_cost, steel_cost ) - total_mass = self.mass + substructure_mass # t - + total_mass = substructure_mass # t # Call the install_platform function self.install_capex = install_platform(total_mass, self.area, self.distance, \ install_duration, self.install_vessel) @@ -234,7 +233,7 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_c '''Topside Cost & Mass Topside Mass is the required Mass the platform will hold Topside Cost is a function of topside mass, fab cost and design cost''' - topside_cost = topside_mass *topside_fab_cost_rate +topside_design_cost + topside_cost = topside_mass*topside_fab_cost_rate + topside_design_cost '''Substructure Substructure Mass is a function of the topside mass @@ -244,12 +243,12 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost=14500., design_c substructure_cost_rate = sub_cost # USD/t pile_cost_rate = pile_cost # USD/t - substructure_mass = 0.4 * topside_mass # t - substructure_pile_mass = 8 * substructure_mass**0.5574 # t - substructure_cost = (substructure_mass *substructure_cost_rate + - substructure_pile_mass *pile_cost_rate) # USD + substructure_mass = 0.4*topside_mass # t + substructure_pile_mass = 8*substructure_mass**0.5574 # t + substructure_cost = (substructure_mass*substructure_cost_rate + + substructure_pile_mass*pile_cost_rate) # USD - substructure_total_mass = substructure_mass +substructure_pile_mass # t + substructure_total_mass = substructure_mass + substructure_pile_mass # t '''Total Platform capex = capex Topside + capex substructure''' @@ -329,7 +328,7 @@ def calc_platform_opex(capex, opex_rate=0.011): platform = ProjectManager(config_fname) platform.run() - design_capex = platform.design_results['platform_design']['total_cost_USD'] + design_capex = platform.design_results['platform_design']['total_cost'] install_capex = platform.installation_capex #print("Project Params", h2platform.project_params.items()) diff --git a/hopp/offshore/floating_platform.py b/hopp/offshore/floating_platform.py index f06e66af7..b7beaaf84 100644 --- a/hopp/offshore/floating_platform.py +++ b/hopp/offshore/floating_platform.py @@ -43,6 +43,8 @@ from ORBIT.core.library import initialize_library from ORBIT.phases.design import DesignPhase from ORBIT.phases.install import InstallPhase +from ORBIT.phases.design import SemiTaut_mooring_system_design + from scipy.interpolate import interp1d import numpy as np @@ -57,16 +59,16 @@ class FloatingPlatformDesign(DesignPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance_m" : "int | float", - "depth_m" : "int | float", + "distance" : "int | float", + "depth" : "int | float", }, "equipment": { - "tech_required_area_m^2" : "float", - "tech_combined_mass_t" : "float", - "topside_design_cost_USD": "USD (optional, default:4.5e6)", - "fabrication_cost_rate_USD/t": "USD/t (optional, default: 14500.)", - "substructure_steel_rate_USD/t": "USD/t (optional, default: 3000.)", + "tech_required_area" : "float", + "tech_combined_mass" : "float", + "topside_design_cost": "USD (optional, default:4.5e6)", + "fabrication_cost_rate": "USD/t (optional, default: 14500.)", + "substructure_steel_rate": "USD/t (optional, default: 3000.)", } } @@ -86,28 +88,34 @@ def run(self): #print("Floating Platform Design run() is working!!!") - self.distance = self.config['site']['distance_m'] # km - self.depth = self.config['site']['depth_m'] # m + self.distance = self.config['site']['distance'] # km + self.depth = self.config['site']['depth'] # m _platform = self.config.get('equipment',{}) - self.mass = _platform.get('tech_combined_mass_t',999) # t - self.area = _platform.get('tech_required_area_m^2', 1000) # m**2 + self.mass = _platform.get('tech_combined_mass',999) # t + self.area = _platform.get('tech_required_area', 1000) # m**2 design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + fab_cost_rate = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate', 3000) # USD/t ##NEED updated version # Add individual calcs/functions in the run() method + '''Calls in SemiTaut Costs and Variables for Substructure mass and cost''' + self.anchor_type = "Drag Embedment" + self.num_lines = 4 + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.calculate_line_length_mass(self) + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.calculate_anchor_mass_cost(self) + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.determine_mooring_line_cost(self) total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area, - self.depth, fab_cost, design_cost, steel_cost - ) + self.depth, fab_cost_rate, design_cost, steel_cost, + self.line_cost, self.anchor_cost, self.anchor_mass, self.line_mass, self.num_lines) # Create an ouput dict self._outputs['floating_platform'] = { - "mass_t" : total_mass, - "area_m^2" : self.area, - "total_cost_USD" : total_cost + "mass" : total_mass, + "area" : self.area, + "total_cost" : total_cost } # A design object needs to have attribute design_result and detailed_output @@ -116,9 +124,9 @@ def design_result(self): return { "platform_design":{ - "mass_t" : self._outputs['floating_platform']['mass_t'], - "area_m^2" : self._outputs['floating_platform']['area_m^2'], - "total_cost_USD": self._outputs['floating_platform']['total_cost_USD'], + "mass" : self._outputs['floating_platform']['mass'], + "area" : self._outputs['floating_platform']['area'], + "total_cost": self._outputs['floating_platform']['total_cost'], } } @@ -137,13 +145,13 @@ class FloatingPlatformInstallation(InstallPhase): # Expected inputs from config yaml file expected_config = { "site": { - "distance_m" : "int | float", - "depth_m" : "int | float", + "distance" : "int | float", + "depth" : "int | float", }, "equipment": { - "tech_required_area_m^2" : "float", - "tech_combined_mass_t" : "float", + "tech_required_area" : "float", + "tech_combined_mass" : "float", "install_duration": "days (optional, default: 14)", }, @@ -166,15 +174,15 @@ def setup_simulation(self, **kwargs): #print("Floating Platform Install setup_sim() is working!!!") - self.distance = self.config['site']['distance_m'] - self.depth = self.config['site']['depth_m'] - self.mass = self.config['equipment']['tech_combined_mass_t'] - self.area = self.config['equipment']['tech_required_area_m^2'] + self.distance = self.config['site']['distance'] + self.depth = self.config['site']['depth'] + self.mass = self.config['equipment']['tech_combined_mass'] + self.area = self.config['equipment']['tech_required_area'] _platform = self.config.get('equipment', {}) design_cost = _platform.get('topside_design_cost', 4.5e6) # USD - fab_cost_rate = _platform.get('fabrication_cost_rate_USD/t', 14500.) # USD/t - steel_cost = _platform.get('substructure_steel_rate_USD/t', 3000) # USD/t + fab_cost_rate = _platform.get('fabrication_cost_rate', 14500.) # USD/t + steel_cost = _platform.get('substructure_steel_rate', 3000) # USD/t install_duration = _platform.get("install_duration", 14) # days @@ -189,11 +197,19 @@ def setup_simulation(self, **kwargs): self.install_vessel = vessel # Add in the mass of the substructure to total mass (may or may not impact the final install cost) + + '''Calls in SemiTaut Costs and Variables''' + self.anchor_type = "Drag Embedment" + self.num_lines = 4 + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.calculate_line_length_mass(self) + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.calculate_anchor_mass_cost(self) + SemiTaut_mooring_system_design.SemiTautMooringSystemDesign.determine_mooring_line_cost(self) + _, substructure_mass = calc_substructure_mass_and_cost(self.mass, self.area, - self.depth, fab_cost_rate, design_cost, steel_cost - ) + self.depth, fab_cost_rate, design_cost, steel_cost, + self.line_cost, self.anchor_cost, self.anchor_mass, self.line_mass, self.num_lines) - total_mass = self.mass + substructure_mass # t + total_mass = substructure_mass # t # Call the install_platform function self.install_capex = install_platform(total_mass, self.area, self.distance, \ @@ -216,7 +232,7 @@ def detailed_output(self): return {} # Define individual calculations and functions to use outside or with ORBIT -def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., design_cost=4.5e6, sub_cost_rate=3000): +def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., design_cost=4.5e6, sub_cost_rate=3000, line_cost=0, anchor_cost=0, anchor_mass=0, line_mass=0,num_lines=4): ''' calc_substructure_mass_and_cost returns the total mass including substructure, topside and equipment. Also returns the cost of the substructure and topside Inputs: mass | Mass of equipment on platform (tonnes) @@ -238,7 +254,7 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., des '''Topside Cost & Mass Topside Mass is the required Mass the platform will hold Topside Cost is a function of topside mass, fab cost and design cost''' - topside_cost = topside_mass*topside_fab_cost_rate + topside_design_cost + topside_cost = topside_mass*topside_fab_cost_rate + topside_design_cost #USD '''Substructure Substructure Mass is a function of the topside mass @@ -250,23 +266,16 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., des substructure_cost = (substructure_mass*substructure_cost_rate) # USD substructure_total_mass = substructure_mass # t - '''These arrays were pulled from Rebecca Fuchs SemitTaut_mooring cost models''' - depths = np.array([0, 500, 750, 1000, 1250, 1500]) # [m] - - anchor_costs = np.array([28823, 112766, 125511, 148703, 204988, 246655]) # [USD] - finterp_anchor_cost = interp1d(depths, anchor_costs) - anchor_cost = finterp_anchor_cost(depth) - - total_line_costs = np.array([430315, 826598, 1221471, 1682208, 2380035, 3229700]) # [USD] - finterp_total_line_cost = interp1d(depths, total_line_costs) - line_cost = finterp_total_line_cost(depth) - - mooring_cost = line_cost + anchor_cost + '''Total Mooring cost and mass for the substructure + Line_cost, anchor_cost, line_mass, anchor_mass are grabbed from SemiTaut_mooring_system_design in ORBIT's SemiTaut branch + Mooring_mass is returned in kilograms and will need to ''' + mooring_cost = (line_cost + anchor_cost)*num_lines #USD + mooring_mass = (line_mass + anchor_mass)*num_lines #kg '''Total Platform capex = capex Topside + capex substructure''' total_capex = (topside_cost + substructure_cost + mooring_cost) platform_capex = total_capex # USD - platform_mass = substructure_total_mass + topside_mass # t + platform_mass = substructure_total_mass + topside_mass + mooring_mass/1000 # t #mass of equipment and floating substructure for substation return platform_capex, platform_mass @@ -343,7 +352,7 @@ def calc_platform_opex(capex, opex_rate=0.011): platform = ProjectManager(config_fname) platform.run() - design_capex = platform.design_results['platform_design']['total_cost_USD'] + design_capex = platform.design_results['platform_design']['total_cost'] install_capex = platform.installation_capex #print("Project Params", h2platform.project_params.items()) diff --git a/tests/hopp/test_offshore/test_offshore.py b/tests/hopp/test_offshore/test_offshore.py index 6832b4236..e90322ca5 100644 --- a/tests/hopp/test_offshore/test_offshore.py +++ b/tests/hopp/test_offshore/test_offshore.py @@ -5,6 +5,7 @@ from ORBIT import load_config from hopp.offshore.fixed_platform import install_platform, calc_platform_opex, calc_substructure_mass_and_cost +'''https://www.nrel.gov/docs/fy17osti/66874.pdf''' @pytest.fixture def config(): offshore_path = Path(__file__).parents[3] / "hopp" / "offshore" @@ -23,7 +24,7 @@ def test_install_platform(config): assert pytest.approx(cost) == 7200014 -def test_calc_substructure_mass_and_cost(config): +def test_calc_substructure_cost(config): ''' Test the code that calculates the CapEx from fixed_platform.py ''' @@ -31,10 +32,21 @@ def test_calc_substructure_mass_and_cost(config): toparea = 1000 depth = 45 - cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) + cost, _ = calc_substructure_mass_and_cost(topmass, toparea, depth) assert pytest.approx(cost) == 7640000 - assert pytest.approx(mass, 1.) == 372.02 + +def test_calc_substructure_mass(config): + ''' + Test the code that calculates the CapEx from fixed_platform.py + ''' + topmass = 200 + toparea = 1000 + depth = 45 + + _,mass = calc_substructure_mass_and_cost(topmass, toparea, depth) + + assert pytest.approx(mass,.1) == 372.02 def test_calc_platform_opex(): ''' @@ -44,4 +56,4 @@ def test_calc_platform_opex(): opex_rate = 0.01 cost = calc_platform_opex(capex, opex_rate) - assert cost == 28e4 + assert pytest.approx(cost) == 28e4 diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py index 6126dba21..8c7633913 100644 --- a/tests/hopp/test_offshore/test_offshore_floating.py +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -11,7 +11,7 @@ from ORBIT.phases.design import DesignPhase from ORBIT.phases.install import InstallPhase #from hopp.offshore.floating_platform import FloatingPlatformDesign - +'''Values for these asserts were used using the equations from https://www.nrel.gov/docs/fy17osti/66874.pdf''' @pytest.fixture def config(): offshore_path = Path(__file__).parents[3] / "hopp" / "offshore" @@ -29,20 +29,33 @@ def test_install_platform(config): cost = install_platform(mass, area, distance, install_duration=14) assert pytest.approx(cost) == 7142871 - -def test_calc_substructure_mass_and_cost(config): + +def test_calc_substructure_cost(config): ''' Test the code that calculates the CapEx from floating_platform.py + ''' topmass = 200 toparea = 1000 - depth = 45 + depth = 500 + + cost,_ = calc_substructure_mass_and_cost(topmass, toparea, depth,fab_cost_rate=14500, design_cost=4500000, sub_cost_rate=3000, line_cost=850000, anchor_cost=120000, anchor_mass=20, line_mass=100000, num_lines=4) + + assert pytest.approx(cost) == 11520000 - cost, mass = calc_substructure_mass_and_cost(topmass, toparea, depth) - assert pytest.approx(cost) == 8142358.34 - assert pytest.approx(mass, 1.) == 280 +def test_calc_substructure_mass(config): + ''' + Test the code that calculates the CapEx from floating_platform.py + + ''' + topmass = 200 + toparea = 1000 + depth = 500 + _, mass = calc_substructure_mass_and_cost(topmass, toparea, depth,fab_cost_rate=14500, design_cost=4500000, sub_cost_rate=3000, line_cost=850000, anchor_cost=120000, anchor_mass=20, line_mass=100000, num_lines=4) + + assert pytest.approx(mass,.1) == 680. def test_calc_platform_opex(): ''' @@ -52,8 +65,4 @@ def test_calc_platform_opex(): opex_rate = 0.01 cost = calc_platform_opex(capex, opex_rate) - assert cost == 28e4 - - -#def test_FloatingPlatform_Design(): - # DesignPhase = FloatingPlatformDesign(DesignPhase) + assert pytest.approx(cost) == 28e4 \ No newline at end of file From fbd0797826f305f5ec48c00051804800800f255f Mon Sep 17 00:00:00 2001 From: Kiefer Date: Fri, 7 Jul 2023 09:51:42 -0600 Subject: [PATCH 5/6] Add sources to test files, Update floating cost to match ORBIT Technical report and update to test file --- hopp/offshore/floating_platform.py | 2 +- tests/hopp/test_offshore/test_offshore.py | 8 +++++++- .../hopp/test_offshore/test_offshore_floating.py | 15 ++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hopp/offshore/floating_platform.py b/hopp/offshore/floating_platform.py index b7beaaf84..e4a6af385 100644 --- a/hopp/offshore/floating_platform.py +++ b/hopp/offshore/floating_platform.py @@ -273,7 +273,7 @@ def calc_substructure_mass_and_cost(mass, area, depth, fab_cost_rate=14500., des mooring_mass = (line_mass + anchor_mass)*num_lines #kg '''Total Platform capex = capex Topside + capex substructure''' - total_capex = (topside_cost + substructure_cost + mooring_cost) + total_capex = 2*(topside_cost + substructure_cost + mooring_cost) platform_capex = total_capex # USD platform_mass = substructure_total_mass + topside_mass + mooring_mass/1000 # t #mass of equipment and floating substructure for substation diff --git a/tests/hopp/test_offshore/test_offshore.py b/tests/hopp/test_offshore/test_offshore.py index e90322ca5..f6dda63cb 100644 --- a/tests/hopp/test_offshore/test_offshore.py +++ b/tests/hopp/test_offshore/test_offshore.py @@ -5,7 +5,10 @@ from ORBIT import load_config from hopp.offshore.fixed_platform import install_platform, calc_platform_opex, calc_substructure_mass_and_cost -'''https://www.nrel.gov/docs/fy17osti/66874.pdf''' +'''Sources: + - [1] 2017 ORBIT Technical Report: https://www.nrel.gov/docs/fy17osti/66874.pdf +''' + @pytest.fixture def config(): offshore_path = Path(__file__).parents[3] / "hopp" / "offshore" @@ -15,6 +18,7 @@ def config(): def test_install_platform(config): ''' Test the code that calculates the platform installation cost + [1]: equations (91),(113),(98) ''' distance = 24 mass = 2100 @@ -27,6 +31,7 @@ def test_install_platform(config): def test_calc_substructure_cost(config): ''' Test the code that calculates the CapEx from fixed_platform.py + [1]: equations (81),(83),(84) ''' topmass = 200 toparea = 1000 @@ -39,6 +44,7 @@ def test_calc_substructure_cost(config): def test_calc_substructure_mass(config): ''' Test the code that calculates the CapEx from fixed_platform.py + [1]: equations (81),(83),(84) ''' topmass = 200 toparea = 1000 diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py index 8c7633913..661a1ba1d 100644 --- a/tests/hopp/test_offshore/test_offshore_floating.py +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -10,8 +10,12 @@ from ORBIT.core.library import initialize_library from ORBIT.phases.design import DesignPhase from ORBIT.phases.install import InstallPhase -#from hopp.offshore.floating_platform import FloatingPlatformDesign -'''Values for these asserts were used using the equations from https://www.nrel.gov/docs/fy17osti/66874.pdf''' + + +'''Sources: + - [1] 2017 ORBIT Technical Report: https://www.nrel.gov/docs/fy17osti/66874.pdf +''' + @pytest.fixture def config(): offshore_path = Path(__file__).parents[3] / "hopp" / "offshore" @@ -21,6 +25,7 @@ def config(): def test_install_platform(config): ''' Test the code that calculates the platform installation cost + [1]: equations (91),(113),(98) ''' distance = 24 mass = 2100 @@ -33,7 +38,7 @@ def test_install_platform(config): def test_calc_substructure_cost(config): ''' Test the code that calculates the CapEx from floating_platform.py - + [1]: equations (81),(83),(84) ''' topmass = 200 toparea = 1000 @@ -41,13 +46,13 @@ def test_calc_substructure_cost(config): cost,_ = calc_substructure_mass_and_cost(topmass, toparea, depth,fab_cost_rate=14500, design_cost=4500000, sub_cost_rate=3000, line_cost=850000, anchor_cost=120000, anchor_mass=20, line_mass=100000, num_lines=4) - assert pytest.approx(cost) == 11520000 + assert pytest.approx(cost) == 23040000 def test_calc_substructure_mass(config): ''' Test the code that calculates the CapEx from floating_platform.py - + [1]: equations (81),(83),(84) ''' topmass = 200 toparea = 1000 From 34d72b54da35de7c19686766e8732b64534ec5c3 Mon Sep 17 00:00:00 2001 From: Kiefer Date: Tue, 11 Jul 2023 13:14:48 -0600 Subject: [PATCH 6/6] Update to source information for test files --- tests/hopp/test_offshore/test_offshore.py | 2 +- tests/hopp/test_offshore/test_offshore_floating.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/hopp/test_offshore/test_offshore.py b/tests/hopp/test_offshore/test_offshore.py index f6dda63cb..3b2c1dfc6 100644 --- a/tests/hopp/test_offshore/test_offshore.py +++ b/tests/hopp/test_offshore/test_offshore.py @@ -6,7 +6,7 @@ from hopp.offshore.fixed_platform import install_platform, calc_platform_opex, calc_substructure_mass_and_cost '''Sources: - - [1] 2017 ORBIT Technical Report: https://www.nrel.gov/docs/fy17osti/66874.pdf + - [1] M. Maness, B. Maples and A. Smith, "NREL Offshore Balance-of-System Model," National Renewable Energy Laboratory, 2017. https://www.nrel.gov/docs/fy17osti/66874.pdf ''' @pytest.fixture diff --git a/tests/hopp/test_offshore/test_offshore_floating.py b/tests/hopp/test_offshore/test_offshore_floating.py index 661a1ba1d..6d67552aa 100644 --- a/tests/hopp/test_offshore/test_offshore_floating.py +++ b/tests/hopp/test_offshore/test_offshore_floating.py @@ -13,7 +13,7 @@ '''Sources: - - [1] 2017 ORBIT Technical Report: https://www.nrel.gov/docs/fy17osti/66874.pdf + - [1] M. Maness, B. Maples and A. Smith, "NREL Offshore Balance-of-System Model," National Renewable Energy Laboratory, 2017. https://www.nrel.gov/docs/fy17osti/66874.pdf ''' @pytest.fixture