From 3cbfd5b6e203fe7905ec46395da42b7f3beca8d6 Mon Sep 17 00:00:00 2001 From: cgschwend1 <162733802+cgschwend1@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:26:21 -0400 Subject: [PATCH 1/3] Updated electrical export design phase to be able to pull sets of substation costs from common_costs.py based on the voltage rating of the cable. Added 420kV cable file, modified per-km cost of 525kV cable, modified substation component costs for 420 and 525kV cables --- ORBIT/core/defaults/common_costs.yaml | 85 +++++++++++++++++++++++- ORBIT/phases/design/_cables.py | 13 +--- ORBIT/phases/design/electrical_export.py | 81 +++++++++++++++------- library/cables/HVDC_2500mm_525kV.yaml | 2 +- library/cables/XLPE_2500mm_420kV.yaml | 11 +++ 5 files changed, 152 insertions(+), 40 deletions(-) create mode 100644 library/cables/XLPE_2500mm_420kV.yaml diff --git a/ORBIT/core/defaults/common_costs.yaml b/ORBIT/core/defaults/common_costs.yaml index 1e8e9867..64311545 100644 --- a/ORBIT/core/defaults/common_costs.yaml +++ b/ORBIT/core/defaults/common_costs.yaml @@ -19,7 +19,7 @@ spar_design: secondary_steel_CR: 7250 # USD/t # Offshore substation component cost rates -substation_design: +substation_design_275kV: mpt_cost_rate: 12500 # USD/MW topside_fab_cost_rate: 14500 # USD/t topside_design_cost: # USD @@ -43,13 +43,60 @@ substation_design: oss_substructure_cost_rate: 3000 # USD/t oss_pile_cost_rate: 0 # USD/t +# Offshore substation component cost rates +substation_design_420kV: + topside_fab_cost_rate: 17400 # USD/t + topside_design_cost: # USD + #oldHVAC: 4.5e6 + HVAC: 128.76e6 + HVDC-monopole: 352.8e6 + HVDC-bipole: 571.2e6 + mpt_unit_cost: 3.44e6 # USD/mpt + shunt_unit_cost: 3.8e3 # USD/cable + switchgear_cost: 4.8e6 # USD/cable + dc_breaker_cost: 12.6e6 # USD/cable + backup_gen_cost: 1.2e6 # USD + workspace_cost: 2.4e6 # USD + other_ancillary_cost: 3.6e6 # USD + topside_assembly_factor: 0.075 # % + converter_cost: # USD + HVAC: 0 + HVDC-monopole: 152.4e6 + HVDC-bipole: 355.2e6 + oss_substructure_cost_rate: 3600 # USD/t + oss_pile_cost_rate: 0 # USD/t + + # Offshore substation component cost rates +substation_design_525kV: + mpt_cost_rate: 15525 # USD/MW + topside_fab_cost_rate: 17980 # USD/t + topside_design_cost: # USD + #oldHVAC: 4.5e6 + HVAC: 133.1e6 + HVDC-monopole: 364.6e6 + HVDC-bipole: 590.2e6 + mpt_unit_cost: 3.56e6 # USD/mpt + shunt_unit_cost: 10000 # USD/cable + switchgear_cost: 4.96e6 # USD/cable + dc_breaker_cost: 13.02e6 # USD/cable + backup_gen_cost: 1.24e6 # USD + workspace_cost: 2.48e6 # USD + other_ancillary_cost: 3.72e6 # USD + topside_assembly_factor: 0.075 # % + converter_cost: # USD + HVAC: 0 + HVDC-monopole: 157.5e6 + HVDC-bipole: 367e6 + oss_substructure_cost_rate: 3720 # USD/t + oss_pile_cost_rate: 0 # USD/t + # Onshore substation component cost rates -onshore_substation_design: +onshore_substation_design_275kV: onshore_converter_cost: # USD HVAC: 0 HVDC-monopole: 157e6 HVDC-bipole: 350e6 - shunt_unit_cost: 13000 # USD/cable + shunt_unit_cost: 3.8e3 # USD/cable switchgear_cost: 9.33e6 # USD/cable compensation_rate: # USD/cable HVAC: 31.3e6 @@ -60,6 +107,38 @@ onshore_substation_design: HVDC-monopole: 87.3e6 HVDC-bipole: 100e6 +onshore_substation_design_420kV: + onshore_converter_cost: # USD + HVAC: 0 + HVDC-monopole: 188.4e6 + HVDC-bipole: 420e6 + shunt_unit_cost: 3.8e3 # USD/cable + switchgear_cost: 11.196e6 # USD/cable + compensation_rate: # USD/cable + HVAC: 37.56e6 + HVDC-monopole: 0 + HVDC-bipole: 0 + onshore_construction_rate: # USD + HVAC: 6e6 + HVDC-monopole: 104.76e6 + HVDC-bipole: 120e6 + +onshore_substation_design_525kV: + onshore_converter_cost: # USD + HVAC: 0 + HVDC-monopole: 194.7e6 + HVDC-bipole: 434e6 + shunt_unit_cost: 4.71e3 # USD/cable + switchgear_cost: 11.57e6 # USD/cable + compensation_rate: # USD/cable + HVAC: 31.3e6 + HVDC-monopole: 0 + HVDC-bipole: 0 + onshore_construction_rate: # USD + HVAC: 6.2e6 + HVDC-monopole: 108.3e6 + HVDC-bipole: 124e6 + # Semisubmersible component cost rates semisubmersible_design: stiffened_column_CR: 3120 # USD/t diff --git a/ORBIT/phases/design/_cables.py b/ORBIT/phases/design/_cables.py index 56410eea..3b5be8ed 100644 --- a/ORBIT/phases/design/_cables.py +++ b/ORBIT/phases/design/_cables.py @@ -84,18 +84,7 @@ def __init__(self, cable_specs, **kwargs): raise ValueError(f"{needs_value} must be defined in cable_specs") self.line_frequency = cable_specs.get("line_frequency", 60) - cable_type = cable_specs.get("cable_type", "HVAC").split("-") - if len(cable_type) == 1: - self.cable_type = cable_type[0].upper() - elif len(cable_type) == 2: - self.cable_type = ( - f"{cable_type[0].upper()}-{cable_type[1].lower()}" - ) - else: - raise ValueError( - "`cable_type` should be of the form `type-subtype`," - " e.g. 'HVDC-monopole'." - ) + self.cable_type = cable_specs.get("cable_type", "HVAC") # Calc additional cable specs if self.cable_type == "HVAC": diff --git a/ORBIT/phases/design/electrical_export.py b/ORBIT/phases/design/electrical_export.py index db06635b..a6c6463f 100644 --- a/ORBIT/phases/design/electrical_export.py +++ b/ORBIT/phases/design/electrical_export.py @@ -140,7 +140,7 @@ def __init__(self, config, **kwargs): self.substructure_type = self._oss_design.get( "oss_substructure_type", "Monopile" - ).title() + ) self._outputs = {} @@ -350,6 +350,33 @@ def calc_crossing_cost(self): ) """SUBSTATION""" + def get_substation_design(self): + #Matching cable name with corresponding section in common_costs + #cable_name = self.cable.name + #voltage = next(part for part in cable_name.split('_') if part.endswith("kV")) + voltage = self.cable.rated_voltage + substation_design = f"substation_design_{voltage}kV" + + #default to using the 275 kV substation costs if there is no corresponding section in common_costs + try: + self.get_default_cost(substation_design, "mpt_unit_cost") + except KeyError: + voltage = "275kV" + substation_design = f"substation_design_{voltage}" + return substation_design + + def get_onshore_substation_design(self): + #Matching cable name with corresponding section in common_costs + voltage = self.cable.rated_voltage + onshore_substation_design = f"onshore_substation_design_{voltage}kV" + + #default to using the 275 kV onshore substation costs if there is no corresponding section in common_costs + try: + self.get_default_cost(onshore_substation_design, "shunt_unit_cost") + except KeyError: + voltage = "275kV" + onshore_substation_design = f"onshore_substation_design_{voltage}" + return onshore_substation_design @property def total_substation_cost(self): @@ -397,10 +424,10 @@ def calc_mpt_cost(self): """Computes HVAC main power transformer (MPT). MPT cost is 0 for HVDC. """ - + substation_design =self.get_substation_design() _key = "mpt_unit_cost" _mpt_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) self.num_mpt = self.num_cables @@ -417,13 +444,13 @@ def calc_shunt_reactor_cost(self): """Computes HVAC shunt reactor cost. Shunt reactor cost is 0 for HVDC. """ - + substation_design =self.get_substation_design() touchdown = self.config["site"]["distance_to_landfall"] _key = "shunt_unit_cost" shunt_unit_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) if "HVDC" in self.cable.cable_type: @@ -438,10 +465,10 @@ def calc_shunt_reactor_cost(self): def calc_switchgear_costs(self): """Computes HVAC switchgear cost. Switchgear cost is 0 for HVDC.""" - + substation_design =self.get_substation_design() _key = "switchgear_cost" switchgear_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) self.num_switchgear = ( @@ -452,10 +479,10 @@ def calc_switchgear_costs(self): def calc_dc_breaker_cost(self): """Computes HVDC circuit breaker cost. Breaker cost is 0 for HVAC.""" - + substation_design =self.get_substation_design() _key = "dc_breaker_cost" dc_breaker_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) num_dc_breakers = ( @@ -467,19 +494,20 @@ def calc_dc_breaker_cost(self): def calc_ancillary_system_cost(self): """Calculates cost of ancillary systems.""" + substation_design =self.get_substation_design() _key = "backup_gen_cost" backup_gen_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) _key = "workspace_cost" workspace_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) _key = "other_ancillary_cost" other_ancillary_cost = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) self.ancillary_system_costs = ( @@ -489,9 +517,10 @@ def calc_ancillary_system_cost(self): def calc_assembly_cost(self): """Calculates the cost of assembly on land.""" + substation_design =self.get_substation_design() _key = "topside_assembly_factor" topside_assembly_factor = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) if topside_assembly_factor > 1.0: @@ -506,11 +535,12 @@ def calc_assembly_cost(self): def calc_converter_cost(self): """Computes converter cost.""" + substation_design =self.get_substation_design() _key = "converter_cost" converter_cost = self._oss_design.get( _key, self.get_default_cost( - "substation_design", _key, subkey=self.cable.cable_type + substation_design, _key, subkey=self.cable.cable_type ), ) @@ -521,15 +551,15 @@ def calc_substructure_mass_and_cost(self): Calculates the mass and associated cost of the substation substructure based on equations 81-84 [1]. """ - + substation_design =self.get_substation_design() _key = "oss_substructure_cost_rate" oss_substructure_cost_rate = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) _key = "oss_pile_cost_rate" oss_pile_cost_rate = self._oss_design.get( - _key, self.get_default_cost("substation_design", _key) + _key, self.get_default_cost(substation_design, _key) ) # Substructure mass components @@ -539,7 +569,7 @@ def calc_substructure_mass_and_cost(self): substructure_pile_mass = ( 0 - if self.substructure_type == "Floating" + if "Floating" in self.substructure_type else 8 * substructure_mass**0.5574 ) @@ -580,6 +610,8 @@ def calc_topside_deck_space(self): def calc_topside_mass_and_cost(self): """Calculates the mass and cost of the substation topsides.""" + substation_design =self.get_substation_design() + self.topside_mass = ( 3.85 * (self.mpt_rating * self.num_mpt) / self.num_substations + 285 @@ -589,7 +621,7 @@ def calc_topside_mass_and_cost(self): topside_design_cost = self._oss_design.get( _key, self.get_default_cost( - "substation_design", _key, subkey=self.cable.cable_type + substation_design, _key, subkey=self.cable.cable_type ), ) @@ -599,12 +631,13 @@ def calc_onshore_cost(self): """Minimum Cost of Onshore Substation Connection.""" _design = self.config.get("onshore_substation_design", {}) + onshore_substation_design =self.get_onshore_substation_design() _key = "onshore_converter_cost" _converter_cost = _design.get( _key, self.get_default_cost( - "onshore_substation_design", _key, subkey=self.cable.cable_type + onshore_substation_design, _key, subkey=self.cable.cable_type ), ) @@ -612,7 +645,7 @@ def calc_onshore_cost(self): _key = "switchgear_cost" _switchgear_cost = _design.get( - _key, self.get_default_cost("onshore_substation_design", _key) + _key, self.get_default_cost(onshore_substation_design, _key) ) self.onshore_switchgear_cost = self.num_switchgear * _switchgear_cost @@ -621,7 +654,7 @@ def calc_onshore_cost(self): _construction_rate = _design.get( _key, self.get_default_cost( - "onshore_substation_design", _key, subkey=self.cable.cable_type + onshore_substation_design, _key, subkey=self.cable.cable_type ), ) @@ -629,7 +662,7 @@ def calc_onshore_cost(self): _key = "shunt_unit_cost" _shunt_unit_cost = _design.get( - _key, self.get_default_cost("onshore_substation_design", _key) + _key, self.get_default_cost(onshore_substation_design, _key) ) self.onshore_shunt_reactor_cost = ( @@ -640,7 +673,7 @@ def calc_onshore_cost(self): _compensation_rate = _design.get( _key, self.get_default_cost( - "onshore_substation_design", _key, subkey=self.cable.cable_type + onshore_substation_design, _key, subkey=self.cable.cable_type ), ) diff --git a/library/cables/HVDC_2500mm_525kV.yaml b/library/cables/HVDC_2500mm_525kV.yaml index ffeb6330..dff09c86 100644 --- a/library/cables/HVDC_2500mm_525kV.yaml +++ b/library/cables/HVDC_2500mm_525kV.yaml @@ -1,7 +1,7 @@ ac_resistance: 0 # ohm/km capacitance: 227000 # nF/km conductor_size: 2500 # mm^2 -cost_per_km: 1420000 # $ +cost_per_km: 1855000 # $ current_capacity: 1905 # A inductance: 0.149 # mH/km linear_density: 74 # t/km diff --git a/library/cables/XLPE_2500mm_420kV.yaml b/library/cables/XLPE_2500mm_420kV.yaml new file mode 100644 index 00000000..fe9b5eea --- /dev/null +++ b/library/cables/XLPE_2500mm_420kV.yaml @@ -0,0 +1,11 @@ +# from TFKabel +ac_resistance: 0.014 # ohm/km +capacitance: 225 # nF/km +conductor_size: 2500 # mm^2 +cost_per_km: 2450000 # $/km Based on the 220kV costs that are in ORBIT/electrical-refactor right now and the Xiang et al. (2016) +current_capacity: 688 # A # trefoil config in earth, 90C +inductance: 0.115 # mH/km +linear_density: 156 # t/km # ESTIMATE +cable_type: HVAC # HVDC vs HVAC +name: XLPE_2500mm_420kV_dynamic +rated_voltage: 420 \ No newline at end of file From 8fc96fcf7333495c675037e6efb6a7d4069f09d9 Mon Sep 17 00:00:00 2001 From: cgschwend1 <162733802+cgschwend1@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:20:01 -0400 Subject: [PATCH 2/3] Cleaned up common costs and export phase changes --- ORBIT/core/defaults/common_costs.yaml | 45 +++++++++++------------- ORBIT/phases/design/electrical_export.py | 4 --- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/ORBIT/core/defaults/common_costs.yaml b/ORBIT/core/defaults/common_costs.yaml index 64311545..c3d77ea2 100644 --- a/ORBIT/core/defaults/common_costs.yaml +++ b/ORBIT/core/defaults/common_costs.yaml @@ -20,14 +20,12 @@ spar_design: # Offshore substation component cost rates substation_design_275kV: - mpt_cost_rate: 12500 # USD/MW topside_fab_cost_rate: 14500 # USD/t topside_design_cost: # USD #oldHVAC: 4.5e6 HVAC: 107.3e6 HVDC-monopole: 294e6 HVDC-bipole: 476e6 - shunt_cost_rate: 35000 # USD/MW mpt_unit_cost: 2.87e6 # USD/mpt shunt_unit_cost: 10000 # USD/cable switchgear_cost: 4e6 # USD/cable @@ -43,32 +41,29 @@ substation_design_275kV: oss_substructure_cost_rate: 3000 # USD/t oss_pile_cost_rate: 0 # USD/t -# Offshore substation component cost rates substation_design_420kV: - topside_fab_cost_rate: 17400 # USD/t + topside_fab_cost_rate: 16240 # USD/t topside_design_cost: # USD #oldHVAC: 4.5e6 - HVAC: 128.76e6 - HVDC-monopole: 352.8e6 - HVDC-bipole: 571.2e6 - mpt_unit_cost: 3.44e6 # USD/mpt + HVAC: 120.1766e6 + HVDC-monopole: 329.28e6 + HVDC-bipole: 533.12e6 + mpt_unit_cost: 3.2144e6 # USD/mpt shunt_unit_cost: 3.8e3 # USD/cable - switchgear_cost: 4.8e6 # USD/cable - dc_breaker_cost: 12.6e6 # USD/cable - backup_gen_cost: 1.2e6 # USD - workspace_cost: 2.4e6 # USD - other_ancillary_cost: 3.6e6 # USD + switchgear_cost: 4.48e6 # USD/cable + dc_breaker_cost: 11.76e6 # USD/cable + backup_gen_cost: 1.12e6 # USD + workspace_cost: 2.24e6 # USD + other_ancillary_cost: 3.36e6 # USD topside_assembly_factor: 0.075 # % converter_cost: # USD HVAC: 0 - HVDC-monopole: 152.4e6 - HVDC-bipole: 355.2e6 - oss_substructure_cost_rate: 3600 # USD/t + HVDC-monopole: 142.24e6 + HVDC-bipole: 331.52.2e6 + oss_substructure_cost_rate: 3360 # USD/t oss_pile_cost_rate: 0 # USD/t - # Offshore substation component cost rates substation_design_525kV: - mpt_cost_rate: 15525 # USD/MW topside_fab_cost_rate: 17980 # USD/t topside_design_cost: # USD #oldHVAC: 4.5e6 @@ -110,18 +105,18 @@ onshore_substation_design_275kV: onshore_substation_design_420kV: onshore_converter_cost: # USD HVAC: 0 - HVDC-monopole: 188.4e6 - HVDC-bipole: 420e6 + HVDC-monopole: 175.84e6 + HVDC-bipole: 392e6 shunt_unit_cost: 3.8e3 # USD/cable - switchgear_cost: 11.196e6 # USD/cable + switchgear_cost: 10.4496e6 # USD/cable compensation_rate: # USD/cable - HVAC: 37.56e6 + HVAC: 35.056e6 HVDC-monopole: 0 HVDC-bipole: 0 onshore_construction_rate: # USD - HVAC: 6e6 - HVDC-monopole: 104.76e6 - HVDC-bipole: 120e6 + HVAC: 5.6e6 + HVDC-monopole: 97.776e6 + HVDC-bipole: 112e6 onshore_substation_design_525kV: onshore_converter_cost: # USD diff --git a/ORBIT/phases/design/electrical_export.py b/ORBIT/phases/design/electrical_export.py index a6c6463f..0146ce5c 100644 --- a/ORBIT/phases/design/electrical_export.py +++ b/ORBIT/phases/design/electrical_export.py @@ -351,9 +351,6 @@ def calc_crossing_cost(self): """SUBSTATION""" def get_substation_design(self): - #Matching cable name with corresponding section in common_costs - #cable_name = self.cable.name - #voltage = next(part for part in cable_name.split('_') if part.endswith("kV")) voltage = self.cable.rated_voltage substation_design = f"substation_design_{voltage}kV" @@ -366,7 +363,6 @@ def get_substation_design(self): return substation_design def get_onshore_substation_design(self): - #Matching cable name with corresponding section in common_costs voltage = self.cable.rated_voltage onshore_substation_design = f"onshore_substation_design_{voltage}kV" From fcb853f7d85f1ebad0d3c6d8aa523968a2bc490f Mon Sep 17 00:00:00 2001 From: cgschwend1 <162733802+cgschwend1@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:30:39 -0400 Subject: [PATCH 3/3] Made updates consistent with most recent dev commit --- ORBIT/phases/design/_cables.py | 13 ++++++++++++- ORBIT/phases/design/electrical_export.py | 6 +++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ORBIT/phases/design/_cables.py b/ORBIT/phases/design/_cables.py index 3b5be8ed..56410eea 100644 --- a/ORBIT/phases/design/_cables.py +++ b/ORBIT/phases/design/_cables.py @@ -84,7 +84,18 @@ def __init__(self, cable_specs, **kwargs): raise ValueError(f"{needs_value} must be defined in cable_specs") self.line_frequency = cable_specs.get("line_frequency", 60) - self.cable_type = cable_specs.get("cable_type", "HVAC") + cable_type = cable_specs.get("cable_type", "HVAC").split("-") + if len(cable_type) == 1: + self.cable_type = cable_type[0].upper() + elif len(cable_type) == 2: + self.cable_type = ( + f"{cable_type[0].upper()}-{cable_type[1].lower()}" + ) + else: + raise ValueError( + "`cable_type` should be of the form `type-subtype`," + " e.g. 'HVDC-monopole'." + ) # Calc additional cable specs if self.cable_type == "HVAC": diff --git a/ORBIT/phases/design/electrical_export.py b/ORBIT/phases/design/electrical_export.py index 0146ce5c..0bf5b048 100644 --- a/ORBIT/phases/design/electrical_export.py +++ b/ORBIT/phases/design/electrical_export.py @@ -140,7 +140,7 @@ def __init__(self, config, **kwargs): self.substructure_type = self._oss_design.get( "oss_substructure_type", "Monopile" - ) + ).title() self._outputs = {} @@ -565,7 +565,7 @@ def calc_substructure_mass_and_cost(self): substructure_pile_mass = ( 0 - if "Floating" in self.substructure_type + if self.substructure_type == "Floating" else 8 * substructure_mass**0.5574 ) @@ -627,7 +627,7 @@ def calc_onshore_cost(self): """Minimum Cost of Onshore Substation Connection.""" _design = self.config.get("onshore_substation_design", {}) - onshore_substation_design =self.get_onshore_substation_design() + onshore_substation_design = self.get_onshore_substation_design() _key = "onshore_converter_cost" _converter_cost = _design.get(