1. NREL Cost and Scaling Model Example

Turbine Component Masses Using the NREL_CSM (2015)

As an example of estimating turbine component masses (only) using the 2015 update of the NREL Cost and Scaling Model (CSM), let us simulate the NREL 5MW Reference Model [JBMS09].

The first step is to import OpenMDAO and the model itself:

import openmdao.api as om

from wisdem.nrelcsm.nrel_csm_mass_2015 import nrel_csm_mass_2015

Next, we initialize an OpenMDAO instance and assign the model to be the nrel_csm_mass_2015 module. The setup() command completes the high-level configuration readies the model for variable inpu:

# OpenMDAO Problem instance
prob = om.Problem(reports=False)
prob.model = nrel_csm_mass_2015()
prob.setup()

The turbine scaling relies on key turbine configuration parameters. These filter down to the individual component models through the rotor, nacelle, and tower as described on the Theory page. The variables are set like a Python dictionary:

# Initialize variables for NREL CSM
prob["machine_rating"] = 5000.0
prob["rotor_diameter"] = 126.0
prob["turbine_class"] = 2
prob["tower_length"] = 90.0
prob["blade_number"] = 3
prob["blade_has_carbon"] = False
prob["max_tip_speed"] = 80.0
prob["max_efficiency"] = 0.90
prob["main_bearing_number"] = 2
prob["crane"] = True

We can now run the model to compute the component masses:

# Evaluate the model
prob.run_model()

We can then print out an exhaustive listing of the inputs and outputs to each submodule:

# Print all intermediate inputs and outputs to the screen
prob.model.list_inputs(units=True)
prob.model.list_outputs(units=True)

The final lines highlight the mass breakdown summaries:

>>>  turbine
>>>    hub_system_mass      [47855.49446548]   kg
>>>    rotor_mass           [95109.93575675]   kg
>>>    nacelle_mass         [165460.38774975]  kg
>>>    turbine_mass         [442906.80408368]  kg

See the full source for this example on Github.

Turbine Component Masses and Costs Using the NREL_CSM (2015)

It is often desired to estimate the component costs and cost of energy of a hypothetical turbine, not just the component masses in the previous example. To do so, all that is required is import the full 2015 Cost and Scaling model with:

import openmdao.api as om

from wisdem.nrelcsm.nrel_csm_mass_2015 import nrel_csm_2015

The OpenMDAO problem instance must also be assigned this model (nrel_csm_2015):

# OpenMDAO Problem instance
prob = om.Problem(reports=False)
prob.model = nrel_csm_2015()
prob.setup()

The model inputs remain the same:

# Initialize variables for NREL CSM
prob["machine_rating"] = 5000.0
prob["rotor_diameter"] = 126.0
prob["turbine_class"] = 2
prob["tower_length"] = 90.0
prob["blade_number"] = 3
prob["blade_has_carbon"] = False
prob["max_tip_speed"] = 80.0
prob["max_efficiency"] = 0.90
prob["main_bearing_number"] = 2
prob["crane"] = True

We can now run the model to compute the component masses and costs:

# Evaluate the model
prob.run_model()

Then we can again print out an exhaustive listing of the inputs and outputs:

# Print all intermediate inputs and outputs to the screen
prob.model.list_inputs(units=True)
prob.model.list_outputs(units=True)

The final screen output is:

>>>    turbine_c
>>>      turbine_mass_tcc                       [434686.14646457]   kg
>>>      turbine_cost                           [3543676.12253719]  USD
>>>      turbine_cost_kW                        [708.73522451]      USD/kW

See the full source for this example on Github.

Turbine Component Costs Using the NREL_CSM (2015)

As an example of estimating turbine component costs (only), if the component masses are already known, using the 2015 update of the NREL Cost and Scaling Model (CSM), let us simulate the NREL 5MW Reference Model [JBMS09].

The first step is to import OpenMDAO and the model itself:

import openmdao.api as om

from wisdem.nrelcsm.nrel_csm_cost_2015 import Turbine_CostsSE_2015

Next, we initialize an OpenMDAO instance and assign the model to be the Turbine_CostsSE_2015 module. This module has a configuration option to print to the screen a nicely formatted summary of the outputs, which is accessed by setting verbosity=True. The setup() command completes the high-level configuration readies the model for variable inpu:

# OpenMDAO Problem instance
prob = om.Problem(reports=False)
prob.model = Turbine_CostsSE_2015(verbosity=True)
prob.setup()

The turbine scaling relies on key turbine configuration parameters. These filter down to the individual component models through the rotor, nacelle, and tower as described on the Theory page. The variables are set like a Python dictionary:

