How to Use WAVES#
This section will provide a guided overview of all the components of the Project
class that are
relevant to users, and demonstrate inputs used for the 2022 Cost of Wind Energy Review (COWER)
analysis. For a complete API reference, please refer to the API documentation.
Configuring#
The Project Class#
- class waves.project.Project(library_path, turbine_type, weather_profile, orbit_config=None, wombat_config=None, floris_config=None, landbosse_config=None, orbit_start_date=None, orbit_weather_cols=['windspeed', 'wave_height'], floris_windspeed='windspeed', floris_wind_direction='wind_direction', floris_turbulence_intensity=None, floris_x_col='floris_x', floris_y_col='floris_y', connect_floris_to_layout=True, connect_orbit_array_design=True, offtake_price=None, fixed_charge_rate=0.0582, environmental_loss_ratio=0.0159, discount_rate=None, finance_rate=None, reinvestment_rate=None, soft_capex_date=None, project_capex_date=None, system_capex_date=None, turbine_capex_date=None, report_config=None)[source]
The unified interface for creating, running, and assessing analyses that combine ORBIT, WOMBAT, and FLORIS.
- Parameters:
library_path (str | pathlib.Path) -- The file path where the configuration data for ORBIT, WOMBAT, and FLORIS can be found.
turbine_type (str) -- The type of wind turbine used. Must be one of "land", "fixed", or "floating".
weather_profile (str | pathlib.Path) --
The file path where the weather profile data is located, with the following column requirements:
datetime: The timestamp column
orbit_weather_cols: see
orbit_weather_cols
floris_windspeed: see
floris_windspeed
floris_wind_direction: see
floris_wind_direction
orbit_weather_cols (list[str]) -- The windspeed and wave height column names in
weather
to use for running ORBIT. Defaults to["windspeed", "wave_height"]
.floris_windspeed (str) -- The windspeed column in
weather
that will be used for the FLORIS wake analysis. Defaults to "windspeed_100m".floris_wind_direction (str) -- The wind direction column in
weather
that will be used for the FLORIS wake analysis. Defaults to "wind_direction_100m".floris_turbulence_intensity (str | None) -- The turbulence intensity column in
weather
that will be used for the FLORIS wake analysis. If None, then the input fromfloris_config
will be used. Defaults to "None".floris_x_col (str) -- The column of x-coordinates in the WOMBAT layout file that corresponds to the
Floris.farm.layout_x
Defaults to "floris_x".floris_y_col (str) -- The column of x-coordinates in the WOMBAT layout file that corresponds to the
Floris.farm.layout_y
Defaults to "floris_y".orbit_config (str | pathlib.Path | dict | None) -- The ORBIT configuration file name or dictionary. If None, will not set up the ORBIT simulation component.
landbosse_config (str | pathlib.Path | dict | None) -- The LandBOSSE configuration file name or dictionary. If None, will not set up the LandBOSSE simulation component.
wombat_config (str | pathlib.Path | dict | None) -- The WOMBAT configuration file name or dictionary. If None, will not set up the WOMBAT simulation component.
floris_config (str | pathlib.Path | dict | None) --
The FLORIS configuration file name or dictionary. If None, will not set up the FLORIS simulation component.
Note
The farm:trubine_library_path is automatically set to use the
library_path
/ turbines folder to maintain consistency.connect_floris_to_layout (bool, optional) --
If True, automatically connect the FLORIS and WOMBAT layout files, so that the simulation results can be linked. If False, don't connec the two models. Defaults to True.
Note
This should only be set to False if the FLORIS and WOMBAT layouts need to be connected in an additional step
connect_orbit_array_design (bool, optional) -- If True, the ORBIT array cable lengths will be calculated on initialization and added into the primary layout file.
offtake_price (float, optional) -- The price paid per MWh of energy produced. Defaults to None.
fixed_charge_rate (float, optional) -- Revenue per amount of investment required to cover the investment cost, with the default provided through the NREL 2021 Cost of Energy report [1]_. Defaults to 0.0582.
discount_rate (float, optional) -- The minimum acceptable rate of return, or the assumed return on an alternative investment of comparable risk. Defaults to None.
finance_rate (float, optional) -- Interest rate paid on the cash flows. Defaults to None.
reinvestment_rate (float, optional) -- Interest rate paid on the cash flows upon reinvestment. Defaults to None.
environmental_loss_ratio (float, optional) -- Percentage of environmental losses to deduct from the total energy production. Should be represented as a decimal in the range of [0, 1]. Defaults to 0.0159.
orbit_start_date (str | None) -- The date to use for installation phase start timings that are set to "0" in the
install_phases
configuration. If None the raw configuration data will be used. Defaults to None.soft_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT soft CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Noneproject_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT project CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Nonesystem_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT system CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.turbine_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT turbine CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.report_config (dict[str, dict], optional) -- A dictionary that can be passed to
generate_report()
, and be used as themetrics_configuration
dictionary. An additional field ofname
is required as input, which will be passed tosimulation_name
. Defaults to None.
References
-
turbine_type:
str
-
landbosse_config:
str
|Path
|dict
|None
-
floris_turbulence_intensity:
str
|None
-
environmental_loss_ratio:
float
-
report_config:
dict
[str
,dict
] |None
-
landbosse_config_dict:
dict
-
landbosse:
LandBOSSERunner
- validate_turbine_type(attribute, value)[source]
Validates that :py:attr`turbine_type` is one of "land", "fixed", or "floating".
- Return type:
None
- Parameters:
attribute (attrs.Attribute) -- The attrs Attribute information/metadata/configuration.
value (str) -- The user input.
- Raises:
ValueError -- Raised if not one of "land", "fixed", or "floating".
- validate_report_config(attribute, value)[source]
Validates the user input for
report_config
.- Return type:
None
- Parameters:
attribute (attrs.Attribute) -- The attrs Attribute information/metadata/configuration.
value (dict | None) -- _description_
- Raises:
ValueError -- Raised if
report_config
is not a dictionary.KeyError -- Raised if
report_config
does not contain a key, value pair for "name".
- check_consistent_config()[source]
Check the configurations of the models after they have been set up but before running the model to ensure the basicinputs to each model are consistent.
- Return type:
None
- setup_landbosse()[source]
Creates the LandBOSSE runner object and readies it for running an analysis.
- Return type:
None
- capex_orbit(*, breakdown=False)[source]
Provides a thin wrapper to ORBIT's
ProjectManager
CapEx calculations that can provide a breakdown of total or normalize it by the project's capacity, in MW.- Return type:
DataFrame
- Parameters:
breakdown (bool, optional) -- Provide a detailed view of the CapEx breakdown, and a total, which is the sum of the BOS, turbine, project, and soft CapEx categories. Defaults to False.
- Returns:
pd.DataFrame -- Project CapEx, normalized by
per_capacity
, if using, as either a pandas DataFrame ifbreakdown
is True, otherwise, a float total.
- capex_landbosse(*, breakdown=False)[source]
Calculates project CapEx from LandBOSSE result, in MW.
- Return type:
DataFrame
|float
- Parameters:
breakdown (bool, optional) -- Provide a detailed view of the CapEx breakdown, and a total, which is the sum of the BOS, turbine, project, and soft CapEx categories. Defaults to False.
- Returns:
pd.DataFrame | float -- Project CapEx, normalized by
per_capacity
, if using, as either a pandas DataFrame ifbreakdown
is True, otherwise, a float total.
- _get_floris_energy(which, frequency='project', by='windfarm', units='gw')[source]
- Return type:
DataFrame
|float
- Parameters:
which (str)
frequency (str)
by (str)
units (str)
- wake_losses(frequency='project', by='windfarm', units='kw', per_capacity=None, aep=False, ratio=False)[source]
Computes the wake losses, in GWh, for the simulation by extrapolating the monthly wake loss contributions to AEP if FLORIS (with wakes) results were computed by a wind rose, or using the time series results.
- Return type:
DataFrame
|float
- Parameters:
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the wake losses.
per_capacity (str, optional) -- Provide the wake losses normalized by the project's capacity, in the desired units. If None, then the unnormalized energy production is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
aep (bool, optional) -- Flag to return the wake losses normalized by the number of years the plan is in operation. Note that
frequency
must be "project" for this to be computed.ratio (bool, optional) -- Flag to return the wake loss ratio or the ratio of wake losses to the potential energy generation.
units (str)
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".- Returns:
pd.DataFrame | float -- The wind farm-level losses, in GWh, for the desired
frequency
.
- technical_loss_ratio()[source]
Calculate technical losses based on the project type.
This method is adopted from ORCA where a 1% hysterisis loss is applied for fixed-bottom turbines. For floating turbines, this is 1% hysterisis, 0.1% for onboard equipment, and 0.1% for rotor misalignment (0.01197901 total).
- Return type:
float
- Returns:
float -- The technical loss ratio.
- electrical_loss_ratio()[source]
Calculate electrical losses based on ORBIT parameters.
- Return type:
float
- Returns:
float -- The electrical loss ratio.
- generate_report_lcoe_breakdown()[source]
Generates a dataframe containing the detailed breakdown of LCOE (Levelized Cost of Energy) metrics for the project, which is used to produce LCOE waterfall charts and CapEx donut charts in the Cost of Wind Energy Review. The breakdown includes the contributions of each CapEx and OpEx component (from ORBIT and WOMBAT) to the LCOE in $/MWh.
This function calculates the LCOE by considering both CapEx (from ORBIT) and OpEx (from WOMBAT),and incorporates the fixed charge rate (FCR) and net annual energy production (net AEP) into the computation for each component.
- Return type:
DataFrame
- Returns:
pd.DataFrame --
- A DataFrame containing the detailed LCOE breakdown with the following columns:
"Component": The name of the project component (e.g., "Turbine", "Balance of System CapEx", "OpEx").
"Category": The category of the component (e.g., "Turbine", "Balance of System CapEx", "Financial CapEx", "OpEx").
"Value ($/kW)": The value of the component in $/kW.
"Fixed charge rate (FCR) (real)": The real fixed charge rate (FCR) applied to the component.
"Value ($/kW-yr)": The value of the component in $/kW-yr, after applying the FCR.
"Net AEP (MWh/kW/yr)": The net annual energy production (AEP) in MWh/kW/yr.
"Value ($/MWh)": The value of the component in $/MWh, calculated by dividing the $/kW-yr value by the net AEP.
Notes
- CapEx components are categorized into "Turbine", "Balance of System CapEx", and "Financial
CapEx".
OpEx components are derived from WOMBAT's OpEx metrics, categorized as "OpEx".
The LCOE is calculated by considering both CapEx and OpEx components, and adjusting for net AEP and FCR.
Rows with a value of 0 in the "Value ($/MWh)" column are removed to avoid clutter in annual reporting charts.
- generate_report_project_details()[source]
Generates a DataFrame containing detailed project information, following the format from the table at slide 64 in the Cost of Wind Energy Review: 2024 Edition (https://www.nrel.gov/docs/fy25osti/91775.pdf).
This function collects various project parameters such as turbine specifications, wind speed data,energy capture, and efficiency metrics, and formats them into a comprehensive report.
- Return type:
DataFrame
- Returns:
pd.DataFrame -- A DataFrame containing the project details, where each row corresponds to a specific assumption and its associated unit and value. The columns include:
"Assumption": Describes the specific project assumption (e.g., "Wind plant capacity","Turbine rating").
"Units": The unit of measurement for each assumption (e.g., "MW", "Number", "m/s").
- "Value": The actual value for each assumption, computed based on the project's
configurations and available data.
- determine_substructure_type()[source]
Determine the substructure type based on the ORBIT configuration file.
This function scans the "design_phases" section of the ORBIT configuration file to identify the substructure type used in the project. The substructure types considered are "Monopile", "SemiSubmersible", "Jacket", and "Spar". The function returns the substructure type as a string if found in the design phases, or "Unknown" if no substructure type is identified.
- Returns:
str -- The substructure type as one of the following: "Monopile", "SemiSubmersible", "Jacket", "Spar", or "Unknown" if no match is found.
Notes
The search is case-insensitive and looks for the substructure types as substrings within the design phase names.
If no substructure type is found after checking all phases, the function will return "Unknown".
- cut_in_windspeed()[source]
Determine the cut-in wind speed for the turbine based on the power-thrust table.
This function extracts the power and wind speed data from the turbine definitions in the FLORIS model and identifies the cut-in wind speed. The cut-in wind speed is the wind speed at which the turbine begins to produce power after zero power is achieved. The function returns the cut-in wind speed or None if no valid value can be determined.
- Returns:
float | None -- The wind speed (in m/s) at which the turbine starts producing power, or None if no valid cut-in wind speed can be found.
Notes
The cut-in wind speed is identified as the wind speed immediately following the last wind speed with zero power that is lower than the wind speed at which the turbine produces maximum power.
If no valid zero power wind speeds are found below the maximum power wind speed, None is returned.
- cut_out_windspeed()[source]
Determine the cut-out wind speed for the turbine based on the power-thrust table.
This function extracts the power and wind speed data from the turbine definitions in the FLORIS model and identifies the cut-out wind speed. The cut-out wind speed is the wind speed at which the turbine stops producing power, which occurs when the power drops to zero. The function returns the cut-out wind speed or None if no valid value can be determined.
- Returns:
float | None -- The wind speed (in m/s) at which the turbine stops producing power, or None if no valid cut-out wind speed can be found.
Notes
The cut-out wind speed is identified as the wind speed immediately preceding the first wind speed where the turbine generates zero power and the wind speed is greater than the wind speed at which maximum power is produced.
If no valid zero power wind speeds are found above the maximum power wind speed, None is returned.
- calculate_wind_speed(height)[source]
Calculates a new array, series, or value of wind speed at a given height.
- Return type:
Series
- Parameters:
height (int | float) -- The new height to calculate the wind speed.
- Returns:
pd.Series -- The wind speed data at :py:arg:`height`.
- average_wind_speed(height)[source]
Calculates the average wind speed at a specified height.
This method computes the mean wind speed from the weather data at the given height. If the wind speed data for the specified height is not found, and there are not at least two columns of wind speed data, an error is raised.
- Parameters:
height (int) -- The height (in meters) at which the average wind speed is to be calculated. The method expects the column in the weather data to be named in the format 'windspeed_{height}m'.
- Returns:
float | str -- If the wind speed data for the specified height exists, the method returns the mean wind speed as a float (in meters per second). If the data is not found, it returns a string indicating that the wind speed data is missing.
- Raises:
KeyError -- If the column corresponding to the specified height is not present in the weather data.
- identify_windspeed_columns_and_heights(df)[source]
Identifies columns containing wind speed measurements and their respective sensor heights.
Scans the DataFrame to find columns that match the pattern "windspeed_{number}m", where `{number}`corresponds to the sensor height in meters. The function returns a dictionary with the first two matches, where the keys are the column names and the values are the heights in meters.
- Parameters:
df (pandas.DataFrame) -- A DataFrame containing wind speed columns. The columns should follow the naming convention"windspeed_{number}m", where {number} represents the sensor height in meters.
- Returns:
dict[str, int] -- A dictionary containing the first two columns that match the "windspeed_{number}m" pattern. The keys are the column names (e.g., "windspeed_10m"), and the values are the corresponding sensor heights (e.g., 10).
- compute_weibull(height, random_seed=1)[source]
Fits a Weibull distribution to wind speed data at a specified height.
This function fits a Weibull distribution to the wind speed data at the specified height from the weather data. It assumes that wind speeds are non-negative, so it fixes the location parameter of the Weibull distribution to 0. If the required column for the given height is not present in the weather data, an error message is printed and the function returns a string indicating the missing data.
- Parameters:
height (int) -- The height (in meters) at which the wind speed data is collected. The function looks for a column named "windspeed_{height}m" in the weather data.
random_seed (int, optional) -- A random seed for reproducibility. Defaults to 1. This seed is used to initialize the random number generator for the Weibull fitting procedure.
- Returns:
float | str -- If the wind speed data is available for the specified height, the function returns the shape parameter of the fitted Weibull distribution. If the necessary column is missing from the weather data, it returns a string indicating the error (e.g., "wind speed at {height}m not provided").
Working With Configurations#
- classmethod Project.from_file(library_path, config_file)[source]
Creates a
Project
object from either a JSON or YAML file. SeeProject
for configuration requirements.- Return type:
- Parameters:
library_path (str | Path) -- The library path to be used in the simulation.
config_file (str | Path) -- The configuration file to create a
Project
object from, which should be located at:library_path
/ project / config /config_file
.
- Raises:
FileExistsError -- Raised if
library_path
is not a valid directory.ValueError -- Raised if
config_file
is not a JSON or YAML file.
- Returns:
Project -- An initialized Project object.
- property Project.config_dict: dict
Generates a configuration dictionary that can be saved to a new file for later re/use.
- Returns:
dict -- YAML-safe dictionary of a Project-loadable configuration.
- Project.save_config(config_file)[source]
Saves a copy of the Project configuration settings to recreate the results of the current settings.
- Return type:
None
- Parameters:
config_file (str | Path) -- The name to use for saving to a YAML configuration file.
COWER 2022 Configuration#
Aligning with COWER, we have the following inputs. It should be noted that each model's
configuration is a pointer to another file to keep each configuration as tidy as possible. However,
each of orbit_config
, wombat_config
, and floris_config
allow for a direct dictionary
configuration input.
1#project type, can be "fixed", "floating" or "land"
2turbine_type: fixed
3
4# Primary model configurations
5orbit_config: base_fixed_bottom_2022_install.yaml
6wombat_config: base_fixed_bottom_2022_operations.yaml
7floris_config: base_fixed_bottom_2022_floris_jensen.yaml
8weather_profile: era5_40.0N_72.5W_1990_2020.csv
9
10# Shared input connections
11orbit_start_date: 1/1/1998
12orbit_weather_cols:
13- windspeed_100m
14- windspeed_10m
15- waveheight
16floris_wind_direction: wind_direction_100m
17floris_windspeed: windspeed_100m
18floris_x_col: floris_x
19floris_y_col: floris_y
20
21# Create the necessary connections
22# NOTE: these are default values, but worth highlighting for an example
23connect_floris_to_layout: true
24conenct_orbit_array_design: true
25
26# High-level project financials
27discount_rate: 0.025
28fixed_charge_rate: 0.0648 # real FCR from national LCOE study, 25 year lifetime instead of 30
29loss_ratio: 0.1
30offtake_price: 83.30
31
32# Cash flow settings
33project_capex_date:
34- !!python/tuple
35 - 1996
36 - 1
...
43- !!python/tuple
...
46soft_capex_date: !!python/tuple
...
95- !!python/tuple
...
141- !!python/tuple
142 - 1999
143 - 7
Connecting Configurations#
Also, see the configurations Project.connect_floris_to_layout
and
Project.connect_orbit_array_design
to automatically run the below during project
initialization.
The following method is run on intialization when Project.connect_floris_to_layout
is set to
True
, which is the case in the COWER example.
- Project.connect_floris_to_turbines(x_col='floris_x', y_col='floris_y')[source]
Generates
floris_turbine_order
from the WOMBATWindfarm.layout_df
.- Parameters:
x_col (str, optional) -- The column name in the layout corresponding to the FLORIS x poszitions, by default "floris_x".
y_col (str, optional) -- The column name in the layout corresponding to the FLORIS y positions, by default "floris_y".
Visually, this looks like the following workflow:
Updating Configurations#
Sometimes, additional configurations may need to be connected prior to running an analysis. For
instance, it may be cumbersome to manually compute the FLORIS layout from a traditional coordinate
system, such as WGS-84, or a localized distance-based coordinate reference system. In the latter,
WAVES can help with the generate_floris_positions_from_layout
method. However, as can be seen
below, the following generation method has to be run prior to connecting the FLORIS and ORBIT/WOMBAT
layouts. As such, your workflow leading up to an analysis might look like the following.
from pathlib import Path
from waves import Project
from waves.utilities import load_yaml
library_path = Path("../library/base_2022/")
config = load_yaml(library_path / "project/config", "base_fixed_bottom_2022.yaml")
config["library_path"] = library_path # add the library path
# Ensure FLORIS is not automatically connected
config["connect_floris_to_turbines"] = False
project = Project.from_dict(config)
# Generate the layout and connect FLORIS
project.generate_floris_positions_from_layout() # note the defaults in the section below
project.connect_floris_to_turbines()
- Project.generate_floris_positions_from_layout(x_col='easting', y_col='northing', update_config=True, config_fname=None)[source]
Updates the FLORIS layout_x and layout_y based on the relative coordinates from the WOMBAT layout file.
- Return type:
None
- Parameters:
x_col (str, optional) -- The relative, distance-based x-coordinate column name. Defaults to "easting".
y_col (str, optional) -- The relative, distance-based y-coordinate column name. Defaults to "northing".
update_config (bool, optional) -- Run
FlorisInterface.reinitialize
with the updatedlayout_x
andlayout_y
values. Defaults to True.config_fname (str | None, optional) -- Provide a file name if
update_config
and this new configuration should be saved. Defaults to None.
The following method allows users to connect the ORBIT-calculated cable lengths and insert them back into the layout configuration file. This is helpful if the base distance is to be computed, then reused later, without re-calculating, or after modifying, if desired.
- Project.connect_orbit_cable_lengths(save_results=True)[source]
Runs the ORBIT design phases, so that the array system has computed the necessary cable length and distance measures, then attaches the cable length calculations back to the layout file, saves the results to the layout files, and reloads both ORBIT and WOMBAT with this data.
- Return type:
None
- Parameters:
save_results (bool, optional) -- Save the resulting, updated layout table to both
library_path
/project/plant/wombat_config_dict["layout"]
andlibrary_path
/cables/wombat_config_dict["layout"]
for WOMBAT and ORBIT compatibility, respectively.
- Project.reinitialize(orbit_config=None, landbosse_config=None, wombat_config=None, floris_config=None)[source]
Enables a user to reinitialize one or multiple of the CapEx, OpEx, and AEP models.
- Return type:
None
- Parameters:
orbit_config (str | Path | dict | None, optional) -- ORBIT configuration file or dictionary. Defaults to None.
landbosse_config ((str | Path | dict | None, optional) -- LandBOSSE configuration file or dictionary. Defaults to None.
wombat_config (str | Path | dict | None, optional) -- WOMBAT configuation file or dictionary. Defaults to None.
floris_config ((str | Path | dict | None, optional) -- FLORIS configuration file or dictionary. Defaults to None.
- Raises:
RuntimeError -- Raised if neither ORBIT nor LandBOSSE are configured to run.
Viewing the Wind Farm Properties#
For the following set of methods, users only need to create a Project
object in order to use.
- Project.plot_farm(figure_kwargs=None, draw_kwargs=None, return_fig=False)[source]
Plot the graph representation of the windfarm as represented through WOMBAT.
- Return type:
None
|tuple
[figure
,axes
]- Parameters:
figure_kwargs (dict | None, optional) -- Customized keyword arguments for matplotlib figure instantiation that will passed as
plt.figure(**figure_kwargs)
. Defaults to None.draw_kwargs (dict | None, optional) -- Customized keyword arguments for
networkx.draw()
that can will passed asnx.draw(**figure_kwargs)
. Defaults to None.return_fig (bool, optional) -- Whether or not to return the figure and axes objects for further editing and/or saving. Defaults to False.
- Returns:
None | tuple[plt.figure, plt.axes] -- If
return_fig
is False, then None is returned, otherwise (True) theFigure
andAxes
objects are returned.
- Project.determine_substructure_type()[source]
Determine the substructure type based on the ORBIT configuration file.
This function scans the "design_phases" section of the ORBIT configuration file to identify the substructure type used in the project. The substructure types considered are "Monopile", "SemiSubmersible", "Jacket", and "Spar". The function returns the substructure type as a string if found in the design phases, or "Unknown" if no substructure type is identified.
- Returns:
str -- The substructure type as one of the following: "Monopile", "SemiSubmersible", "Jacket", "Spar", or "Unknown" if no match is found.
Notes
The search is case-insensitive and looks for the substructure types as substrings within the design phase names.
If no substructure type is found after checking all phases, the function will return "Unknown".
- Project.n_turbines()[source]
Returns the number of turbines from either ORBIT, WOMBAT, or FLORIS depending on which model is available internally.
- Return type:
int
- Returns:
int -- The number of turbines in the project.
- Raises:
RuntimeError -- Raised if no model configurations were provided on initialization.
- Project.n_substations()[source]
Calculates the number of substations in the project.
- Return type:
int
- Returns:
int -- The number of substations in the project.
- Project.capacity(units='mw')[source]
Calculates the project's capacity in the desired units of kW, MW, or GW.
- Return type:
float
- Parameters:
units (str, optional) -- One of "kw", "mw", or "gw". Defaults to "mw".
- Returns:
float -- The project capacity, returned in the desired units
- Raises:
RuntimeError -- Raised if no model configurations were provided on initialization.
- Project.turbine_rating()[source]
Calculates the average turbine rating, in MW, of all the turbines in the project.
- Return type:
float
- Returns:
float -- The average rating of the turbines, in MW.
- Raises:
RuntimeError -- Raised if no model configurations were provided on initialization.
- Project.cut_in_windspeed()[source]
Determine the cut-in wind speed for the turbine based on the power-thrust table.
This function extracts the power and wind speed data from the turbine definitions in the FLORIS model and identifies the cut-in wind speed. The cut-in wind speed is the wind speed at which the turbine begins to produce power after zero power is achieved. The function returns the cut-in wind speed or None if no valid value can be determined.
- Returns:
float | None -- The wind speed (in m/s) at which the turbine starts producing power, or None if no valid cut-in wind speed can be found.
Notes
The cut-in wind speed is identified as the wind speed immediately following the last wind speed with zero power that is lower than the wind speed at which the turbine produces maximum power.
If no valid zero power wind speeds are found below the maximum power wind speed, None is returned.
- Project.cut_out_windspeed()[source]
Determine the cut-out wind speed for the turbine based on the power-thrust table.
This function extracts the power and wind speed data from the turbine definitions in the FLORIS model and identifies the cut-out wind speed. The cut-out wind speed is the wind speed at which the turbine stops producing power, which occurs when the power drops to zero. The function returns the cut-out wind speed or None if no valid value can be determined.
- Returns:
float | None -- The wind speed (in m/s) at which the turbine stops producing power, or None if no valid cut-out wind speed can be found.
Notes
The cut-out wind speed is identified as the wind speed immediately preceding the first wind speed where the turbine generates zero power and the wind speed is greater than the wind speed at which maximum power is produced.
If no valid zero power wind speeds are found above the maximum power wind speed, None is returned.
- Project.identify_windspeed_columns_and_heights(df)[source]
Identifies columns containing wind speed measurements and their respective sensor heights.
Scans the DataFrame to find columns that match the pattern "windspeed_{number}m", where `{number}`corresponds to the sensor height in meters. The function returns a dictionary with the first two matches, where the keys are the column names and the values are the heights in meters.
- Parameters:
df (pandas.DataFrame) -- A DataFrame containing wind speed columns. The columns should follow the naming convention"windspeed_{number}m", where {number} represents the sensor height in meters.
- Returns:
dict[str, int] -- A dictionary containing the first two columns that match the "windspeed_{number}m" pattern. The keys are the column names (e.g., "windspeed_10m"), and the values are the corresponding sensor heights (e.g., 10).
- Project.calculate_wind_speed(height)[source]
Calculates a new array, series, or value of wind speed at a given height.
- Return type:
Series
- Parameters:
height (int | float) -- The new height to calculate the wind speed.
- Returns:
pd.Series -- The wind speed data at :py:arg:`height`.
- Project.average_wind_speed(height)[source]
Calculates the average wind speed at a specified height.
This method computes the mean wind speed from the weather data at the given height. If the wind speed data for the specified height is not found, and there are not at least two columns of wind speed data, an error is raised.
- Parameters:
height (int) -- The height (in meters) at which the average wind speed is to be calculated. The method expects the column in the weather data to be named in the format 'windspeed_{height}m'.
- Returns:
float | str -- If the wind speed data for the specified height exists, the method returns the mean wind speed as a float (in meters per second). If the data is not found, it returns a string indicating that the wind speed data is missing.
- Raises:
KeyError -- If the column corresponding to the specified height is not present in the weather data.
- Project.compute_weibull(height, random_seed=1)[source]
Fits a Weibull distribution to wind speed data at a specified height.
This function fits a Weibull distribution to the wind speed data at the specified height from the weather data. It assumes that wind speeds are non-negative, so it fixes the location parameter of the Weibull distribution to 0. If the required column for the given height is not present in the weather data, an error message is printed and the function returns a string indicating the missing data.
- Parameters:
height (int) -- The height (in meters) at which the wind speed data is collected. The function looks for a column named "windspeed_{height}m" in the weather data.
random_seed (int, optional) -- A random seed for reproducibility. Defaults to 1. This seed is used to initialize the random number generator for the Weibull fitting procedure.
- Returns:
float | str -- If the wind speed data is available for the specified height, the function returns the shape parameter of the fitted Weibull distribution. If the necessary column is missing from the weather data, it returns a string indicating the error (e.g., "wind speed at {height}m not provided").
Running the Models#
- Project.run(floris_kwargs=None, full_wind_rose=False, skip=None)[source]
Run all three models in serial, or a subset if
skip
is used.- Return type:
None
- Parameters:
floris_kwargs (dict | None) -- Any additional
FlorisModel.set
keyword arguments. Defaults to None.full_wind_rose (bool, optional) -- Indicates, for "wind_rose" analyses ONLY, if the full weather profile from
weather
(True) or the limited, WOMBAT simulation period (False) should be used for analyis. Defaults to False.skip (list[str] | None, optional) -- A list of models to be skipped. This is intended to be used after a model is reinitialized with a new or modified configuration. Defaults to None.
Results#
Visually, the following is a general flow of operations for combining each model's outputs:
To quickly produce any of the high-level outputs to a single DataFrame
, the below method can be
used in place of individually calculating each metric and combining into a report. Additionally,
users can refer to the COWER 2022 example for the reported results,
which relies on the generate_report
method and accessing the ORBIT ProjectManager
directly for
further CapEx breakdowns.
- Project.generate_report(metrics_configuration=None, simulation_name=None)[source]
Generates a single row dataframe of all the desired resulting metrics from the project. :rtype:
DataFrame
Note
This assumes all results will be a single number, and not a Pandas
DataFrame
- Parameters:
metrics_dict (dict[str, dict], optional) --
The dictionary of dictionaries containing the following key, value pair pattern:
{ "Descriptive Name (units)": { "metric": "metric_method_name", "kwargs": {"kwarg1": "kwarg_value_1"} # Exclude if not needed } }
For metrics that have no keyword arguments, or where the default parameter values are desired, either an empty dictionary or no dictionary input for "kwargs" is allowed. If no input is provided, then
report_config
will be used to populatesimulation_name (str) -- The name that should be given to the resulting index.
metrics_configuration (dict[str, dict] | None)
- Returns:
pd.DataFrame -- A pandas.DataFrame containing all of the provided outputs defined in
metrics_dict
.- Raises:
ValueError -- Raised if any of the keys of
metrics_dict
aren't implemented methods.- Return type:
DataFrame
All the models can be individually accessed to calculate results that are not integrated into WAVES officially, but it should be noted that they should be used with caution in case there are any interdependencies between model outputs.
-
Project.orbit:
ProjectManager
-
Project.wombat:
Simulation
-
Project.floris:
FlorisModel
Balance of Systems Costs and Properties#
- Project.capex(breakdown=False, per_capacity=None)[source]
Calculate capital expenditure (CapEx) using ORBIT or LandBOSSE outputs.
- Return type:
DataFrame
|float
- Parameters:
breakdown (bool, optional) -- Provide a detailed view of the CapEx breakdown, and a total, which is the sum of the BOS, turbine, project, and soft CapEx categories. Defaults to False.
per_capacity (str, optional) -- Provide the CapEx normalized by the project's capacity, in the desired units. If None, then the unnormalized CapEx is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
- Raises:
RuntimeError -- Raised if neither ORBIT nor LandBOSSE have been run.
- Returns:
pd.DataFrame | float -- Project CapEx, normalized by
per_capacity
, if using, as either a pandas DataFrame ifbreakdown
is True, otherwise, a float total.
- Project.capex_breakdown(frequency='month-year', installation_start_date=None, soft_capex_date=None, project_capex_date=None, system_capex_date=None, turbine_capex_date=None, breakdown=False)[source]
Calculates the monthly CapEx breakdwon into a DataFrame, that is returned at the desired frequency, allowing for custom starting dates for the varying CapEx costs.
- Return type:
DataFrame
- Parameters:
frequency (str, optional) -- The desired frequency of the outputs, where "month-year" is the monthly total over the course of a project's life. Must be one of: "project", "annual", "month-year". Defaults to "month-year"
installation_start_date (str | None, optional) -- If not provided in the
Project
configuration asorbit_start_date
, an installation starting date that is parseable from a string by Pandas may be provided here. Defaults to Nonesoft_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT soft CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Noneproject_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT project CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Nonesystem_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT system CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.turbine_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT turbine CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.offtake_price (int | float | None, optional) -- The price paid for the energy produced, by default None.
breakdown (bool, optional) -- If True, all the CapEx categories will be provided as a column, in addition to the OpEx and Revenue columns, and the total cost in "cash_flow", othwerwise, only the "cash_flow" column will be provided. Defaults to False.
- Returns:
pd.DataFrame -- Returns the pandas DataFrame of the cashflow with a fixed monthly or annual interval, or a project total for the desired categories.
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".TypeError -- Raised if a valid starting date can't be found for the installation.
- Project.array_system_total_cable_length()[source]
Calculates the total length of the cables in the array system, in km.
- Returns:
float -- Total length, in km, of the array system cables.
- Raises:
ValueError -- Raised if neither
ArraySystemDesign
norCustomArraySystem
design were created in ORBIT.RuntimeError -- Raised if neither ORBIT nor LandBOSSE have been run.
- Project.export_system_total_cable_length()[source]
Calculates the total length of the cables in the export system, in km.
- Returns:
float -- Total length, in km, of the export system cables.
- Raises:
ValueError -- Raised if
ExportSystemDesign
was not created in ORBIT.RuntimeError -- Raised if neither ORBIT nor LandBOSSE have been run.
Operations and Maintenance Costs#
- Project.opex(frequency='project', per_capacity=None)[source]
Calculates the operational expenditures of the project.
- Return type:
DataFrame
|float
- Parameters:
(str (frequency) -- Defaults to "project".
optional) (One of "project", "annual", "monthly", "month-year".) -- Defaults to "project".
per_capacity (str, optional) -- Provide the OpEx normalized by the project's capacity, in the desired units. If None, then the unnormalized OpEx is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
frequency (str)
- Returns:
pd.DataFrame | float -- The resulting OpEx DataFrame at the desired frequency, if more granular than the project frequency, otherwise a float. This will be normalized by the capacity, if
per_capacity
is not None.
- Project.availability(which, frequency='project', by='windfarm')[source]
Calculates the availability based on either a time or energy basis. This is a thin wrapper around self.wombat.metrics.time_based_availability() or self.wombat.metrics.production_based_availability().
- Return type:
DataFrame
|float
- Parameters:
which (str) -- One of "energy" or "time" to indicate which basis to use for the availability calculation. For "energy", this indicates the operating capacity of the project, and for "time", this is the ratio of any level operational to all time.
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the availability.
- Returns:
pd.DataFrame | float -- The appropriate availability metric, as a DataFrame unless it's calculated at the project/windfarm level.
- Raises:
ValueError -- Raised if
which
is not one of "energy" or "time".
- Project.capacity_factor(which, frequency='project', by='windfarm', environmental_loss_ratio=None)[source]
Calculates the capacity factor over a project's lifetime as a single value, annual average, or monthly average for the whole windfarm or by turbine.
- Return type:
DataFrame
|float
- Parameters:
which (str) -- One of "net" (realized energy / capacity) or "gross" (potential energy production / capacity).
frequency (str) -- One of "project", "annual", "monthly", or "month-year". Defaults to "project".
by (str) --
One of "windfarm" or "turbine". Defaults to "windfarm".
Note
This will only be checked for
which
= "net".environmental_loss_ratio (float, optional) -- The decimal environmental loss ratio to apply to the energy production. If None, then it will attempt to use the
environmental_loss_ratio
provided in the Project configuration. Defaults to 0.0159.
- Returns:
pd.DataFrame | float -- The capacity factor at the desired aggregation level.
Energy Production#
- Project.energy_potential(frequency='project', by='windfarm', units='gw', per_capacity=None, aep=False)[source]
Computes the potential energy production, or annual potential energy production, in GWh, for the simulation by extrapolating the monthly contributions to AEP if FLORIS (without wakes) results were computed by a wind rose, or using the time series results.
- Return type:
DataFrame
|float
- Parameters:
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the energy production.
units (str, optional) -- One of "gw", "mw", or "kw" to determine the units for energy production.
per_capacity (str, optional) -- Provide the energy production normalized by the project's capacity, in the desired units. If None, then the unnormalized energy production is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
aep (bool, optional) -- Flag to return the energy production normalized by the number of years the plan is in operation. Note that
frequency
must be "project" for this to be computed.
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".- Returns:
pd.DataFrame | float -- The wind farm-level energy prodcution, in GWh, for the desired
frequency
.
- Project.energy_production(frequency='project', by='windfarm', units='gw', per_capacity=None, environmental_loss_ratio=None, aep=False)[source]
Computes the energy production, or annual energy production.
To compute the losses, the total loss ratio from :py:method:`loss_ratio` is applied to each time step (e.g., month for month-year) rather than using each time step's losses. :rtype:
DataFrame
|float
This lower resolution apporach to turbine and time step energy stems from the nature of the loss method itself, which is intended for farm-level results.
- Parameters:
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the energy production.
units (str, optional) -- One of "gw", "mw", or "kw" to determine the units for energy production.
per_capacity (str, optional) -- Provide the energy production normalized by the project's capacity, in the desired units. If None, then the unnormalized energy production is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
environmental_loss_ratio (float, optional) -- The decimal environmental loss ratio to apply to the energy production. If None, then it will attempt to use the
environmental_loss_ratio
provided in the Project configuration. Defaults to 0.0159.aep (bool, optional) -- Flag to return the energy production normalized by the number of years the plan is in operation. Note that
frequency
must be "project" for this to be computed.
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".- Returns:
pd.DataFrame | float -- The wind farm-level energy prodcution, in GWh, for the desired
frequency
.- Return type:
DataFrame | float
- Project.energy_losses(frequency='project', by='windfarm', units='gw', per_capacity=None, environmental_loss_ratio=None, aep=False)[source]
Computes the energy losses for the simulation by subtracting the energy production from the potential energy production.
To compute the losses, the total loss ratio from :py:method:`loss_ratio` is applied to each time step (e.g., month for month-year) rather than using each time step's losses. :rtype:
DataFrame
This lower resolution apporach to turbine and time step energy stems from the nature of the loss method itself, which is intended for farm-level results.
- Parameters:
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the energy production.
units (str, optional) -- One of "gw", "mw", or "kw" to determine the units for energy production.
per_capacity (str, optional) -- Provide the energy production normalized by the project's capacity, in the desired units. If None, then the unnormalized energy production is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
environmental_loss_ratio (float, optional) -- The decimal environmental loss ratio to apply to the energy production. If None, then it will attempt to use the
environmental_loss_ratio
provided in the Project configuration. Defaults to 0.0159.aep (bool, optional) -- AEP for the annualized losses. Only used for
frequency
= "project".
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".- Returns:
pd.DataFrame | float -- The wind farm-level energy prodcution, in GWh, for the desired
frequency
.- Return type:
DataFrame
- Project.loss_ratio(environmental_loss_ratio=None, breakdown=False)[source]
Calculate total losses based on environmental, availability, wake, technical, and electrical losses. :rtype:
DataFrame
|float
Note
This method treats different types of losses as efficiencies and is applied
as in Equation 1 from Beiter et al. 2020 (https://www.nrel.gov/docs/fy21osti/77384.pdf).
- Parameters:
environmental_loss_ratio (float, optional) -- The decimal environmental loss ratio to apply to the energy production. If None, then it will attempt to use the
environmental_loss_ratio
provided in the Project configuration. Defaults to 0.0159.breakdown (bool, optional) -- Flag to return the losses breakdown including environmental, availability, wake, technical, electrical losses, and total losses.
- Returns:
float | pd.DataFrame -- The total loss ratio, or the DataFrame of all loss ratios.
- Return type:
DataFrame | float
- Project.wake_losses(frequency='project', by='windfarm', units='kw', per_capacity=None, aep=False, ratio=False)[source]
Computes the wake losses, in GWh, for the simulation by extrapolating the monthly wake loss contributions to AEP if FLORIS (with wakes) results were computed by a wind rose, or using the time series results.
- Return type:
DataFrame
|float
- Parameters:
frequency (str, optional) -- One of "project" (project total), "annual" (annual total), or "month-year" (monthly totals for each year).
by (str, optional) -- One of "windfarm" (project level) or "turbine" (turbine level) to indicate what level to calculate the wake losses.
per_capacity (str, optional) -- Provide the wake losses normalized by the project's capacity, in the desired units. If None, then the unnormalized energy production is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
aep (bool, optional) -- Flag to return the wake losses normalized by the number of years the plan is in operation. Note that
frequency
must be "project" for this to be computed.ratio (bool, optional) -- Flag to return the wake loss ratio or the ratio of wake losses to the potential energy generation.
units (str)
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".- Returns:
pd.DataFrame | float -- The wind farm-level losses, in GWh, for the desired
frequency
.
- Project.technical_loss_ratio()[source]
Calculate technical losses based on the project type.
This method is adopted from ORCA where a 1% hysterisis loss is applied for fixed-bottom turbines. For floating turbines, this is 1% hysterisis, 0.1% for onboard equipment, and 0.1% for rotor misalignment (0.01197901 total).
- Return type:
float
- Returns:
float -- The technical loss ratio.
Project Financials#
- Project.capex(breakdown=False, per_capacity=None)[source]
Calculate capital expenditure (CapEx) using ORBIT or LandBOSSE outputs.
- Return type:
DataFrame
|float
- Parameters:
breakdown (bool, optional) -- Provide a detailed view of the CapEx breakdown, and a total, which is the sum of the BOS, turbine, project, and soft CapEx categories. Defaults to False.
per_capacity (str, optional) -- Provide the CapEx normalized by the project's capacity, in the desired units. If None, then the unnormalized CapEx is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
- Raises:
RuntimeError -- Raised if neither ORBIT nor LandBOSSE have been run.
- Returns:
pd.DataFrame | float -- Project CapEx, normalized by
per_capacity
, if using, as either a pandas DataFrame ifbreakdown
is True, otherwise, a float total.
- Project.capex_breakdown(frequency='month-year', installation_start_date=None, soft_capex_date=None, project_capex_date=None, system_capex_date=None, turbine_capex_date=None, breakdown=False)[source]
Calculates the monthly CapEx breakdwon into a DataFrame, that is returned at the desired frequency, allowing for custom starting dates for the varying CapEx costs.
- Return type:
DataFrame
- Parameters:
frequency (str, optional) -- The desired frequency of the outputs, where "month-year" is the monthly total over the course of a project's life. Must be one of: "project", "annual", "month-year". Defaults to "month-year"
installation_start_date (str | None, optional) -- If not provided in the
Project
configuration asorbit_start_date
, an installation starting date that is parseable from a string by Pandas may be provided here. Defaults to Nonesoft_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT soft CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Noneproject_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT project CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Nonesystem_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT system CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.turbine_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT turbine CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.offtake_price (int | float | None, optional) -- The price paid for the energy produced, by default None.
breakdown (bool, optional) -- If True, all the CapEx categories will be provided as a column, in addition to the OpEx and Revenue columns, and the total cost in "cash_flow", othwerwise, only the "cash_flow" column will be provided. Defaults to False.
- Returns:
pd.DataFrame -- Returns the pandas DataFrame of the cashflow with a fixed monthly or annual interval, or a project total for the desired categories.
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".TypeError -- Raised if a valid starting date can't be found for the installation.
- Project.opex(frequency='project', per_capacity=None)[source]
Calculates the operational expenditures of the project.
- Return type:
DataFrame
|float
- Parameters:
(str (frequency) -- Defaults to "project".
optional) (One of "project", "annual", "monthly", "month-year".) -- Defaults to "project".
per_capacity (str, optional) -- Provide the OpEx normalized by the project's capacity, in the desired units. If None, then the unnormalized OpEx is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
frequency (str)
- Returns:
pd.DataFrame | float -- The resulting OpEx DataFrame at the desired frequency, if more granular than the project frequency, otherwise a float. This will be normalized by the capacity, if
per_capacity
is not None.
- Project.revenue(frequency='project', offtake_price=None, per_capacity=None)[source]
Calculates the revenue stream using the WOMBAT availabibility, FLORIS energy production, and WAVES energy pricing.
- Return type:
DataFrame
|float
- Parameters:
frequency (str, optional) -- One of "project", "annual", "monthly", or "month-year". Defaults to "project".
offtake_price (float, optional) -- Price paid per MWh of energy produced. Defaults to None.
per_capacity (str, optional) -- Provide the revenue normalized by the project's capacity, in the desired units. If None, then the unnormalized revenue is returned, otherwise it must be one of "kw", "mw", or "gw". Defaults to None.
- Returns:
pd.DataFrame | float -- The revenue stream of the wind farm at the provided frequency.
- Project.cash_flow(frequency='month-year', installation_start_date=None, soft_capex_date=None, project_capex_date=None, system_capex_date=None, turbine_capex_date=None, offtake_price=None, breakdown=False)[source]
Calculates the monthly cashflows into a DataFrame, that is returned at the desired frequency, and with or without a high level breakdown, allowing for custom starting dates for the varying CapEx costs.
- Return type:
DataFrame
- Parameters:
frequency (str, optional) -- The desired frequency of the outputs, where "month-year" is the monthly total over the course of a project's life. Must be one of: "project", "annual", "month-year". Defaults to "month-year"
installation_start_date (str | None, optional) -- If not provided in the
Project
configuration asorbit_start_date
, an installation starting date that is parseable from a string by Pandas may be provided here. Defaults to Nonesoft_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT soft CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Noneproject_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT project CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to Nonesystem_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT system CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.turbine_capex_date (tuple[int, int] | list[tuple[int, int]] | None, optional) -- The date(s) where the ORBIT turbine CapEx costs should be applied as a tuple of year and month, for instance:
(2020, 1)
for January 2020. Alternatively multiple dates can be set, which evenly divides the cost over all the dates, by providing a list of year and month combinations, for instance, a semi-annual 2 year cost starting in 2020 would look like:[(2020, 1), (2020, 7), (2021, 1), (2021, 7)]
. If None is provided, then the CapEx date will be the same as the start of the installation. Defaults to None.offtake_price (int | float | None, optional) -- The price paid for the energy produced, by default None.
breakdown (bool, optional) -- If True, all the CapEx categories will be provided as a column, in addition to the OpEx and Revenue columns, and the total cost in "cash_flow", othwerwise, only the "cash_flow" column will be provided. Defaults to False.
- Returns:
pd.DataFrame -- Returns the pandas DataFrame of the cashflow with a fixed monthly or annual interval, or a project total for the desired categories.
- Raises:
ValueError -- Raised if
frequency
is not one of: "project", "annual", "month-year".TypeError -- Raised if a valid starting date can't be found for the installation.
- Project.npv(frequency='project', discount_rate=None, offtake_price=None, cash_flow=None, **kwargs)[source]
Calculates the net present value of the windfarm at a project, annual, or monthly resolution given a base discount rate and offtake price. :rtype:
DataFrame
Note
NPV is implemented via https://numpy.org/numpy-financial/latest/npv.html#numpy_financial.npv.
- Parameters:
frequency (str) -- One of "project", "annual", "monthly", or "month-year".
discount_rate (float, optional) -- The rate of return that could be earned on alternative investments. Defaults to None.
offtake_price (float, optional) -- Price of energy, per MWh. Defaults to None.
cash_flow (pd.DataFrame, optional) -- A modified cash flow DataFrame for custom workflows. Must have the "cash_flow" column with consistent time steps (monthly, annually, etc.). Defaults to None.
kwargs (dict, optional) -- See
cash_flow()
for details on starting date options.
- Returns:
pd.DataFrame -- The project net prsent value at the desired time resolution.
- Return type:
DataFrame
- Project.irr(offtake_price=None, finance_rate=None, reinvestment_rate=None, cash_flow=None, **kwargs)[source]
Calculates the Internal Rate of Return using the ORBIT CapEx as the initial investment in conjunction with the WAVES monthly cash flows. :rtype:
float
Note
This method allows for the caluclation of the modified internal rate of return through https://numpy.org/numpy-financial/latest/mirr.html#numpy_financial.mirr if both the
finance_rate
and thereinvestment_rate
are provided.- Parameters:
offtake_price (float, optional) -- Price of energy, per MWh. Defaults to None.
finance_rate (float, optional) -- Interest rate paid on the cash flows. Only used if
reinvestment_rate
is also provided. Defaults to None.reinvestment_rate (float, optional) -- Interest rate received on the cash flows upon reinvestment. Only used if
finance_rate
is also provided.cash_flow (pd.DataFrame, optional) -- A modified cash flow DataFrame for custom workflows. Must have the "cash_flow" column with consistent time steps (monthly, annually, etc.). Defaults to None.
kwargs (dict, optional) -- See
cash_flow()
for details on starting date options.
- Returns:
float -- The IRR.
- Return type:
float
- Project.lcoe(fixed_charge_rate=None, capex=None, opex=None, aep=None)[source]
Calculates the levelized cost of energy (LCOE) as the following: LCOE = (CapEx * FCR + OpEx) / AEP, in $/MWh.
- Return type:
float
- Parameters:
fixed_charge_rate (float, optional) -- Revenue per amount of investment required to cover the investment cost. Required if no value was provided in the
Project
configuration. Defaults to Nonecapex (float, optional) -- Custom CapEx value, in $/kW. Defaults to None.
opex (float, optional) -- Custom OpEx value, in $/kW/year. Defaults to None.
aep (float, optional) -- Custom AEP value, in MWh/MW/year. Defaults to None.
- Returns:
float -- The levelized cost of energy.
- Raises:
ValueError -- Raised if the input to
units
is not one of "kw", "mw", or "gw".
Report Generation#
- Project.generate_report_project_details()[source]
Generates a DataFrame containing detailed project information, following the format from the table at slide 64 in the Cost of Wind Energy Review: 2024 Edition (https://www.nrel.gov/docs/fy25osti/91775.pdf).
This function collects various project parameters such as turbine specifications, wind speed data,energy capture, and efficiency metrics, and formats them into a comprehensive report.
- Return type:
DataFrame
- Returns:
pd.DataFrame -- A DataFrame containing the project details, where each row corresponds to a specific assumption and its associated unit and value. The columns include:
"Assumption": Describes the specific project assumption (e.g., "Wind plant capacity","Turbine rating").
"Units": The unit of measurement for each assumption (e.g., "MW", "Number", "m/s").
- "Value": The actual value for each assumption, computed based on the project's
configurations and available data.
- Project.generate_report(metrics_configuration=None, simulation_name=None)[source]
Generates a single row dataframe of all the desired resulting metrics from the project. :rtype:
DataFrame
Note
This assumes all results will be a single number, and not a Pandas
DataFrame
- Parameters:
metrics_dict (dict[str, dict], optional) --
The dictionary of dictionaries containing the following key, value pair pattern:
{ "Descriptive Name (units)": { "metric": "metric_method_name", "kwargs": {"kwarg1": "kwarg_value_1"} # Exclude if not needed } }
For metrics that have no keyword arguments, or where the default parameter values are desired, either an empty dictionary or no dictionary input for "kwargs" is allowed. If no input is provided, then
report_config
will be used to populatesimulation_name (str) -- The name that should be given to the resulting index.
metrics_configuration (dict[str, dict] | None)
- Returns:
pd.DataFrame -- A pandas.DataFrame containing all of the provided outputs defined in
metrics_dict
.- Raises:
ValueError -- Raised if any of the keys of
metrics_dict
aren't implemented methods.- Return type:
DataFrame
- Project.generate_report_lcoe_breakdown()[source]
Generates a dataframe containing the detailed breakdown of LCOE (Levelized Cost of Energy) metrics for the project, which is used to produce LCOE waterfall charts and CapEx donut charts in the Cost of Wind Energy Review. The breakdown includes the contributions of each CapEx and OpEx component (from ORBIT and WOMBAT) to the LCOE in $/MWh.
This function calculates the LCOE by considering both CapEx (from ORBIT) and OpEx (from WOMBAT),and incorporates the fixed charge rate (FCR) and net annual energy production (net AEP) into the computation for each component.
- Return type:
DataFrame
- Returns:
pd.DataFrame --
- A DataFrame containing the detailed LCOE breakdown with the following columns:
"Component": The name of the project component (e.g., "Turbine", "Balance of System CapEx", "OpEx").
"Category": The category of the component (e.g., "Turbine", "Balance of System CapEx", "Financial CapEx", "OpEx").
"Value ($/kW)": The value of the component in $/kW.
"Fixed charge rate (FCR) (real)": The real fixed charge rate (FCR) applied to the component.
"Value ($/kW-yr)": The value of the component in $/kW-yr, after applying the FCR.
"Net AEP (MWh/kW/yr)": The net annual energy production (AEP) in MWh/kW/yr.
"Value ($/MWh)": The value of the component in $/MWh, calculated by dividing the $/kW-yr value by the net AEP.
Notes
- CapEx components are categorized into "Turbine", "Balance of System CapEx", and "Financial
CapEx".
OpEx components are derived from WOMBAT's OpEx metrics, categorized as "OpEx".
The LCOE is calculated by considering both CapEx and OpEx components, and adjusting for net AEP and FCR.
Rows with a value of 0 in the "Value ($/MWh)" column are removed to avoid clutter in annual reporting charts.
Command Line Interface (CLI)#
Run one or multiple WAVES analyses given a configuration dictionary, and optionally output and save the results.
Usage:
waves [OPTIONS] LIBRARY_PATH CONFIGURATION...
Arguments:
LIBRARY_PATH
: The relative or absolute path to the simulation data library.CONFIGURATION...
: The configuration file name(s) to run. These should be located inLIBRARY_PATH/project/config/
.
Options:
--report / --no-report
: [default: report] Generate a table of metrics;report_config
must be configured in theconfiguration
. See the API forProject.generate_report()
for details. Use--no-report
to just run the simulation.--save-report / --no-save-report
: [default: save-report] Save the output report metrics to a CSV file. Useno-save-report
to only display the results.--install-completion
: Install completion for the current shell.--show-completion
: Show completion for the current shell, to copy it or customize the installation.--help
: Show this message and exit.
Additional Configurations#
Running one or many analyses from the command line can be used with a few additional parameters defined in the configuration file. The provided example is configured to also be run through the CLI. Below is an example of the additional configurations
146 - 10
147
148
149# CLI Arguments
150run:
151 which_floris: wind_rose # month-based wind rose wake analysis
152 full_wind_rose: False # use the WOMBAT date range
153 floris_reinitialize_kwargs:
154 cut_in_wind_speed: 3.0
155 cut_out_wind_speed: 25.0 # standard ws range
156report_config:
157 name: Base Fixed Bottom 2022
158 "# Turbines":
159 metric: n_turbines
160 Turbine Rating (MW):
161 metric: turbine_rating
162 Project Capacity (MW):
163 metric: capacity
164 kwargs:
165 units: mw
166 "# OSS":
167 metric: n_substations
168 Total Export Cable Length (km):
169 metric: export_system_total_cable_length
170 Total Array Cable Length (km):
171 metric: array_system_total_cable_length
172 CapEx ($):
173 metric: capex
174 CapEx per kW ($/kW):
175 metric: capex
176 kwargs:
177 per_capacity: kw
178 OpEx ($):
179 metric: opex
180 OpEx per kW ($/kW):
181 metric: opex
182 kwargs:
183 per_capacity: kw
184 AEP (MWh):
185 metric: energy_production
186 kwargs:
187 units: mw
188 aep: True
189 with_losses: True
190 AEP per kW (MWh/kW):
191 metric: energy_production
192 kwargs:
193 units: mw
194 per_capacity: kw
195 aep: True
196 with_losses: True
197 Net Capacity Factor With Wake Losses (%):
198 metric: capacity_factor
199 kwargs:
200 which: net
201 Net Capacity Factor With All Losses (%):
202 metric: capacity_factor
203 kwargs:
204 which: net
205 with_losses: True
206 Gross Capacity Factor (%):
207 metric: capacity_factor
208 kwargs:
209 which: gross
210 Energy Availability (%):
211 metric: availability
212 kwargs:
213 which: energy
214 LCOE ($/MWh):
215 metric: lcoe