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

Power control #375

Open
wants to merge 74 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
6ceb155
Add initial torque/speed power controllers
dzalkind Mar 27, 2024
3a5c59a
Add power control via pitch
dzalkind Mar 28, 2024
ab7a1f8
Update .readthedocs.yaml
dzalkind Jun 14, 2024
7870a01
Implement open loop PRC inputs
dzalkind Jun 14, 2024
b8559fa
Add ZMQ PRC inputs, defaults in wfc setpoints
dzalkind Jun 14, 2024
7546879
Update to latest scipy functions
dzalkind Jul 1, 2024
86cf9f6
Update max_torque_rates
dzalkind Jul 1, 2024
105dc45
Switch constants to integers
dzalkind Jul 2, 2024
c9895f6
Merge remote-tracking branch 'origin/fix_scipy' into power_control
dzalkind Jul 2, 2024
8b62b85
Let R_Speed affect below rated ref speed (based on TSR)
dzalkind Jul 2, 2024
ed1522a
Merge branch 'main' into docs
dzalkind Jul 12, 2024
a83e4bb
Docs: Fix small bug in docs that caused errors with links
abhineet-gupta Jul 12, 2024
3cc17b3
Add open loop control with wind speed breakpoints
dzalkind Jul 12, 2024
a2255a1
Add checks for power control
dzalkind Jul 12, 2024
541fe81
Start using automatic documentation for examples (#371)
abhineet-gupta Aug 9, 2024
df133ee
Added autodocumentation template for all examples
abhineet-gupta Aug 10, 2024
b12e499
Re-enable previous PRC_Mode
dzalkind Sep 3, 2024
0b569e4
Update 5MW yamls for constant power
dzalkind Sep 3, 2024
a24f157
Edit comments
dzalkind Sep 3, 2024
c5c2920
Merge remote-tracking branch 'origin/develop' into power_control
dzalkind Sep 3, 2024
860182d
Merge remote-tracking branch 'origin/docs' into power_control
dzalkind Sep 3, 2024
9456ef2
Check for IPC openfast flag when using AWC
dzalkind Sep 3, 2024
87f681e
Merge remote-tracking branch 'origin/main' into power_control
dzalkind Sep 3, 2024
9124f93
Update discons
dzalkind Sep 3, 2024
3327225
Fix GB and gen efficiencies
dzalkind Sep 5, 2024
552a434
Merge remote-tracking branch 'origin/develop' into vbf
dzalkind Sep 5, 2024
27476f9
Allow updating of Ind_BldPitch from int to array
dzalkind Sep 9, 2024
ac73dde
Remove print statement
dzalkind Sep 10, 2024
bdc1c3e
Add power control example
dzalkind Sep 10, 2024
5eb81de
Fix issue where PRC_Mode 2 was not setting PC_RefSpeed_PRC
dzalkind Sep 10, 2024
008c154
Add offset to sine_timeseries
dzalkind Sep 10, 2024
5c94207
Clean up power control example
dzalkind Sep 10, 2024
2ec41dd
Disable WSE automatically outside normal operation
dzalkind Sep 27, 2024
302a6ba
Fix OL_BP_FiltFreq input writing
dzalkind Sep 27, 2024
0c0e423
Check breakpoints of open loop control against maxima
dzalkind Sep 27, 2024
f249520
Account for rating command in setpoint smoother
dzalkind Sep 27, 2024
90ccdd8
Set up PRC demos and documentation
dzalkind Sep 27, 2024
26fe47c
Elaborate old PRC documentation
dzalkind Sep 27, 2024
82413fc
Update some example documentation
dzalkind Sep 27, 2024
d1021ff
Update example list in docs
dzalkind Sep 27, 2024
b450185
Fix old open loop nomenclature
dzalkind Sep 27, 2024
7882913
Fix error message in ol control
dzalkind Sep 30, 2024
ee2b46a
Configure PRC example for CI
dzalkind Sep 30, 2024
8b304cf
Add docs images
dzalkind Sep 30, 2024
aa1694d
Add a note about building docs
dzalkind Sep 30, 2024
8778c79
Fix global variables in Example 17a and 17b
abhineet-gupta Sep 30, 2024
b0aa0e4
Merge remote-tracking branch 'origin/docs' into docs
dzalkind Oct 3, 2024
16551d0
Merge remote-tracking branch 'origin/docs' into power_control
dzalkind Oct 3, 2024
f075c80
Try numpy<2.0
dzalkind Oct 3, 2024
4486d7a
Fixed bug where TRA_LastRefSpd was set to a generator speed not the L…
BenClaytonARUP Oct 3, 2024
e6cc03a
Merge remote-tracking branch 'origin/docs' into power_control
dzalkind Oct 3, 2024
ca57895
Merge remote-tracking branch 'origin/vbf' into power_control
dzalkind Oct 3, 2024
a613bf8
Merge remote-tracking branch 'origin/docs' into vbf
dzalkind Oct 3, 2024
7b7dcd0
Merge remote-tracking branch 'origin/vbf' into vbf
dzalkind Oct 3, 2024
7a958ac
Merge remote-tracking branch 'origin/develop' into power_control
dzalkind Oct 3, 2024
4055eeb
Document PRC API changes
dzalkind Oct 3, 2024
727d8a1
Check for Ind_BldPitch in original_vt
dzalkind Oct 3, 2024
520fb62
Merge remote-tracking branch 'origin/vbf' into power_control
dzalkind Oct 3, 2024
504c1ee
Merge remote-tracking branch 'origin/main' into docs
dzalkind Oct 4, 2024
f56749d
Update example documentation with figures
dzalkind Oct 4, 2024
fb19631
Fix another ol_timeseries reference
dzalkind Oct 4, 2024
39d7e84
Fix bug if WE_Mode = 0
dzalkind Oct 7, 2024
7e42a6a
Clean up example 26
dzalkind Oct 7, 2024
cefe7f6
Remove own specified rosco_dll
dzalkind Oct 7, 2024
2acc4bd
Add note of WE_Op in ROSCO warning
dzalkind Oct 7, 2024
e34d0b4
Fix example 27 name
dzalkind Oct 8, 2024
23e978a
Put make install builds in same place as pip installed library
dzalkind Oct 9, 2024
e94bb64
Re-initialize WSE/Filter if transitioning from op to nonop and vv
dzalkind Oct 9, 2024
26ad635
Merge remote-tracking branch 'origin/develop' into power_control
dzalkind Oct 11, 2024
82cc985
Merge remote-tracking branch 'origin/docs' into power_control
dzalkind Oct 11, 2024
8765a79
Delete duplicate main in ex 17
dzalkind Oct 11, 2024
7f8b65f
Re-enable ex 20 docs
dzalkind Oct 11, 2024
ed3c6a4
Update variable descriptions
dzalkind Oct 11, 2024
04de072
Filter Nacelle vane signal and use to calculate aligned wind speed
dzalkind Oct 18, 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
4 changes: 2 additions & 2 deletions Examples/05_openfast_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

Note

* you will need to have a compiled controller in ROSCO/build/
* you will need to have a compiled controller in ROSCO/rosco/controller/build
dzalkind marked this conversation as resolved.
Show resolved Hide resolved
"""

# Python Modules
Expand Down Expand Up @@ -67,4 +67,4 @@ def main():
)

if __name__ == "__main__":
main()
main()
7 changes: 5 additions & 2 deletions Examples/06_peak_shaving.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""
06_peak_shavings
----------------
06_peak_shaving
---------------
Load saved turbine, tune controller, plot minimum pitch schedule
In this example:

* Load a yaml file
* Load a turbine from openfast
* Tune a controller
* Plot minimum pitch schedule

.. image:: ../images/06_MinPitch.png

"""

# Python modules
Expand Down
10 changes: 5 additions & 5 deletions Examples/14_open_loop_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def main():

# Set up open loop input
olc = ROSCO_controller.OpenLoopControl(t_max=20)
olc.interp_timeseries(
olc.interp_series(
'blade_pitch',
[0,20],
[0,0.0873] ,
Expand All @@ -59,7 +59,7 @@ def main():
olc.sine_timeseries('nacelle_yaw', 0.0524, 60)

# Plot open loop timeseries
fig,ax = olc.plot_timeseries()
fig,ax = olc.plot_series()
if False:
plt.show()
else:
Expand Down Expand Up @@ -144,9 +144,9 @@ def main():
valid_ind = tt > 2 # first few timesteps can differ, depending on OpenFAST solve config

# Compute errors
nacelle_yaw_diff = fo['NacYaw'][valid_ind] - np.degrees(np.interp(tt[valid_ind],olc.ol_timeseries['time'],olc.ol_timeseries['nacelle_yaw']))
bld_pitch_diff = fo['BldPitch1'][valid_ind] - np.degrees(np.interp(tt[valid_ind],olc.ol_timeseries['time'],olc.ol_timeseries['blade_pitch']))
gen_tq_diff = fo['GenTq'][valid_ind] - np.interp(tt[valid_ind],olc.ol_timeseries['time'],olc.ol_timeseries['generator_torque'])/1e3
nacelle_yaw_diff = fo['NacYaw'][valid_ind] - np.degrees(np.interp(tt[valid_ind],olc.ol_series['time'],olc.ol_series['nacelle_yaw']))
bld_pitch_diff = fo['BldPitch1'][valid_ind] - np.degrees(np.interp(tt[valid_ind],olc.ol_series['time'],olc.ol_series['blade_pitch']))
gen_tq_diff = fo['GenTq'][valid_ind] - np.interp(tt[valid_ind],olc.ol_series['time'],olc.ol_series['generator_torque'])/1e3

# Check diff timeseries
np.testing.assert_allclose(nacelle_yaw_diff, 0, atol = 1e-1) # yaw has dynamics and integration error, tolerance higher
Expand Down
72 changes: 72 additions & 0 deletions Examples/17b_zeromq_multi_openfast.py
dzalkind marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,71 @@

TIME_CHECK = 20
DESIRED_YAW_OFFSET = [-10, 10]
DESIRED_R_PITCH = 0.9

def main():


# Start wind farm control server and two openfast simulation
# as separate processes
logfile = os.path.join(EXAMPLE_OUT_DIR,os.path.splitext(os.path.basename(__file__))[0]+'.log')
p0 = mp.Process(target=run_zmq,args=(logfile,))
p1 = mp.Process(target=sim_openfast_1)
p2 = mp.Process(target=sim_openfast_2)

p0.start()
p1.start()
p2.start()

p0.join()
p1.join()
p2.join()

## Run tests
# Check that info is passed to ROSCO for first simulation
op1 = output_processing.output_processing()
debug_file1 = os.path.join(
EXAMPLE_OUT_DIR,
"17b_zeromq_OF1",
"NREL5MW",
"power_curve",
"base",
"NREL5MW_0.RO.dbg2",
)
local_vars1 = op1.load_fast_out(debug_file1, tmin=0)

# Check that info is passed to ROSCO for first simulation
op2 = output_processing.output_processing()
debug_file2 = os.path.join(
EXAMPLE_OUT_DIR,
"17b_zeromq_OF2",
"NREL5MW",
"power_curve",
"base",
"NREL5MW_0.RO.dbg2",
)
local_vars2 = op2.load_fast_out(debug_file2, tmin=0)

# Generate plots
_, axs = plt.subplots(2, 1)
axs[0].plot(local_vars1[0]["Time"], local_vars1[0]["ZMQ_YawOffset"])
axs[1].plot(local_vars2[0]["Time"], local_vars2[0]["ZMQ_YawOffset"])

if False:
plt.show()
else:
plt.savefig(os.path.join(EXAMPLE_OUT_DIR, "17b_NREL5MW_ZMQ_Setpoints.png"))

# Spot check input at time = 30 sec.
ind1_30 = local_vars1[0]["Time"] == TIME_CHECK
ind2_30 = local_vars2[0]["Time"] == TIME_CHECK

np.testing.assert_almost_equal(
local_vars1[0]["ZMQ_YawOffset"][ind1_30], DESIRED_YAW_OFFSET[0]
)
np.testing.assert_almost_equal(
local_vars2[0]["ZMQ_YawOffset"][ind2_30], DESIRED_YAW_OFFSET[1]
)

def main():

Expand Down Expand Up @@ -115,13 +180,15 @@ def __init__(self):
return None

def update_setpoints(self, id, current_time, measurements):
R_Pitch = 1.0
if current_time <= 10.0:
YawOffset = 0.0
col_pitch_command = 0.0
else:
col_pitch_command = np.deg2rad(2) * np.sin(0.1 * current_time) + np.deg2rad(2) # Implement dynamic induction control
if id == 1:
YawOffset = DESIRED_YAW_OFFSET[0]
R_Pitch = DESIRED_R_PITCH
else:
YawOffset = DESIRED_YAW_OFFSET[1]

Expand All @@ -131,6 +198,7 @@ def update_setpoints(self, id, current_time, measurements):
setpoints['ZMQ_PitOffset(1)'] = col_pitch_command
setpoints['ZMQ_PitOffset(2)'] = col_pitch_command
setpoints['ZMQ_PitOffset(3)'] = col_pitch_command
setpoints['ZMQ_R_Pitch'] = R_Pitch
return setpoints


Expand All @@ -149,6 +217,8 @@ def sim_openfast_1():
r.controller_params["DISCON"] = {}
r.controller_params["DISCON"]["ZMQ_Mode"] = 1
r.controller_params["DISCON"]["ZMQ_ID"] = 1
r.controller_params['DISCON']['PRC_Mode'] = 1
r.controller_params['DISCON']['PRC_Comm'] = 2
r.save_dir = run_dir
r.run_FAST()

Expand All @@ -169,6 +239,8 @@ def sim_openfast_2():
r.controller_params["LoggingLevel"] = 2
r.controller_params["DISCON"]["ZMQ_Mode"] = 1
r.controller_params["DISCON"]["ZMQ_ID"] = 2
r.controller_params['DISCON']['PRC_Mode'] = 1
r.controller_params['DISCON']['PRC_Comm'] = 2
r.run_FAST()


Expand Down
6 changes: 3 additions & 3 deletions Examples/22_cable_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,21 @@ def main():
line_ends = [-14.51, 1.58, 10.33]

olc = OpenLoopControl(t_max=t_max)
olc.interp_timeseries(
olc.interp_series(
'cable_control_1',
[0,t_trans,t_trans+t_sigma],
[0,0,line_ends[0]] ,
'sigma'
)

olc.interp_timeseries(
olc.interp_series(
'cable_control_2',
[0,t_trans,t_trans+t_sigma],
[0,0,line_ends[1]] ,
'sigma'
)

olc.interp_timeseries(
olc.interp_series(
'cable_control_3',
[0,t_trans,t_trans+t_sigma],
[0,0,line_ends[2]] ,
Expand Down
6 changes: 3 additions & 3 deletions Examples/23_structural_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ def main():
applied_force = [-2e6, 1e6, 1e6]

olc = OpenLoopControl(t_max=t_max)
olc.interp_timeseries(
olc.interp_series(
'struct_control_1',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[0]] ,
'sigma'
)

olc.interp_timeseries(
olc.interp_series(
'struct_control_2',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[1]] ,
'sigma'
)

olc.interp_timeseries(
olc.interp_series(
'struct_control_3',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[2]] ,
Expand Down
12 changes: 6 additions & 6 deletions Examples/25_rotor_position_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ def main():
fast_out = op.load_fast_out(os.path.join(run_dir,'NREL2p8/power_curve/base/NREL2p8_0.outb'), tmin=0)

olc = OpenLoopControl()
olc.ol_timeseries['time'] = fast_out[0]['Time']
olc.ol_timeseries['blade_pitch1'] = np.radians(fast_out[0]['BldPitch1'])
olc.ol_timeseries['blade_pitch2'] = np.radians(fast_out[0]['BldPitch2'])
olc.ol_timeseries['blade_pitch3'] = np.radians(fast_out[0]['BldPitch3'])
olc.ol_timeseries['generator_torque'] = fast_out[0]['GenTq'] * 1000
olc.ol_timeseries['azimuth'] = np.radians(fast_out[0]['Azimuth'])
olc.ol_series['time'] = fast_out[0]['Time']
olc.ol_series['blade_pitch1'] = np.radians(fast_out[0]['BldPitch1'])
olc.ol_series['blade_pitch2'] = np.radians(fast_out[0]['BldPitch2'])
olc.ol_series['blade_pitch3'] = np.radians(fast_out[0]['BldPitch3'])
olc.ol_series['generator_torque'] = fast_out[0]['GenTq'] * 1000
olc.ol_series['azimuth'] = np.radians(fast_out[0]['Azimuth'])

# Save initial RotSpeed, Azimuth for later
RotSpeed_0 = fast_out[0]['RotSpeed'][0]
Expand Down
33 changes: 2 additions & 31 deletions Examples/26_marine_hydro.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
import os
from rosco.toolbox.ofTools.case_gen.run_FAST import run_FAST_ROSCO
from rosco.toolbox.ofTools.case_gen import CaseLibrary as cl
#from rosco.toolbox.ofTools.fast_io import output_processing
#from rosco.toolbox.ofTools.fast_io.FAST_reader import InputReader_OpenFAST
#from rosco.toolbox.inputs.validation import load_rosco_yaml
#import matplotlib.pyplot as plt
#from rosco.toolbox.controller import OpenLoopControl


def main():
#directories
Expand Down Expand Up @@ -40,35 +36,10 @@ def main():
# r.controller_params = controller_params
r.save_dir = run_dir
r.rosco_dir = rosco_dir
# r.rosco_dll = '/Users/dzalkind/Tools/ROSCO-PRC/rosco/controller/build/libdiscon.dylib'

r.run_FAST()

print('here')


# op = output_processing.output_processing()
# op2 = output_processing.output_processing()

# md_out = op.load_fast_out([os.path.join(run_dir,'IEA15MW_cable/power_curve/base/IEA15MW_cable_0.MD.Line1.out')], tmin=0)
# local_vars = op2.load_fast_out([os.path.join(run_dir,'IEA15MW_cable/power_curve/base/IEA15MW_cable_0.RO.dbg2')], tmin=0)

# fig, axs = plt.subplots(4,1)
# axs[0].plot(local_vars[0]['Time'],local_vars[0]['CC_DesiredL'],label='CC_DesiredL')
# axs[1].plot(local_vars[0]['Time'],local_vars[0]['CC_ActuatedL'],label='CC_ActuatedL')
# axs[2].plot(local_vars[0]['Time'],local_vars[0]['CC_ActuatedDL'],label='CC_ActuatedDL')
# axs[3].plot(md_out[0]['Time'],md_out[0]['Seg20Lst'],label='Seg20Lst')

# [a.legend() for a in axs]
# [a.grid() for a in axs]

# if False:
# plt.show()
# else:
# plt.savefig(os.path.join(example_out_dir,'22_cable_control.png'))

# Check that the last segment of line 1 shrinks by 10 m
# np.testing.assert_almost_equal(md_out[0]['Seg20Lst'][-1] - md_out[0]['Seg20Lst'][0], line_ends[0], 2)



if __name__=="__main__":
Expand Down
14 changes: 8 additions & 6 deletions Examples/27_power_ref_control.py → Examples/27_soft_cut_out.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""
27_power_ref_control
--------------------
Run openfast with ROSCO and cable control
Demonstrate a simulation with a generator reference speed that changes with estimated wind speed
Set reference rotor speed as a function of wind speed (estimate in ROSCO)
27_soft_cut_out
---------------
Set up a control input to do a soft cut-out of a wind turbine at high wind speeds.
This example uses the first power reference control implementation (PRC_Mode of 1), where the user specifies the speed setpoint versus wind speed.
We can use this to track specific rotor speeds based on the wind speed, or de-rate/power boost using by changing the speed.
With PRC_Mode of 2, we can do this but also change the power rating with pitch and torque.

"""

import os
Expand Down Expand Up @@ -58,7 +60,7 @@ def main():

r = run_FAST_ROSCO()
r.tuning_yaml = parameter_filename
r.wind_case_fcn = cl.ramp # single step wind input
r.wind_case_fcn = cl.ramp # ramp wind input

if FULL_TEST:
# Full test
Expand Down
Loading
Loading