Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into rotor_position_sim
Browse files Browse the repository at this point in the history
  • Loading branch information
dzalkind committed Jul 24, 2023
2 parents 33f1c8f + 2f069da commit ebe0e2e
Show file tree
Hide file tree
Showing 41 changed files with 483 additions and 175 deletions.
2 changes: 1 addition & 1 deletion Examples/01_turbine_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
turbine.load_from_fast(
path_params['FAST_InputFile'],
os.path.join(tune_dir,path_params['FAST_directory']),
rot_source='txt',txt_filename=os.path.join(tune_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
rot_source='txt',txt_filename=os.path.join(tune_dir,path_params['rotor_performance_filename'])
)

# Print some basic turbine info
Expand Down
2 changes: 1 addition & 1 deletion Examples/03_tune_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
controller = ROSCO_controller.Controller(controller_params)

# Load turbine data from OpenFAST and rotor performance text file
cp_filename = os.path.join(tune_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
cp_filename = os.path.join(tune_dir,path_params['rotor_performance_filename'])
turbine.load_from_fast(
path_params['FAST_InputFile'],
os.path.join(tune_dir,path_params['FAST_directory']),
Expand Down
2 changes: 1 addition & 1 deletion Examples/04_simple_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
# controller = ROSCO_controller.Controller(controller_params)

# Load turbine data from OpenFAST and rotor performance text file
cp_filename = os.path.join(tune_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
cp_filename = os.path.join(tune_dir,path_params['rotor_performance_filename'])
turbine.load_from_fast(
path_params['FAST_InputFile'],
os.path.join(tune_dir,path_params['FAST_directory']),
Expand Down
34 changes: 1 addition & 33 deletions Examples/05_openfast_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,48 +41,16 @@
path_params['FAST_InputFile'],
os.path.join(this_dir,path_params['FAST_directory']),
rot_source='txt',
txt_filename=os.path.join(this_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
txt_filename=os.path.join(this_dir,path_params['rotor_performance_filename'])
)

# Tune controller
controller.tune_controller(turbine)

# Now double Kp_float and check that it's passed through
Kp_float = -18
controller_params['Kp_float'] = Kp_float
controller_params['tune_Fl'] = False
controller = ROSCO_controller.Controller(controller_params)
controller.tune_controller(turbine)
np.testing.assert_almost_equal(Kp_float,controller.Kp_float)

# Write parameter input file
param_file = os.path.join(this_dir,'DISCON.IN') # This must be named DISCON.IN to be seen by the compiled controller binary.
write_DISCON(turbine,controller,param_file=param_file, txt_filename=path_params['rotor_performance_filename'])

# Plot gain schedule
fig, ax = plt.subplots(2,2,constrained_layout=True,sharex=True)
ax = ax.flatten()
ax[0].plot(controller.v[len(controller.v_below_rated)+1:], controller.omega_pc_U)
ax[0].set_ylabel('omega_pc')

ax[1].plot(controller.v[len(controller.v_below_rated)+1:], controller.zeta_pc_U)
ax[1].set_ylabel('zeta_pc')

ax[2].plot(controller.v[len(controller.v_below_rated)+1:], controller.pc_gain_schedule.Kp)
ax[2].set_xlabel('Wind Speed')
ax[2].set_ylabel('Proportional Gain')

ax[3].plot(controller.v[len(controller.v_below_rated)+1:], controller.pc_gain_schedule.Ki)
ax[3].set_xlabel('Wind Speed')
ax[3].set_ylabel('Integral Gain')

plt.suptitle('Pitch Controller Gains')

if False:
plt.show()
else:
plt.savefig(os.path.join(example_out_dir,'05_GainSched.png'))

# Run OpenFAST
# --- May need to change fastcall if you use a non-standard command to call openfast
fastcall = 'openfast'
Expand Down
2 changes: 1 addition & 1 deletion Examples/06_peak_shaving.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
turbine.load_from_fast(
path_params['FAST_InputFile'],
os.path.join(tune_dir,path_params['FAST_directory']),
rot_source='txt',txt_filename=os.path.join(tune_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
rot_source='txt',txt_filename=os.path.join(tune_dir,path_params['rotor_performance_filename'])
)
# Tune controller
controller.tune_controller(turbine)
Expand Down
2 changes: 1 addition & 1 deletion Examples/10_linear_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
path_params['FAST_InputFile'],
os.path.join(this_dir,path_params['FAST_directory']),
rot_source='txt',
txt_filename=os.path.join(tune_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
txt_filename=os.path.join(tune_dir,path_params['rotor_performance_filename'])
)

# Tune controller
Expand Down
8 changes: 5 additions & 3 deletions Examples/11_robust_tuning.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Controller tuning to satisfy a robustness criteria
-------------------------------------
NOTE: This example necessitates the mbc3 through either pyFAST or WEIS
pyFAST is the easiest to install by cloning https://github.com/OpenFAST/python-toolbox and
running `python setup.py develop` from your conda environment
In this example:
- setup ROSCO's robust tuning methods for the IEA15MW on the UMaine Semi-sub
Expand Down Expand Up @@ -53,12 +55,12 @@ def run_example():
controller = ROSCO_controller.Controller(controller_params)
turbine.load_from_fast(
path_params['FAST_InputFile'],
os.path.join(this_dir, path_params['FAST_directory']),
rot_source='txt', txt_filename=os.path.join(this_dir,path_params['FAST_directory'],path_params['rotor_performance_filename'])
os.path.join(tune_dir, path_params['FAST_directory']),
rot_source='txt', txt_filename=os.path.join(tune_dir,path_params['rotor_performance_filename'])
)

# Fix path params for robust setup
path_params['FAST_directory'] = os.path.join(this_dir, path_params['FAST_directory'])
path_params['FAST_directory'] = os.path.join(tune_dir, path_params['FAST_directory'])

controller.tune_controller(turbine)
k_float = controller.Kp_float
Expand Down
2 changes: 1 addition & 1 deletion Examples/14_open_loop_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
turbine.load_from_fast(path_params['FAST_InputFile'], \
os.path.join(this_dir,path_params['FAST_directory']), \
rot_source='txt',\
txt_filename=os.path.join(this_dir,path_params['FAST_directory'],path_params['rotor_performance_filename']))
txt_filename=os.path.join(this_dir,path_params['rotor_performance_filename']))

# Tune controller
controller.tune_controller(turbine)
Expand Down
165 changes: 165 additions & 0 deletions Examples/24_floating_feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
'''
----------- 23_structural_control ------------------------
Run openfast with ROSCO and all the floating feedback methods
-----------------------------------------------
Floating feedback methods available in ROSCO/ROSCO_Toolbox
1. Automated tuning, constant for all wind speeds
2. Automated tuning, varies with wind speed
3. Direct tuning, constant for all wind speeds
4. Direct tuning, varies with wind speeds
'''

import os, platform
from ROSCO_toolbox.ofTools.case_gen.run_FAST import run_FAST_ROSCO
from ROSCO_toolbox.ofTools.case_gen import CaseLibrary as cl
import numpy as np
from ROSCO_toolbox.ofTools.fast_io.FAST_reader import InputReader_OpenFAST
from ROSCO_toolbox.inputs.validation import load_rosco_yaml
from ROSCO_toolbox.controller import OpenLoopControl
from ROSCO_toolbox.tune import yaml_to_objs
from ROSCO_toolbox.utilities import write_DISCON, read_DISCON
from ROSCO_toolbox import controller as ROSCO_controller
from ROSCO_toolbox.ofTools.fast_io import output_processing
import matplotlib.pyplot as plt



#directories
this_dir = os.path.dirname(os.path.abspath(__file__))
rosco_dir = os.path.dirname(this_dir)
example_out_dir = os.path.join(this_dir,'examples_out')
os.makedirs(example_out_dir,exist_ok=True)

if platform.system() == 'Windows':
lib_name = os.path.realpath(os.path.join(this_dir, '../ROSCO/build/libdiscon.dll'))
elif platform.system() == 'Darwin':
lib_name = os.path.realpath(os.path.join(this_dir, '../ROSCO/build/libdiscon.dylib'))
else:
lib_name = os.path.realpath(os.path.join(this_dir, '../ROSCO/build/libdiscon.so'))


def main():

# Input yaml and output directory
parameter_filename = os.path.join(rosco_dir,'Tune_Cases/IEA15MW.yaml')
run_dir = os.path.join(example_out_dir,'24_floating_feedback')
os.makedirs(run_dir,exist_ok=True)

controller, turbine, path_params = yaml_to_objs(parameter_filename)

# First, let's write the DISCONs for each method
param_files = []

# Method 1: Automated tuning, constant for all wind speeds
controller_params_1 = controller.controller_params # numbers correspond to methods above
param_file = os.path.join(run_dir,'DISCON_Fl_1.IN')
param_files.append(param_file)
write_DISCON(turbine,controller,
param_file=param_file,
txt_filename=path_params['rotor_performance_filename'])


# Method 2: Automated tuning, all wind speeds
controller_params_2 = controller_params_1.copy()
controller_params_2['U_Fl'] = 'all'
controller_params_2['tune_Fl'] = True
controller = ROSCO_controller.Controller(controller_params_2)
controller.tune_controller(turbine)
param_file = os.path.join(run_dir,'DISCON_Fl_2.IN')
param_files.append(param_file)
write_DISCON(turbine,controller,
param_file=param_file,
txt_filename=path_params['rotor_performance_filename'])

# Method 3: Direct tuning, constant for all wind speeds
controller_params_3 = controller_params_1.copy()
Kp_float = -18
controller_params_3['Kp_float'] = Kp_float
controller_params_3['tune_Fl'] = False
controller = ROSCO_controller.Controller(controller_params_3)
controller.tune_controller(turbine)
np.testing.assert_almost_equal(Kp_float,controller.Kp_float) # Check that it's passed through correctly
param_file = os.path.join(run_dir,'DISCON_Fl_3.IN')
param_files.append(param_file)
write_DISCON(turbine,controller,
param_file=param_file,
txt_filename=path_params['rotor_performance_filename'])

# Method 4: Direct tuning, varies with wind speeds
controller_params_4 = controller_params_1.copy()
controller_params_4['U_Fl'] = [12,16,25]
controller_params_4['Kp_float'] = [-12,-6,-3]
controller_params_4['tune_Fl'] = False
controller = ROSCO_controller.Controller(controller_params_4)
controller.tune_controller(turbine)
param_file = os.path.join(run_dir,'DISCON_Fl_4.IN')
param_files.append(param_file)
write_DISCON(turbine,controller,
param_file=os.path.join(run_dir,'DISCON_Fl_4.IN'),
txt_filename=path_params['rotor_performance_filename'])

# Read all DISCONs and make into case_inputs
case_inputs = {}
discon_lists = {}
for discon in param_files:
discon_vt = read_DISCON(discon)
for discon_input in discon_vt:
if discon_input not in discon_lists: # initialize
discon_lists[discon_input] = []
discon_lists[discon_input].append(discon_vt[discon_input])

for discon_input, input in discon_lists.items():
case_inputs[('DISCON_in',discon_input)] = {'vals': input, 'group': 2}


# simulation set up
r = run_FAST_ROSCO()
r.tuning_yaml = parameter_filename
r.wind_case_fcn = cl.simp_step # single step wind input
r.wind_case_opts = {
'U_start': [13],
'U_end': [16],
'TMax': 100,
'TStep': 50,
}
r.case_inputs = case_inputs
r.save_dir = run_dir
r.rosco_dir = rosco_dir
r.n_cores = 4
r.run_FAST()

op = output_processing.output_processing()
op_dbg = output_processing.output_processing()
op_dbg2 = output_processing.output_processing()

out_files = [os.path.join(run_dir,f'IEA15MW/simp_step/base/IEA15MW_{i_case}.outb') for i_case in range(4)]
dbg_files = [os.path.join(run_dir,f'IEA15MW/simp_step/base/IEA15MW_{i_case}.RO.dbg') for i_case in range(4)]
dbg2_files = [os.path.join(run_dir,f'IEA15MW/simp_step/base/IEA15MW_{i_case}.RO.dbg2') for i_case in range(4)]

fst_out = op.load_fast_out(out_files, tmin=0)
debug_vars = op_dbg.load_fast_out(dbg_files, tmin=0)
local_vars = op_dbg2.load_fast_out(dbg2_files, tmin=0)

comb_out = [None] * len(fst_out)
for i, (r_out, f_out) in enumerate(zip(debug_vars,fst_out)):
r_out.update(f_out)
comb_out[i] = r_out
for i, (r_out2, f_out) in enumerate(zip(local_vars,comb_out)):
r_out2.update(f_out)
comb_out[i] = r_out2

cases = {}
cases['Fl Sigs.'] = ['Wind1VelX','Kp_Float', 'Fl_PitCom', 'BldPitch1','PtfmPitch']#,'PtfmPitch','PtfmYaw','NacYaw']
fig, ax = op.plot_fast_out(comb_out,cases, showplot=True)

if False:
plt.show()
else:
plt.savefig(os.path.join(run_dir,'24_floating_feedback.png'))


if __name__=="__main__":
main()
29 changes: 21 additions & 8 deletions Examples/ROSCO_walkthrough.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Examples/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
'21_optional_inputs',
'22_cable_control',
'23_structural_control',
'24_floating_feedback',
'update_rosco_discons',
]

Expand Down
39 changes: 18 additions & 21 deletions ROSCO/rosco_registry/rosco_types.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,17 @@ ControlParameters:
Fl_Mode:
<<: *integer
description: Floating specific feedback mode {0 - no nacelle velocity feedback, 1 - nacelle velocity feedback}
Fl_n:
<<: *integer
description: Number of Fl_Kp for gain scheduling
Fl_Kp:
<<: *real
description: Nacelle velocity proportional feedback gain [s]
allocatable: True
Fl_U:
<<: *real
description: Wind speeds for scheduling Fl_Kp [m/s]
allocatable: True

# Trailing edge flaps
Flp_Mode:
Expand Down Expand Up @@ -548,30 +556,16 @@ ControlParameters:
<<: *real
allocatable: True
description: AWC clocking angle [deg]

# Active wake control
AWC_Mode:
<<: *integer
description: Active wake control mode [0 - unused, 1 - SNL method, 2 - NREL method]
AWC_NumModes:
<<: *integer
description: AWC- Number of modes to include [-]
AWC_n:

# Pitch actuator error
PF_Mode:
<<: *integer
allocatable: True
description: AWC azimuthal mode [-]
AWC_freq:
<<: *real
allocatable: True
description: AWC frequency [Hz]
AWC_amp:
<<: *real
allocatable: True
description: AWC amplitude [deg]
AWC_clockangle:
description: Pitch actuator fault mode {0 - not used, 1 - offsets on one or more blades}
PF_Offsets:
<<: *real
description: Pitch actuator fault offsets for blade 1-3 [rad/s]
allocatable: True
description: AWC clocking angle [deg]


# External Control
Ext_Mode:
Expand Down Expand Up @@ -1101,6 +1095,9 @@ LocalVariables:
TestType:
<<: *real
description: Test variable, no use
Kp_Float:
<<: *real
description: Local, instantaneous Kp_Float, scheduled on wind speed, if desired
VS_MaxTq:
<<: *real
description: Maximum allowable generator torque [Nm].
Expand Down
Loading

0 comments on commit ebe0e2e

Please sign in to comment.