Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
23a7442
first pass
paulf81 Nov 5, 2025
f9b0ca1
remove new regression test
paulf81 Nov 5, 2025
4ef2a9f
rename to no add wakes
paulf81 Nov 5, 2025
a724039
remove old components
paulf81 Nov 5, 2025
be8b1a2
add scada pass through form
paulf81 Nov 5, 2025
4f68605
Merge branch 'develop' into feature/wind_consolidation
paulf81 Nov 5, 2025
6050cfc
Merge branch 'develop' into feature/wind_consolidation
paulf81 Nov 6, 2025
a3a8729
Merge branch 'develop' into feature/wind_consolidation
paulf81 Nov 6, 2025
9d0730a
remove turbine input and add example input
paulf81 Nov 7, 2025
6bd231a
Add "WindFarmSCADAPower" to available component types in utilities.py
paulf81 Nov 7, 2025
ab00ca3
remove wd_mean requirement
paulf81 Nov 7, 2025
95c5b9b
place hold wind direction for now
paulf81 Nov 7, 2025
5579176
use iloc
paulf81 Nov 12, 2025
ed45b38
Merge branch 'develop' into feature/wind_consolidation
paulf81 Nov 14, 2025
45273e2
Merge branch 'feature/wind_consolidation' of https://github.com/NREL/…
paulf81 Nov 14, 2025
f5ed08e
Merge branch 'develop' into feature/wind_consolidation
paulf81 Nov 14, 2025
7578c5d
bug fix
paulf81 Nov 16, 2025
302dcae
Merge branch 'develop' into feature/wind_consolidation
paulf81 Dec 19, 2025
c2980fe
linting
paulf81 Dec 19, 2025
d5f4ca8
fix docstring
paulf81 Dec 19, 2025
d26e7f6
Apply ruff format
misi9170 Dec 19, 2025
1294789
Merge branch 'fwc' into feature/wind_consolidation
misi9170 Dec 19, 2025
db92bf1
Minor docs update
genevievestarke Dec 29, 2025
3e0207d
Switch to wind_method approach; cleanup to follow
misi9170 Dec 30, 2025
72e8dbc
Remove filter from turbine update model
genevievestarke Dec 30, 2025
2e2a374
Update wind docs to remove SCADA filter references
genevievestarke Dec 30, 2025
370fdad
Further cleanup
misi9170 Dec 30, 2025
5d3e905
Add warning for SCADAPower version
misi9170 Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/h_dict.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The `h_dict` is a Python dictionary that contains all the configurations for eac
| **Hybrid Plant Components** |

### Wind Farm (`wind_farm`)
| `component_type` | str | Must be "Wind_MesoToPower" or "Wind_MesoToPowerPrecomFloris" |
| `component_type` | str | Must be "WindFarm" or "WindFarmSCADAPower" |
| `floris_input_file` | str | FLORIS input file path |
| `wind_input_filename` | str | Wind data input file |
| `turbine_file_name` | str | Turbine configuration file |
Expand Down
6 changes: 4 additions & 2 deletions docs/hercules_input.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ plant:
interconnect_limit: 30000 # kW

wind_farm:
component_type: Wind_MesoToPower
component_type: WindFarm
wake_method: dynamic
floris_input_file: inputs/floris_input.yaml
wind_input_filename: inputs/wind_input.csv
turbine_file_name: inputs/turbine_filter_model.yaml
Expand Down Expand Up @@ -234,7 +235,8 @@ plant:
interconnect_limit: 5000

