Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wind farm power setpoint tracking controller #29

Merged
merged 26 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8377d8d
rename actuator disk interface for generality with passing power setp…
misi9170 Feb 9, 2024
1a0a789
Initial skeleton for power tracking controller.
misi9170 Feb 9, 2024
12931c7
first tests added.
misi9170 Feb 10, 2024
0523000
ruff, typos.
misi9170 Feb 12, 2024
af3629f
Passing through yaw angles as well (at null value).
misi9170 Feb 12, 2024
06072e2
Open-loop setpoint distribution.
misi9170 Feb 12, 2024
00f1c17
Rename to distributing for this version.'
misi9170 Feb 12, 2024
5ce8f7c
References pass through interface; tested."
misi9170 Feb 12, 2024
bcc6a81
placeholder for feedback-based power tracking controller.
misi9170 Feb 12, 2024
9cdbbc1
Tracking control loop runs; not yet tested.
misi9170 Feb 12, 2024
b021ffd
Test proportional control operation.
misi9170 Feb 12, 2024
ebb2915
Slightly more thorough test.
misi9170 Feb 12, 2024
3a81799
Running simulations back to back.
misi9170 Feb 12, 2024
29ca132
plotting; use output_file option in input_dict.
misi9170 Feb 13, 2024
ddc0710
ruff.
misi9170 Feb 13, 2024
9fead0b
Follow externally-provided reference.
misi9170 Feb 13, 2024
c94dc66
Upload data files for examples.
misi9170 Feb 13, 2024
1f7651c
Update tests to reflect external_signals key on hercules_dict.
misi9170 Feb 13, 2024
73c0e95
Better tuning (after transients.
misi9170 Feb 13, 2024
0a36414
Allow proportional gain to be set externally and test that expected b…
misi9170 Feb 13, 2024
7c0af01
Update documentation.
misi9170 Feb 13, 2024
cce2a59
Update plots, trajectory.
misi9170 Feb 16, 2024
b1ee0ee
Update documentation of example.
misi9170 Feb 16, 2024
2819b93
More consistent tracking of hercules time in plot output.
misi9170 Feb 26, 2024
8158b68
Update readme.
misi9170 Feb 26, 2024
5e2cbae
Clarify setpoint default unit; comment out code for integral action t…
misi9170 Feb 26, 2024
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
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ slices
# Data files
*.csv
*.log
examples/*/logstandin
examples/*/loghercules
examples/*/logfloris
examples/*/logstandin*
examples/*/loghercules*
examples/*/logfloris*

# Exceptions
!examples/lookup-based_wake_steering_florisstandin/amr_standin_data.csv
!examples/wind_farm_power_tracking_florisstandin/amr_standin_data.csv
!examples/wind_farm_power_tracking_florisstandin/wind_power_reference_data.csv

# macOS files
.DS_Store
Expand Down
32 changes: 28 additions & 4 deletions docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,39 @@ method of `ControllerBase`.

### LookupBasedWakeSteeringController
Yaw controller that implements wake steering based on a lookup table.
Requires a df_opt object produced by a FLORIS yaw optimization routine. See example
Requires a `df_opt` object produced by a FLORIS yaw optimization routine. See example
lookup-based_wake_steering_florisstandin for example usage.

Currently, yaw angles are set based purely on the (local turbine) wind direction. The lookup table
is sampled at a hardcoded wind speed of 8 m/s. This will be updated in future when an interface is
developed for a simulator that provides wind turbine wind speeds also.

### WakeSteeringROSCOStandin
May be combined into a universal simple wake steeringcontroller.
Not yet developed. May be combined into a universal simple LookupBasedWakeSteeringController.

### HerculesWindBatteryController
TO WRITE
### WindBatteryController
Placeholder for a controller that manages both a wind power plant and colocated
battery.

### WindFarmPowerDistributingController

Wind farm-level power controller that simply distributes a farm-level power
reference between wind turbines evenly, without checking whether turbines are
able to produce power at the requested level. Not expected to perform well when
wind turbines are waked or cannot produce the desired power for other reasons.
However, is a useful comparison case for the WindFarmPowerTrackingController
(described below).

### WindFarmPowerTrackingController

Closed-loop wind farm-level power controller that distributes a farm-level
power reference among the wind turbines in a farm and adjusts the requests made
from each turbine depending on whether the power reference has been met.
Developed under the [A2e2g project](https://github.com/NREL/a2e2g), with
further details provided in
[Sinner et al.](https://pubs.aip.org/aip/jrse/article/15/5/053304/2913100).

Integral action, as well as gain scheduling based on turbine saturation, has been disabled as
simple proportional control appears sufficient currently. However, these may be enabled at a
later date if needed. The `proportional_gain` for the controller may be provided on instantiation,
and defaults to `proportional_gain = 1`.
18 changes: 17 additions & 1 deletion docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ of certain controllers and interfaces.
### lookup-based_wake_steering_florisstandin
2-turbine example of lookup-based wake steering control, run using Hercules with the FLORIS standin
in place of AMR-Wind for exposition purposes. To run this example, navigate to the
examples/lookup-based_wake_steering_florisstandin and then run the following.
examples/lookup-based_wake_steering_florisstandin folder and then run the following.
```
python construct_yaw_offsets.py
```
Expand All @@ -32,4 +32,20 @@ python plot_output_data.py
This should produce the following plot.
![Results of lookup-based_wake_steering_florisstandin example](
graphics/lookup-table-example-plot.png
)

## wind_farm_power_tracking_florisstandin
2-turbine example of wind-farm-level power reference tracking, run using Hercules with the FLORIS
standin in place of AMR-Wind for exposition purposes. To run this example, navigate to the
examples/wind_farm_power_tracking_florisstandin folder and run the following:
```
./bash_script.sh
```

This will run both a closed-loop controller, which compensates for underproduction at individual
turbines, and an open-loop controller, which simply distributes the farm-wide reference evenly
amongst the turbines of the farm without feedback. The resulting trajectories are plotted,
producing:
![Results of wind_farm_power_tracking_florisstandin example](
graphics/wf-power-tracking-plot.png
)
Binary file added docs/graphics/wf-power-tracking-plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 5 additions & 4 deletions docs/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ WHOC with various simulation platforms and other repositories. Each controller
run will require an `interface`, which is an instantiated object of a class
in this library. All interface classes should inherit from `InterfaceBase`,
which can be found n interface_base.py, and should implement three methods:
- `get_measurements()`: Recieve measurements from simulation assets and
- `get_measurements()`: Receive measurements from simulation assets and
organize into a dictionary that the calling controller can utilize. Optionally,
receives a large dictionary (for example, the Hercules `main_dict`), from which
useable measurements can be extracted/repackaged for easy use in the controller.
- `check_controls()`: Check that the keys in `controls_dict` are viable for
the receiving plant.
- `send_controls()`: Send controls to the simulation assets. Controls are
created as specific keyword arguements, which match those controls generated
created as specific keyword arguments, which match those controls generated
by the calling controller. Optionally, receives a large dictionary
(for example, the Hercules `main_dict`), which can be written to and returned
with controls as needed.
Expand All @@ -21,11 +21,12 @@ These methods will all be called in the `step()` method of `ControllerBase`.

## Available interfaces

### HerculesADYawInterface
### HerculesADInterface
For direct python communication with Hercules. This should be instantiated
in a runscript that is running Hercules; used to generate a `controller` from
the WHOC controllers submodule; and that `controller` should be passed to the
Hercules `Emulator` upon its instantiation.
Hercules `Emulator` upon its instantiation. Support transmitting yaw angles
and power setpoints to wind turbines.

### ROSCO_ZMQInterface
For sending and receiving communications from one or more ROSCO instances
Expand Down
201 changes: 201 additions & 0 deletions examples/lookup-based_wake_steering_florisstandin/amr_standin_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
,time,amr_wind_speed,amr_wind_direction
0,0.0,8.0,260.0
1,0.5,8.0,260.0
2,1.0,8.0,260.0
3,1.5,8.0,260.0
4,2.0,8.0,260.0
5,2.5,8.0,260.0
6,3.0,8.0,260.0
7,3.5,8.0,260.0
8,4.0,8.0,260.0
9,4.5,8.0,260.0
10,5.0,8.0,260.0
11,5.5,8.0,260.0
12,6.0,8.0,260.0
13,6.5,8.0,260.0
14,7.0,8.0,260.0
15,7.5,8.0,260.0
16,8.0,8.0,260.0
17,8.5,8.0,260.0
18,9.0,8.0,260.0
19,9.5,8.0,260.0
20,10.0,8.0,260.0
21,10.5,8.0,260.0
22,11.0,8.0,260.0
23,11.5,8.0,260.0
24,12.0,8.0,260.0
25,12.5,8.0,260.0
26,13.0,8.0,260.0
27,13.5,8.0,260.0
28,14.0,8.0,260.0
29,14.5,8.0,260.0
30,15.0,8.0,260.0
31,15.5,8.0,260.0
32,16.0,8.0,260.0
33,16.5,8.0,260.0
34,17.0,8.0,260.0
35,17.5,8.0,260.0
36,18.0,8.0,260.0
37,18.5,8.0,260.0
38,19.0,8.0,260.0
39,19.5,8.0,260.0
40,20.0,8.0,260.0
41,20.5,8.0,260.0
42,21.0,8.0,260.0
43,21.5,8.0,260.0
44,22.0,8.0,260.0
45,22.5,8.0,260.0
46,23.0,8.0,260.0
47,23.5,8.0,260.0
48,24.0,8.0,260.0
49,24.5,8.0,260.0
50,25.0,8.0,260.0
51,25.5,8.0,260.0
52,26.0,8.0,260.0
53,26.5,8.0,260.0
54,27.0,8.0,260.0
55,27.5,8.0,260.0
56,28.0,8.0,260.0
57,28.5,8.0,260.0
58,29.0,8.0,260.0
59,29.5,8.0,260.0
60,30.0,8.0,260.0
61,30.5,8.0,260.126582278481
62,31.0,8.0,260.253164556962
63,31.5,8.0,260.37974683544303
64,32.0,8.0,260.50632911392404
65,32.5,8.0,260.63291139240505
66,33.0,8.0,260.75949367088606
67,33.5,8.0,260.88607594936707
68,34.0,8.0,261.0126582278481
69,34.5,8.0,261.1392405063291
70,35.0,8.0,261.26582278481015
71,35.5,8.0,261.39240506329116
72,36.0,8.0,261.5189873417722
73,36.5,8.0,261.6455696202532
74,37.0,8.0,261.7721518987342
75,37.5,8.0,261.8987341772152
76,38.0,8.0,262.0253164556962
77,38.5,8.0,262.1518987341772
78,39.0,8.0,262.27848101265823
79,39.5,8.0,262.40506329113924
80,40.0,8.0,262.53164556962025
81,40.5,8.0,262.65822784810126
82,41.0,8.0,262.7848101265823
83,41.5,8.0,262.9113924050633
84,42.0,8.0,263.0379746835443
85,42.5,8.0,263.1645569620253
86,43.0,8.0,263.2911392405063
87,43.5,8.0,263.4177215189873
88,44.0,8.0,263.54430379746833
89,44.5,8.0,263.67088607594934
90,45.0,8.0,263.7974683544304
91,45.5,8.0,263.9240506329114
92,46.0,8.0,264.0506329113924
93,46.5,8.0,264.17721518987344
94,47.0,8.0,264.30379746835445
95,47.5,8.0,264.43037974683546
96,48.0,8.0,264.55696202531647
97,48.5,8.0,264.6835443037975
98,49.0,8.0,264.8101265822785
99,49.5,8.0,264.9367088607595
100,50.0,8.0,265.0632911392405
101,50.5,8.0,265.1898734177215
102,51.0,8.0,265.3164556962025
103,51.5,8.0,265.44303797468353
104,52.0,8.0,265.56962025316454
105,52.5,8.0,265.69620253164555
106,53.0,8.0,265.82278481012656
107,53.5,8.0,265.9493670886076
108,54.0,8.0,266.0759493670886
109,54.5,8.0,266.2025316455696
110,55.0,8.0,266.32911392405066
111,55.5,8.0,266.45569620253167
112,56.0,8.0,266.5822784810127
113,56.5,8.0,266.7088607594937
114,57.0,8.0,266.8354430379747
115,57.5,8.0,266.9620253164557
116,58.0,8.0,267.0886075949367
117,58.5,8.0,267.2151898734177
118,59.0,8.0,267.34177215189874
119,59.5,8.0,267.46835443037975
120,60.0,8.0,267.59493670886076
121,60.5,8.0,267.72151898734177
122,61.0,8.0,267.8481012658228
123,61.5,8.0,267.9746835443038
124,62.0,8.0,268.1012658227848
125,62.5,8.0,268.2278481012658
126,63.0,8.0,268.3544303797468
127,63.5,8.0,268.4810126582278
128,64.0,8.0,268.60759493670884
129,64.5,8.0,268.7341772151899
130,65.0,8.0,268.8607594936709
131,65.5,8.0,268.9873417721519
132,66.0,8.0,269.11392405063293
133,66.5,8.0,269.24050632911394
134,67.0,8.0,269.36708860759495
135,67.5,8.0,269.49367088607596
136,68.0,8.0,269.62025316455697
137,68.5,8.0,269.746835443038
138,69.0,8.0,269.873417721519
139,69.5,8.0,270.0
140,70.0,8.0,278.8202617298383
141,70.5,8.0,272.00078604183614
142,71.0,8.0,274.8936899205287
143,71.5,8.0,281.2044659960073
144,72.0,8.0,279.3377899507498
145,72.5,8.0,265.11361060061796
146,73.0,8.0,274.75044208762796
147,73.5,8.0,269.2432139585115
148,74.0,8.0,269.48390574103223
149,74.5,8.0,272.0529925096919
150,75.0,8.0,270.7202178558044
151,75.5,8.0,277.2713675348149
152,76.0,8.0,273.80518862573496
153,76.5,8.0,270.60837508246414
154,77.0,8.0,272.2193161637271
155,77.5,8.0,271.6683716368713
156,78.0,8.0,277.470395365788
157,78.5,8.0,268.974208681171
158,79.0,8.0,271.5653385082545
159,79.5,8.0,265.72952130349137
160,80.0,8.0,257.2350509208296
161,80.5,8.0,273.26809297720183
162,81.0,8.0,274.3221809942975
163,81.5,8.0,266.28917489796777
164,82.0,8.0,281.348773119938
165,82.5,8.0,262.7281716270062
166,83.0,8.0,270.2287925865072
167,83.5,8.0,269.0640807498708
168,84.0,8.0,277.6638960717923
169,84.5,8.0,277.3467938495014
170,85.0,8.0,270.77473712848456
171,85.5,8.0,271.8908125980109
172,86.0,8.0,265.56107126184946
173,86.5,8.0,260.0960176588804
174,87.0,8.0,268.2604392533692
175,87.5,8.0,270.7817448455199
176,88.0,8.0,276.1514534036386
177,88.5,8.0,276.0118992439221
178,89.0,8.0,268.06336591296025
179,89.5,8.0,268.4884862471233
180,90.0,8.0,264.7572351746645
181,90.5,8.0,262.8999103141051
182,91.0,8.0,261.46864904687493
183,91.5,8.0,279.75387697615895
184,92.0,8.0,267.45173909124173
185,92.5,8.0,267.80962849194407
186,93.0,8.0,263.7360231997504
187,93.5,8.0,273.88745177915956
188,94.0,8.0,261.93051076221025
189,94.5,8.0,268.93629859893014
190,95.0,8.0,265.5226671940316
191,95.5,8.0,271.9345124892963
192,96.0,8.0,267.4459743121556
193,96.5,8.0,264.09683907938796
194,97.0,8.0,269.85908885830673
195,97.5,8.0,272.1416593526521
196,98.0,8.0,270.3325861119158
197,98.5,8.0,271.5123594886989
198,99.0,8.0,266.8283895315952
199,99.5,8.0,268.1862941700643
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
from hercules.py_sims import PySims
from hercules.utilities import load_yaml
from whoc.controllers.lookup_based_wake_steering_controller import LookupBasedWakeSteeringController
from whoc.interfaces.hercules_actuator_disk_yaw_interface import HerculesADYawInterface
from whoc.interfaces.hercules_actuator_disk_interface import HerculesADInterface

input_dict = load_yaml(sys.argv[1])

# Load the optimal yaw angle lookup table for controller us
df_opt = pd.read_pickle("yaw_offsets.pkl")

interface = HerculesADYawInterface(input_dict)
interface = HerculesADInterface(input_dict)
controller = LookupBasedWakeSteeringController(interface, input_dict, df_yaw=df_opt)

py_sims = PySims(input_dict)
Expand Down
Loading
Loading