# Initialize variables for NREL CSM
prob["machine_rating"] = 5000.0
prob["blade_number"] = 3
prob["crane"] = True
prob["main_bearing_number"] = 2

Next we set the individual component masses. These values might come from publicly available data, the other WISDEM modules, or through parametric study. In this example, we grab the masses computed in the previous example:

# Component masses
prob["blade_mass"] = 15751.48043042
prob["hub_mass"] = 37548.40498997
prob["pitch_system_mass"] = 9334.08947551
prob["spinner_mass"] = 973.0
prob["lss_mass"] = 20568.96284886
prob["main_bearing_mass"] = 2245.41649102
prob["gearbox_mass"] = 43468.32086769
prob["hss_mass"] = 994.7
prob["generator_mass"] = 14900.0
prob["bedplate_mass"] = 41765.26095285
prob["yaw_mass"] = 12329.96247921
prob["hvac_mass"] = 400.0
prob["cover_mass"] = 6836.69
prob["tower_mass"] = 182336.48057717
prob["transformer_mass"] = 11485.0
prob["platforms_mass"] = 8220.65761911

We can now run the model to compute the component costs:

# Evaluate the model
prob.run_model()

A formatted tabular output is printed to the screen:

>>> ######################################################################
>>> Computation of costs of the main turbine components from TurbineCostSE
>>> Blade cost              229.972 k USD       mass 15751.480 kg
>>> Pitch system cost       206.283 k USD       mass 9334.089 kg
>>> Hub cost                146.439 k USD       mass 37548.405 kg
>>> Spinner cost            10.800 k USD        mass 973.000 kg
>>> ---------------------------------------------------------------------
>>> Rotor cost              1053.437 k USD      mass 95109.936 kg
>>>
>>> LSS cost                244.771 k USD       mass 20568.963 kg
>>> Main bearing cost       10.104 k USD        mass 2245.416 kg
>>> Gearbox cost            560.741 k USD       mass 43468.321 kg
>>> HSS cost                6.764 k USD         mass 994.700 kg
>>> Generator cost          184.760 k USD       mass 14900.000 kg
>>> Bedplate cost           121.119 k USD       mass 41765.261 kg
>>> Yaw system cost         102.339 k USD       mass 12329.962 kg
>>> HVAC cost               49.600 k USD        mass 400.000 kg
>>> Nacelle cover cost      38.969 k USD        mass 6836.690 kg
>>> Electr connection cost  209.250 k USD
>>> Controls cost           105.750 k USD
>>> Other main frame cost   101.273 k USD
>>> Transformer cost        215.918 k USD       mass 11485.000 kg
>>> Converter cost          0.000 k USD         mass 0.000 kg
>>> ---------------------------------------------------------------------
>>> Nacelle cost            1961.463 k USD      mass 157239.730 kg
>>>
>>> Tower cost              528.776 k USD       mass 182336.481 kg
>>> ---------------------------------------------------------------------
>>> ---------------------------------------------------------------------
>>> Turbine cost            3543.676 k USD      mass 434686.146 kg
>>> Turbine cost per kW     708.735 k USD/kW
>>> ######################################################################

We can also print out an exhaustive listing of the inputs and outputs to each submodule:

# Print all intermediate inputs and outputs to the screen
prob.model.list_inputs(units=True)
prob.model.list_outputs(units=True)

See the full source for this example on Github.

Parametric Studies Using the NREL_CSM (2015)

The simplicity and rapid execution of the NREL CSM makes it well suited for parametric studies. This example runs approximately 6000 points in a Design of Experiment (DoE) parametric analysis varying machine rating, rotor diameter (and thereby hub_height), the blade mass scaling exponent, the average wind speed, and wind shear.

As above, the first step is to import OpenMDAO and the model itself, but we will also need other Python and WISDEM packages. In this case, the NumPy library and the annual energy production (AEP) estimator from the older (~2010) CSM code:

import numpy as np
import openmdao.api as om

from wisdem.nrelcsm.nrel_csm_orig import aep_csm
from wisdem.nrelcsm.nrel_csm_mass_2015 import nrel_csm_2015

Next, we initialize an OpenMDAO instance and assign the model to be the nrel_csm_2015 module. We also initialize an instance of the AEP model:

# OpenMDAO Problem instance
prob = om.Problem(reports=False)
prob.model = nrel_csm_2015()
prob.setup()

# Initialize AEP calculator from CSM model
aep_instance = aep_csm()

The CSM model initialization is abbreviated here because some of the variables will be modified within the DoE loop. The remaining ones are:

# Initialize variables for NREL CSM
prob["turbine_class"] = -1  # Sets blade mass based on user input, not auto-determined
prob["blade_number"] = 3
prob["blade_has_carbon"] = False
prob["max_tip_speed"] = max_tip_speed = 90.0
prob["max_efficiency"] = max_efficiency = 0.9
prob["main_bearing_number"] = 2
prob["crane"] = True

