Skip to content
Merged
59 changes: 43 additions & 16 deletions ORBIT/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def compile_input_dict(cls, phases):
" commissioning_factor)"
),
"construction_insurance_factor": (
"float (optional, default: 0.0115)"
"float (optional, default: 0.0207)"
),
"construction_financing_factor": (
"$/kW (optional, default: value calculated using"
Expand All @@ -379,20 +379,20 @@ def compile_input_dict(cls, phases):
),
"tax_rate": "float (optional, default: 0.26",
"interest_during_construction": (
"float (optional, default: 0.044"
"float (optional, default: 0.065"
),
"procurement_contingency_factor": (
"float (optional, default: 0.0575)"
),
"installation_contingency_factor": (
"float (optional, default: 0.345)"
),
"decommissioning_factor": ("float (optional, default: 0.1725)"),
"decommissioning_factor": ("float (optional, default: 0.2)"),
"commissioning_factor": "float (optional, default: 0.0115)",
"site_auction_price": "$ (optional, default: 100e6)",
"site_assessment_cost": "$ (optional, default: 50e6)",
"construction_plan_cost": "$ (optional, default: 1e6)",
"installation_plan_cost": "$ (optional, default: 0.25e6)",
"site_auction_price": "$ (optional, default: 105e6)",
"site_assessment_cost": "$ (optional, default: 200e6)",
"construction_plan_cost": "$ (optional, default: 25e6)",
"installation_plan_cost": "$ (optional, default: 25e6)",
}

config["design_phases"] = [*design_phases.keys()]
Expand Down Expand Up @@ -670,7 +670,7 @@ def run_install_phase(self, name, start, **kwargs):

if phase.installation_capex:
self.installation_costs[name] = phase.installation_capex

return time, logs

def get_phase_class(self, phase):
Expand Down Expand Up @@ -747,6 +747,8 @@ def run_design_phase(self, name, **kwargs):
self.detailed_outputs, phase.detailed_output
)



def run_multiple_phases_in_serial(self, phase_list, **kwargs):
"""
Runs multiple phases listed in self.config['install_phases'] in serial.
Expand Down Expand Up @@ -1054,6 +1056,8 @@ def outputs(self, include_logs=False, npv_detailed=False):
"npv": self.npv,
"supply_chain_capex": self.supply_chain_capex,
"supply_chain_capex_kw": self.supply_chain_capex_per_kw,
"onshore_substation_capex": self.onshore_substation_capex,
"onshore_substation_capex_kw": self.onshore_substation_capex_per_kw,
}

if include_logs:
Expand Down Expand Up @@ -1442,6 +1446,8 @@ def capex_breakdown(self):
else:
outputs[name] = cost

outputs["Onshore Substation"] = self.onshore_substation_capex

outputs["Turbine"] = self.turbine_capex

outputs["Soft"] = self.soft_capex
Expand Down Expand Up @@ -1491,7 +1497,7 @@ def capex_detailed_soft_capex_breakdown_per_kw(self):
def bos_capex(self):
"""Returns total balance of system CapEx."""

return self.system_capex + self.installation_capex
return self.system_capex + self.installation_capex + self.onshore_substation_capex

@property
def bos_capex_per_kw(self):
Expand Down Expand Up @@ -1562,6 +1568,27 @@ def supply_chain_capex_per_kw(self):

return _capex

@property
def onshore_substation_capex(self):
"""Returns the onshore substation CapEx if available in 'ElectricalDesign', otherwise 0."""
if "ElectricalDesign" in self.phases:
try:
return self.phases["ElectricalDesign"].detailed_output["export_system"]["onshore_substation_costs"]
except KeyError:
return 0
return 0

@property
def onshore_substation_capex_per_kw(self):
"""Returns the onshore substation CapEx/kW.
"""
if "ElectricalDesign" in self.phases:
try:
return self.onshore_substation_capex / (self.capacity * 1000)
except KeyError:
return None
return None

@property
def overnight_capex(self):
"""Returns the overnight capital cost of the project."""
Expand Down Expand Up @@ -1616,7 +1643,7 @@ def construction_insurance_capex(self):
)

contruction_insurance_factor = self.project_params.get(
"construction_insurance_factor", 0.0115
"construction_insurance_factor", 0.0207
)

if construction_insurance_per_kW is not None:
Expand All @@ -1642,7 +1669,7 @@ def decommissioning_capex(self):
)

decommissioning_factor = self.project_params.get(
"decommissioning_factor", 0.175
"decommissioning_factor", 0.2
)

if decommissioning_per_kW is not None:
Expand Down Expand Up @@ -1747,7 +1774,7 @@ def construction_financing_factor(self):
)
tax_rate = self.project_params.get("tax_rate", 0.26)
interest_during_construction = self.project_params.get(
"interest_during_construction", 0.044
"interest_during_construction", 0.065
)

_check = 0
Expand Down Expand Up @@ -1808,15 +1835,15 @@ def project_capex(self):
the keys below should be passed to the 'project_parameters' subdict.
"""

site_auction = self.project_params.get("site_auction_price", 122698898)
site_auction = self.project_params.get("site_auction_price", 105000000)
site_assessment = self.project_params.get(
"site_assessment_cost", 61349449
"site_assessment_cost", 200000000
)
construction_plan = self.project_params.get(
"construction_plan_cost", 1226989
"construction_plan_cost", 25000000
)
installation_plan = self.project_params.get(
"installation_plan_cost", 306747
"installation_plan_cost", 25000000
)

