import warnings
import openmdao.api as om
from wisdem.plant_financese.plant_finance import PlantFinance as PlantFinance_orig
from wisdem.landbosse.landbosse_omdao.landbosse import LandBOSSE as LandBOSSE_orig
[docs]
class LandBOSSE(LandBOSSE_orig):
"""
Wrapper for WISDEM's LandBOSSE BOS calculators.
A thin wrapper of `wisdem.landbosse.landbosse_omdao.landbosse.LandBOSSE`
that traps warning messages that are recognized not to be issues.
See: https://github.com/WISDEM/LandBOSSE
"""
[docs]
def setup(self):
"""Setup of OM component."""
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
with warnings.catch_warnings():
return super().setup()
[docs]
def setup_partials(self):
"""Derivative setup for OM component."""
# finite difference WISDEM tools for gradients
self.declare_partials(
[
"turbine_spacing_rotor_diameters",
"row_spacing_rotor_diameters",
],
[
"bos_capex_kW",
"total_capex",
],
method="fd",
)
[docs]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
"""Computation for the OM compoent."""
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
with warnings.catch_warnings():
return super().compute(inputs, outputs, discrete_inputs, discrete_outputs)
[docs]
class PlantFinance(PlantFinance_orig):
"""
Wrapper for WISDEM's PlantFinanceSE calculators.
A thin wrapper of `wisdem.plant_financese.plant_finance.PlantFinance` that
traps warning messages that are recognized not to be issues.
See: https://github.com/WISDEM/WISDEM/tree/master/wisdem/plant_financese
"""
[docs]
def setup(self):
"""Setup of OM component."""
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
with warnings.catch_warnings():
return super().setup()
[docs]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
"""Computation for the OM compoent."""
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
with warnings.catch_warnings():
return super().compute(inputs, outputs, discrete_inputs, discrete_outputs)
[docs]
class TurbineCapitalCosts(om.ExplicitComponent):
"""
A simple component to compute the turbine capital costs.
Inputs
------
machine_rating : float
rating of the wind turbine in kW
tcc_per_kW : float
turbine capital costs per kW (as output from WISDEM tools)
offset_tcc_per_kW : float
additional tcc per kW (offset)
Discrete Inputs
---------------
turbine_number : int
number of turbines in the farm
Outputs
-------
tcc : float
turbine capital costs in USD
"""
[docs]
def setup(self):
"""Setup of OM component."""
self.add_input("machine_rating", 0.0, units="kW")
self.add_input("tcc_per_kW", 0.0, units="USD/kW")
self.add_input("offset_tcc_per_kW", 0.0, units="USD/kW")
self.add_discrete_input("turbine_number", 0)
self.add_output("tcc", 0.0, units="USD")
[docs]
def setup_partials(self):
"""Derivative setup for OM component."""
# complex step for simple gradients
self.declare_partials("*", "*", method="cs")
[docs]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
"""Computation for the OM compoent."""
# Unpack parameters
t_rating = inputs["machine_rating"]
n_turbine = discrete_inputs["turbine_number"]
tcc_per_kW = inputs["tcc_per_kW"] + inputs["offset_tcc_per_kW"]
outputs["tcc"] = n_turbine * tcc_per_kW * t_rating
[docs]
class OperatingExpenses(om.ExplicitComponent):
"""
A simple component to compute the operating costs.
Inputs
------
machine_rating : float
rating of the wind turbine in kW
opex_per_kW : float
annual operating and maintenance costs per kW (as output from WISDEM
tools)
Discrete Inputs
---------------
turbine_number : int
number of turbines in the farm
Outputs
-------
opex : float
annual operating and maintenance costs in USD
"""
[docs]
def setup(self):
"""Setup of OM component."""
self.add_input("machine_rating", 0.0, units="kW")
self.add_input("opex_per_kW", 0.0, units="USD/kW/yr")
self.add_discrete_input("turbine_number", 0)
self.add_output("opex", 0.0, units="USD/yr")
[docs]
def setup_partials(self):
"""Derivative setup for OM component."""
# complex step for simple gradients
self.declare_partials("*", "*", method="cs")
[docs]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
"""Computation for the OM compoent."""
# Unpack parameters
t_rating = inputs["machine_rating"]
n_turbine = discrete_inputs["turbine_number"]
opex_per_kW = inputs["opex_per_kW"]
outputs["opex"] = n_turbine * opex_per_kW * t_rating
[docs]
def LandBOSSE_setup_latents(prob, modeling_options):
"""
A function to set up the LandBOSSE latent variables using modeling options.
Parameters
----------
prob : openmdao.api.Problem
an OpenMDAO problem for which we want to setup the LandBOSSE latent
variables
modeling_options : dict
a modeling options dictionary
"""
# get a map from the component variables to the promotion variables
comp2promotion_map = {
v[0]: v[-1]["prom_name"]
for v in prob.model.list_vars(val=False, out_stream=None)
}
# set latent/non-design inputs to LandBOSSE using values in modeling_options
prob.set_val(
comp2promotion_map["landbosse.landbosse.num_turbines"],
modeling_options["farm"]["N_turbines"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.turbine_rating_MW"],
modeling_options["turbine"]["nameplate"]["power_rated"] * 1.0e3,
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.hub_height_meters"],
modeling_options["turbine"]["geometry"]["height_hub"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.wind_shear_exponent"],
modeling_options["turbine"]["costs"]["wind_shear_exponent"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.rotor_diameter_m"],
modeling_options["turbine"]["geometry"]["diameter_rotor"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.number_of_blades"],
modeling_options["turbine"]["geometry"]["num_blades"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.rated_thrust_N"],
modeling_options["turbine"]["costs"]["rated_thrust_N"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.gust_velocity_m_per_s"],
modeling_options["turbine"]["costs"]["gust_velocity_m_per_s"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.blade_surface_area"],
modeling_options["turbine"]["costs"]["blade_surface_area"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.tower_mass"],
modeling_options["turbine"]["costs"]["tower_mass"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.nacelle_mass"],
modeling_options["turbine"]["costs"]["nacelle_mass"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.hub_mass"],
modeling_options["turbine"]["costs"]["hub_mass"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.blade_mass"],
modeling_options["turbine"]["costs"]["blade_mass"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.foundation_height"],
modeling_options["turbine"]["costs"]["foundation_height"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.commissioning_pct"],
modeling_options["turbine"]["costs"]["commissioning_pct"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.decommissioning_pct"],
modeling_options["turbine"]["costs"]["decommissioning_pct"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.trench_len_to_substation_km"],
modeling_options["turbine"]["costs"]["trench_len_to_substation_km"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.distance_to_interconnect_mi"],
modeling_options["turbine"]["costs"]["distance_to_interconnect_mi"],
)
prob.set_val(
comp2promotion_map["landbosse.landbosse.interconnect_voltage_kV"],
modeling_options["turbine"]["costs"]["interconnect_voltage_kV"],
)
[docs]
def FinanceSE_setup_latents(prob, modeling_options):
"""
A function to set up the FinanceSE latent variables using modeling options.
Parameters
----------
prob : openmdao.api.Problem
an OpenMDAO problem for which we want to setup the LandBOSSE latent
variables
modeling_options : dict
a modeling options dictionary
"""
# get a map from the component variables to the promotion variables
comp2promotion_map = {
v[0]: v[-1]["prom_name"]
for v in prob.model.list_vars(val=False, out_stream=None)
}
# inputs to PlantFinanceSE
prob.set_val(
comp2promotion_map["financese.turbine_number"],
int(modeling_options["farm"]["N_turbines"]),
)
prob.set_val(
comp2promotion_map["financese.machine_rating"],
modeling_options["turbine"]["nameplate"]["power_rated"] * 1.0e3,
)
prob.set_val(
comp2promotion_map["financese.tcc_per_kW"],
modeling_options["turbine"]["costs"]["tcc_per_kW"],
)
prob.set_val(
comp2promotion_map["financese.opex_per_kW"],
modeling_options["turbine"]["costs"]["opex_per_kW"],
)