wind_farm:
component_type: Wind_MesoToPower
component_type: WindFarm
wake_method: dynamic
floris_input_file: inputs/floris_input.yaml
wind_input_filename: inputs/wind_input.csv
turbine_file_name: inputs/turbine_filter_model.yaml
Expand Down
4 changes: 2 additions & 2 deletions docs/hybrid_plant.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ HybridPlant automatically detects and initializes components based on the [h_dic

| Component | Component Type | Description |
|-----------|----------------|-------------|
| `wind_farm` | `Wind_MesoToPower` | FLORIS-based wind farm simulation |
| `wind_farm` | `Wind_MesoToPowerPrecomFloris` | Precomputed FLORIS-based wind farm simulation |
| `wind_farm` | `WindFarm` | FLORIS-based wind farm simulation |
| `wind_farm` | `WindFarmSCADAPower` | Pass through wind farm SCADA |
| `solar_farm` | `SolarPySAMPVWatts` | PySAM-based simplified solar simulation |
| `battery` | `BatterySimple` | Basic battery storage model |
| `battery` | `BatteryLithiumIon` | Detailed lithium-ion battery model |
Expand Down
2 changes: 1 addition & 1 deletion docs/timing.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ plant:
interconnect_limit: 50000 # kW

wind_farm:
component_type: Wind_MesoToPower
component_type: WindFarm
wind_input_filename: inputs/wind_data.ftr
# wind_data.ftr must have time_utc column covering the simulation period
...
Expand Down
78 changes: 55 additions & 23 deletions docs/wind.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
# Wind Farm Components

## Wind_MesoToPower
Hercules provides four wind farm simulation components that differ in their approach to wake modeling and data sources. The first three components support both simple filter-based turbine models and 1-degree-of-freedom (1-DOF) turbine dynamics, while the fourth component uses SCADA power data directly.

Wind_MesoToPower is a comprehensive wind farm simulator that focuses on meso-scale phenomena by applying a separate wind speed time signal to each turbine model derived from data. It combines FLORIS wake modeling with detailed turbine dynamics for long-term wind farm performance analysis.

## Wind_MesoToPowerPrecomFloris
## Overview

Wind_MesoToPowerPrecomFloris is an optimized variant of Wind_MesoToPower that pre-computes FLORIS wake deficits for improved simulation performance. This approach trades some accuracy for significant speed improvements in specific operating scenarios.
The `WindFarm` component applies wind speed time signals to turbine models to simulate wind farm behavior over extended periods. This is available with different methods for how wakes are applies, as described below. The `WindFarmSCADAPower` component uses a fundamentally different approach by using actual SCADA power measurements as input.

## Overview
## WindFarm (with Dynamic wake method)

`WindFarm` is a comprehensive wind farm simulator. When `wake_method="dynamic"` (the default), `WindFarm` computes wake effects dynamically at each time step (or at intervals specified by `floris_update_time_s`). It focuses on meso-scale phenomena by applying a separate wind speed time signal to each turbine model derived from data. This model combines FLORIS wake modeling with detailed turbine dynamics for long-term wind farm performance analysis.

**Use this model when:**
- Turbines have individual power setpoints or non-uniform operation
- Precise wake modeling is required for each control action
- Turbines may be partially derated or individually controlled

## WindFarm (with Precomputed wake method)

Both wind farm components integrate FLORIS for wake effects with individual turbine models to simulate wind farm behavior over extended periods. They support both simple filter-based turbine models and 1-degree-of-freedom (1-DOF) turbine dynamics.
`WindFarm` with `wake_method="precomputed"` is an optimized variant that pre-computes all FLORIS wake deficits at initialization for improved simulation speed. This approach provides significant speed improvements while conservatively assuming wakes are always based on nominal operation.

### Precomputed FLORIS Approach
**Use this model when:**
- Not investigating wakes of derated turbines or wake losses can be conservatively estimated.

Wind_MesoToPowerPrecomFloris pre-computes wake deficits using a fixed cadence determined by `floris_update_time_s`. At initialization, FLORIS is evaluated at that cadence using right-aligned time-window averages of wind speed, wind direction, and turbulence intensity. The resulting wake deficits are then held constant between evaluations and applied to the per-turbine inflow time series.

This approach is valid when the wind farm operates under these conditions:
## WindFarm (with No Added Wakes method)

- All turbines operating normally
- All turbines off
- Following a wind-farm wide derating level
Using `WindFarm` with `wake_method="no_added_wakes"` assumes that wake effects are already included in the input wind data and performs no wake modeling during simulation. This model is appropriate for using SCADA data of operational farm since wake losses already included in data.

Important: This model is not appropriate when turbines are partially derated below the curtailment level or not uniformly curtailed. In such cases, use the standard Wind_MesoToPower class instead.

## WindFarmSCADAPower (SCADA Power Data)

`WindFarmSCADAPower` uses SCADA power measurements directly rather than computing power from wind speeds and turbine models. This component applies a filter to the SCADA power data to simulate turbine response dynamics and respects power setpoint constraints.

_This model is a beta feature and is not yet fully tested._

## Configuration

Expand All @@ -31,33 +42,51 @@ Important: This model is not appropriate when turbines are partially derated bel
Required parameters for both components in [h_dict](h_dict.md) (see [timing](timing.md) for time-related parameters):
- `floris_input_file`: FLORIS farm configuration
- `wind_input_filename`: Wind resource data file
- `turbine_file_name`: Turbine model configuration

### Wind_MesoToPower Specific Parameters
### WindFarm Specific Parameters

Required parameters for Wind_MesoToPower:
- `floris_update_time_s`: How often to update FLORIS (the last `floris_update_time_s` seconds are averaged as input)
Required parameters for WindFarm:
- `wake_method`: One of `"dynamic"`, `"precomputed"`, or `"no_added_wakes"` (defaults to `"dynamic"`)
- `floris_update_time_s`: How often to update FLORIS (the last `floris_update_time_s` seconds are averaged as input). Required for `"dynamic"` and `"precomputed"` wake methods; for `"no_added_wakes"`, this parameter is not required and ignored if provided.
- `turbine_file_name`: Turbine model configuration
- `log_channels`: List of output channels to log. See [Logging Configuration](wind-logging-configuration) section below for details.

### Wind_MesoToPowerPrecomFloris Specific Parameters
### WindFarmSCADAPower Specific Parameters

Required parameters for Wind_MesoToPowerPrecomFloris:
- `floris_update_time_s`: Determines the cadence of wake precomputation. At each cadence tick, the last `floris_update_time_s` seconds are averaged and used to evaluate FLORIS. The computed wake deficits are then applied until the next cadence tick.
- `log_channels`: List of output channels to log. See [Logging Configuration](wind-logging-configuration) section below for details.
Required parameters for WindFarmSCADAPower:
- `scada_filename`: Path to SCADA data file (CSV, pickle, or feather format)
- `turbine_file_name`: Turbine model configuration (for filter parameters)
- `log_channels`: List of output channels to log. See [Logging Configuration](#logging-configuration) section below for details.

**SCADA File Format:**

The SCADA file must contain the following columns:
- `time_utc`: Timestamps in UTC (ISO 8601 format or parseable datetime strings)
- `wd_mean`: Mean wind direction in degrees
- `pow_###`: Power output for each turbine (e.g., `pow_000`, `pow_001`, `pow_002`)

Optional columns:
- `ws_###`: Wind speed for each turbine (e.g., `ws_000`, `ws_001`, `ws_002`)
- `ws_mean`: Mean wind speed (used if individual turbine speeds not provided)
- `ti_###`: Turbulence intensity for each turbine (defaults to 0.08 if not provided)

The number of turbines and rated power are automatically inferred from the SCADA data.

## Turbine Models

**Note:** WindFarmSCADAPower does not use a filter model as power values come directly from SCADA data rather than being computed from wind speedes.

### Filter Model
Simple first-order filter for power output smoothing with configurable time constants.

### 1-DOF Model
Advanced model with rotor dynamics, pitch control, and generator torque control.
Advanced model with rotor dynamics, pitch control, and generator torque control. Not applicable to WindFarmSCADAPower.

## Outputs

### Common Outputs

Both components provide these outputs in the h_dict at each simulation step:
All four components provide these outputs in the h_dict at each simulation step:
- `power`: Total wind farm power (kW)
- `turbine_powers`: Individual turbine power outputs (array, kW)
- `turbine_power_setpoints`: Current power setpoint values (array, kW)
Expand All @@ -67,6 +96,9 @@ Both components provide these outputs in the h_dict at each simulation step:
- `wind_speeds_background`: Per-turbine background wind speeds (array, m/s)
- `wind_speeds_withwakes`: Per-turbine with-wakes wind speeds (array, m/s)


**Note for WindFarm with no_added_wakes and WindFarmSCADAPower:** In these models (no wake modeling), `wind_speeds_withwakes` equals `wind_speeds_background` and `wind_speed_mean_withwakes` equals `wind_speed_mean_background`.

(wind-logging-configuration)=
## Logging Configuration

Expand Down
4 changes: 2 additions & 2 deletions examples/00_wind_farm_only/hercules_input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ plant:
interconnect_limit: 15000 # kW

wind_farm:

component_type: Wind_MesoToPower
component_type: WindFarm
wake_method: dynamic
floris_input_file: ../inputs/floris_input_small.yaml
wind_input_filename: ../inputs/wind_input_small.ftr
turbine_file_name: ../inputs/turbine_filter_model.yaml
Expand Down
4 changes: 2 additions & 2 deletions examples/01_wind_farm_dof1_model/hercules_input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ plant:
interconnect_limit: 15000 # kW

wind_farm:

component_type: Wind_MesoToPower
component_type: WindFarm
wake_method: dynamic
floris_input_file: inputs/floris_input.yaml
wind_input_filename: inputs/wind_input.csv
turbine_file_name: inputs/turbine_dof_1.yaml
Expand Down
4 changes: 2 additions & 2 deletions examples/02_wind_farm_realistic_inflow/hercules_input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ plant:
interconnect_limit: 45000 # kW

wind_farm:

component_type: Wind_MesoToPower
component_type: WindFarm
wake_method: dynamic
floris_input_file: ../inputs/floris_input_large.yaml
wind_input_filename: ../inputs/wind_input_large.ftr
turbine_file_name: ../inputs/turbine_filter_model.yaml
Expand Down
10 changes: 4 additions & 6 deletions examples/02b_wind_farm_realistic_inflow_precom_floris/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

## Description

This example is identical to `02_wind_farm_realistic_inflow` with the exception that the `Wind_MesoToPowerPrecomFloris`
class is used to speed up the simulation. This example automatically generates the necessary input files in the centralized `examples/inputs/` folder when first run.
This example is identical to `02_wind_farm_realistic_inflow` with the exception that the `precomputed` wake method of the `WindFarm` class is used to speed up the simulation. This example automatically generates the necessary input files in the centralized `examples/inputs/` folder when first run.

Note the caveats to using this class from the docs:


> In contrast to the Wind_MesoToPower class, this class pre-computes the FLORIS wake
> In contrast to `wake_method="dynamic"`, this class pre-computes the FLORIS wake
deficits for all possible wind speeds and power setpoints. This is done by running for
all wind speeds and wind directions (but not over all power setpoints). This is valid
for cases where the wind farm is operating:
Expand All @@ -19,9 +18,8 @@ Note the caveats to using this class from the docs:
It is in practice conservative with respect to the wake deficits, but it is more efficient
than running FLORIS for each condition. In cases where turbines are:
- partially derated below the curtailment level
- not uniformly curtailed or some turbines are off

This is not an appropriate model and the more general Wind_MesoToPower class should be used.
- not uniformly curtailed or some turbines are off
this is not an appropriate model and the more general `wake_method="dynamic"` version should be used.



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ plant:
interconnect_limit: 45000 # kW

wind_farm:

component_type: Wind_MesoToPowerPrecomFloris
component_type: WindFarm
wake_method: precomputed
floris_input_file: ../inputs/floris_input_large.yaml
wind_input_filename: ../inputs/wind_input_large.ftr
turbine_file_name: ../inputs/turbine_filter_model.yaml
Expand Down
31 changes: 31 additions & 0 deletions examples/02c_wind_farm_realistic_inflow_direct/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Example 02c: Wind Farm Realistic Inflow (Direct - No Wake Modeling)

## Description

This example demonstrates the `"no_added_wakes"` wake method, which assumes that wake effects are already included in the input wind data and performs no additional wake modeling.

In this example, the `WindFarm` component type uses `wake_method="no_added_wakes"`, which means:
- No FLORIS calculations are performed during the simulation (only at initialization to read turbine properties)
- `wind_speeds_withwakes` equals `wind_speeds_background` at all times
- Wake deficits are always zero
- Turbine dynamics (filter model or DOF1 model) still operate normally

This example automatically generates the necessary input files in the centralized `examples/inputs/` folder when first run.

## Running

To run the example, execute the following command in the terminal:

```bash
python hercules_runscript.py
```

## Outputs

To plot the outputs run the following command in the terminal:

```bash
python plot_outputs.py
```


40 changes: 40 additions & 0 deletions examples/02c_wind_farm_realistic_inflow_direct/hercules_input.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Input YAML for hercules

# Name
name: example_02c

###
# Describe this simulation setup
description: Wind Only Realistic Inflow (Direct - No Wake Modeling)

dt: 1.0
starttime_utc: "2024-06-24T16:59:08Z" # Jun 24, 2024 16:59:08 UTC (Zulu time)
endtime_utc: "2024-06-26T16:59:00Z" # ≈48 hours later (Jun 26, 2024 16:59:00 UTC)
verbose: False

plant:
interconnect_limit: 45000 # kW

wind_farm:
component_type: WindFarm
wake_method: no_added_wakes
floris_input_file: ../inputs/floris_input_large.yaml
wind_input_filename: ../inputs/wind_input_large.ftr
turbine_file_name: ../inputs/turbine_filter_model.yaml
log_file_name: outputs/log_wind_sim.log
log_channels:
- power
- wind_speed_mean_background
- wind_speed_mean_withwakes
- wind_direction_mean
- turbine_powers
- wind_speeds_withwakes
- wind_speeds_background
- turbine_power_setpoints

controller:





Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import numpy as np
from hercules.hercules_model import HerculesModel
from hercules.utilities_examples import ensure_example_inputs_exist, prepare_output_directory

prepare_output_directory()

# Ensure example inputs exist
ensure_example_inputs_exist()

# Initialize the Hercules model
hmodel = HerculesModel("hercules_input.yaml")


# Define a simple controller that sets all power setpoints to full rating
class ControllerFullRating:
"""A simple controller that sets all turbines to full rating.

This controller is appropriate for the direct wake model where
wake effects are already included in the input wind data.
"""

def __init__(self, h_dict):
"""Initialize the controller.

Args:
h_dict (dict): The hercules input dictionary.
"""
pass

def step(self, h_dict):
"""Execute one control step.

Args:
h_dict (dict): The hercules input dictionary.

Returns:
dict: The updated hercules input dictionary.
"""
# Set all turbines to full rating
h_dict["wind_farm"]["turbine_power_setpoints"] = 5000 * np.ones(
h_dict["wind_farm"]["n_turbines"]
)

return h_dict


# Assign the controller to the Hercules model
hmodel.assign_controller(ControllerFullRating(hmodel.h_dict))

# Run the simulation
hmodel.run()

hmodel.logger.info("Process completed successfully")
Loading