Note that the turbine_class variable has been set to -1 to allow us to override the blade_mass_exp value as described in the Source Documentation documentation. Also, two variables are jointly assigned to local Python variables for use in the AEP estimation. The AEP model requires a number of other inputs to define the turbine power curve. To keep things simple, we focus on a single turbine, and ignore many of the other losses and options:

# Initialize variables for AEP calculation
opt_tsr = 9.0  # Optimal tip speed ratio
max_Cp = 0.47  # Max (aerodynamic) power coefficient
max_Ct = 0.8  # Max thrust coefficient
max_eff = 0.95  # Drivetrain efficiency
cut_in = 4.0  # m/s
cut_out = 25.0  # m/s
altitude = 0.0  # m (assume sea level)
rho_air = 1.225  # kg/m^3
array_losses = 0.0  # Focusing on single turbine
availability = 1.0  # Assume 100% uptime
soiling_losses = 0.0  # Ignore this for now
turbine_number = 1  # Focus on single turbine
weibull_k = 2.0  # Weibull shape parameter

Next we define our parametric axes using NumPy’s arange function that provides evenly spaced intervals:

# Set Design of Experiment (DoE) parametric sweep bounds
machine_rating = 1e3 * np.arange(2.0, 10.1, 1.0)  # kW
rotor_diameter = np.arange(60, 201.0, 20.0)  # m
blade_mass_exp = np.arange(2.1, 2.41, 0.1)  # Relationship between blade length and mass
shear_exp = np.arange(0.1, 0.31, 0.1)
wind_speed = np.arange(5.0, 11.1, 1.0)  # m/s

To run our n-dimensional DOE, we do a “tensor” or “outer” multiplication of the arrays using NumPy’s meshgrid, but then flatten them into 1-D vectors for easy enumeration of all of the scenarios:

# Enumerate DoE through tensor multiplication and then flatten to get vector of all of the runs
[Rating, Diameter, Bladeexp, Shear, WindV] = np.meshgrid(
    machine_rating, rotor_diameter, blade_mass_exp, shear_exp, wind_speed
)

# Shift to flattened arrays to run through each scenario easily
Rating = Rating.flatten()
Diameter = Diameter.flatten()
Bladeexp = Bladeexp.flatten()
Shear = Shear.flatten()
WindV = WindV.flatten()

# Initialize output containers
tcc = np.zeros(Rating.shape)
aep = np.zeros(Rating.shape)

We are now ready to loop through all of the points, and evaluate the CSM model and AEP model:

# Calculation loop
npts = Rating.size
print("Running, ", npts, " points in the parametric study")
for k in range(npts):
    # Populate remaining NREL CSM inputs for this iteration
    prob["machine_rating"] = Rating[k]
    prob["rotor_diameter"] = Diameter[k]
    prob["blade_user_exp"] = Bladeexp[k]
    prob["tower_length"] = hub_height = 0.5 * Diameter[k] + 30.0

    # Compute Turbine capital cost using the NREL CSM (2015) and store the result
    prob.run_model()
    tcc[k] = float(prob["turbine_cost_kW"])

    # Compute AEP using original CSM function and store result
    aep_instance.compute(
        Rating[k],
        max_tip_speed,
        Diameter[k],
        max_Cp,
        opt_tsr,
        cut_in,
        cut_out,
        hub_height,
        altitude,
        rho_air,
        max_efficiency,
        max_Ct,
        soiling_losses,
        array_losses,
        availability,
        turbine_number,
        Shear[k],
        WindV[k],
        weibull_k,
    )
    aep[k] = aep_instance.aep.net_aep

    # Progress update to screen every 1000 iterations
    if np.mod(k, 1000) == 0:
        print("...Completed iteration, ", k)

To store for later postprocessing, we save everything into a large csv-file. Flattening the arrays makes this fairly straightforward using NumPy’s concatenation shortcuts:

# Write outputs to csv file for later post-processing
alldata = np.c_[Rating, Diameter, Bladeexp, Shear, WindV, tcc, aep]
header = "Rating [kW],Rotor Diam [m],Blade Mass Exp,Shear Exp,Wind Vel [m/s],TCC [USD/kW],AEP [kWh/yr]"
np.savetxt("parametric_scaling.csv", alldata, delimiter=",", header=header)

See the full source for this example on Github.

[JBMS09] (1,2)

J. Jonkman, S. Butterfield, W. Musial, and G. Scott. Definition of a 5-mw reference wind turbine for offshore system development. NREL/TP-500-38060, National Renewable Energy Laboratory, Golden, CO, February 2009.