return sum(
Expand Down
16 changes: 16 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ Unreleased
configuration input.
- Move the matplotlib import from the import section of ``/ORBIT/phases/design/array_system_design.py``
to the ``CustomArraySystemDesign.plot_array_system`` for missing module error handling.
- Updated default `soft_capex` factors. `PR #201 <https://github.com/WISDEM/ORBIT/pull/201>`_
- `construction_insurance_factor` updated from 0.115 to 0.0207 based on industry benchmarking, resulting in higher construction insurance costs.
- `interest_during_construction` updated from 4.4% to 6.5% based on financial assumptions from the 2025 Annual Technology Baseline (ATB), increasing construction financing costs.
- `decommissioning_factor` updated from 0.175 to 0.2 based on industry benchmarking, leading to higher decommissioning costs than in previous versions.
- Updated default `project_capex` values. `PR #201 <https://github.com/WISDEM/ORBIT/pull/201>`_
- `site_auction_price` increased from 100M to 105M USD to account for rent fees before operation.
- `site_assessment_cost`, `construction_plan_cost`, and `installation_plan_cost` increased from 50M, 1M, and 0.25M USD to 200M, 25M, and 25M USD, respectively.
- Total `project_capex` excluding `site_auction_price` now sums to 250M USD, aligning with DevEx recommendations based on industry benchmarking.
- These updates lead to higher default total project costs than in previous versions.
- Included onshore substation costs in BOS CapEx and project breakdown. `PR #201 <https://github.com/WISDEM/ORBIT/pull/201>`_
- The `ElectricalDesign` module previously calculated onshore substation costs but did not include them in `capex_breakdown` or `bos_capex`.
- These costs are now incorporated when `ElectricalDesign` is used, resulting in higher `bos_capex`, `soft_capex`, and `total_capex` than in prior versions.
- Cable configuration file updates. `PR #201 <https://github.com/WISDEM/ORBIT/pull/201>`_
- Added a new dynamic cable configuration file for floating cases: `library/cables/XLPE_1200mm_220kV_dynamic.yaml`.
- Updated cost values for `library/cables/XLPE_630mm_66kV.yaml` and `library/cables/XLPE_630mm_66kV_dynamic.yaml` based on industry benchmarking.
- All cable cost updates are expressed in 2024 USD for consistency with other library configuration files.

1.2.4
-----
Expand Down
13 changes: 13 additions & 0 deletions library/cables/XLPE_1200mm_220kV_dynamic.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Caveat: cable specs not from a spec sheet
# Assumed to have a cable with these costs
# and a capacity of 400 MW (seen on industry)
ac_resistance: 0.16 # ohm/km
capacitance: 190 # nF/km
conductor_size: 1200 # mm^2
cost_per_km: 1801082 # $
current_capacity: 1125 # A
inductance: 0.38 # mH/km
linear_density: 115 # t/km
rated_voltage: 220 # kV
cable_type: HVAC # HVDC vs HVAC
name: XLPE_1200mm_220kV_dynamic
2 changes: 1 addition & 1 deletion library/cables/XLPE_630mm_66kV.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ac_resistance: 0.04
capacitance: 300
conductor_size: 630
cost_per_km: 483084
cost_per_km: 650000
current_capacity: 775
inductance: 0.35
linear_density: 42.5
Expand Down
2 changes: 1 addition & 1 deletion library/cables/XLPE_630mm_66kV_dynamic.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ac_resistance: 0.04
capacitance: 300
conductor_size: 630
cost_per_km: 579700 # $ (20% adder over XLPE_630mm_66kV)
cost_per_km: 780000 # $ (20% adder over XLPE_630mm_66kV)
current_capacity: 775
inductance: 0.35
linear_density: 42.5
Expand Down
12 changes: 6 additions & 6 deletions tests/test_project_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,22 +948,22 @@ def test_project_costs():
baseline = project.project_capex

config = deepcopy(complete_project)
config["project_parameters"] = {"site_auction_price": 50e6}
config["project_parameters"] = {"site_auction_price": 30e6}
project = ProjectManager(config)
assert project.project_capex != baseline

config = deepcopy(complete_project)
config["project_parameters"] = {"site_assessment_cost": 25e6}
config["project_parameters"] = {"site_assessment_cost": 30e6}
project = ProjectManager(config)
assert project.project_capex != baseline

config = deepcopy(complete_project)
config["project_parameters"] = {"construction_plan_cost": 25e6}
config["project_parameters"] = {"construction_plan_cost": 30e6}
project = ProjectManager(config)
assert project.project_capex != baseline

config = deepcopy(complete_project)
config["project_parameters"] = {"installation_plan_cost": 25e6}
config["project_parameters"] = {"installation_plan_cost": 30e6}
project = ProjectManager(config)
assert project.project_capex != baseline

Expand Down Expand Up @@ -995,14 +995,14 @@ def test_total_capex():
fix_project.run()

assert fix_project.total_capex == pytest.approx(
1593220043.059402, abs=1e-1
1843929494.9506452, abs=1e-1
)

flt_project = ProjectManager(complete_floating_project)
flt_project.run()

assert flt_project.total_capex == pytest.approx(
4087665127.1458263, abs=1e-1
4448212431.513601, abs=1e-1
)


Expand Down