From 7c4880a6fafb69373d990a6f5c220ef3d8f60265 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 9 Apr 2026 10:06:20 -0600 Subject: [PATCH 01/21] Account for gearbox ratio in windio conversion --- Examples/34_discon_windio.py | 1 + rosco/toolbox/inputs/windio.py | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Examples/34_discon_windio.py b/Examples/34_discon_windio.py index 025327e2a..9899630e4 100644 --- a/Examples/34_discon_windio.py +++ b/Examples/34_discon_windio.py @@ -24,6 +24,7 @@ def main(): discon_yaml_map = { os.path.join(examples_dir,'Test_Cases','IEA-15-240-RWT', 'IEA-15-240-RWT-UMaineSemi', 'IEA-15-240-RWT-UMaineSemi_DISCON.IN'): os.path.join(examples_dir,'example_inputs', 'windio', 'IEA-15-240-RWT.yaml'), os.path.join(examples_dir,'Test_Cases','IEA-15-240-RWT', 'IEA-15-240-RWT-Monopile', 'IEA-15-240-RWT-Monopile_DISCON.IN'): os.path.join(examples_dir,'example_inputs', 'windio', 'IEA-15-240-RWT.yaml'), + os.path.join(examples_dir,'Test_Cases','NREL-5MW', 'DISCON.IN'): os.path.join(examples_dir,'example_inputs', 'windio', 'nrel5mw.yaml'), } # # For updating windIO repo diff --git a/rosco/toolbox/inputs/windio.py b/rosco/toolbox/inputs/windio.py index dcf6796b2..0138e43cb 100644 --- a/rosco/toolbox/inputs/windio.py +++ b/rosco/toolbox/inputs/windio.py @@ -14,10 +14,11 @@ def windio_to_discon(windio_dict): rosco_vt_from_windio = {} # Convert back from windIO to ROSCO units and format - rosco_vt_from_windio['VS_MinOMSpd'] = windio_dict['control']['min_rotor_speed'] / radps2rpm - rosco_vt_from_windio['PC_RefSpd'] = windio_dict['control']['rated_rotor_speed'] / radps2rpm + gb_ratio = windio_dict['components']['drivetrain']['gearbox']['gear_ratio'] + rosco_vt_from_windio['VS_MinOMSpd'] = windio_dict['control']['min_rotor_speed'] / radps2rpm * gb_ratio + rosco_vt_from_windio['PC_RefSpd'] = windio_dict['control']['rated_rotor_speed'] / radps2rpm * gb_ratio rosco_vt_from_windio['VS_RtPwr'] = windio_dict['control']['rated_power'] # W in windIO, W in ROSCO - rosco_vt_from_windio['SD_MaxGenSpd'] = windio_dict['control']['max_rotor_speed'] / radps2rpm + rosco_vt_from_windio['SD_MaxGenSpd'] = windio_dict['control']['max_rotor_speed'] / radps2rpm * gb_ratio rosco_vt_from_windio['VS_MaxTq'] = windio_dict['control']['max_gen_torque'] rosco_vt_from_windio['VS_MaxRat'] = windio_dict['control']['max_torque_rate'] @@ -55,11 +56,11 @@ def windio_to_discon(windio_dict): def discon_to_windio(rosco_vt): windio_control = {} - windio_control['min_rotor_speed'] = rosco_vt['VS_MinOMSpd'] * radps2rpm - windio_control['rated_rotor_speed'] = rosco_vt['PC_RefSpd'] * radps2rpm + windio_control['min_rotor_speed'] = rosco_vt['VS_MinOMSpd'] * radps2rpm / rosco_vt['WE_GearboxRatio'] + windio_control['rated_rotor_speed'] = rosco_vt['PC_RefSpd'] * radps2rpm / rosco_vt['WE_GearboxRatio'] windio_control['rated_power'] = rosco_vt['VS_RtPwr'] # W in ROSCO, W in windIO - windio_control['max_rotor_speed'] = rosco_vt['SD_MaxGenSpd'] * radps2rpm + windio_control['max_rotor_speed'] = rosco_vt['SD_MaxGenSpd'] * radps2rpm / rosco_vt['WE_GearboxRatio'] windio_control['max_gen_torque'] = rosco_vt['VS_MaxTq'] # Nm in ROSCO, N*m in windIO windio_control['max_torque_rate'] = rosco_vt['VS_MaxRat'] # Nm/s in ROSCO, N*m/s in windIO From 9d759276c9c570e1de62435fb9ce7786c5a67d85 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 2 Sep 2026 15:57:47 -0600 Subject: [PATCH 02/21] Add docs for building the docs --- docs/index.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index fe11ca996..fa3047a35 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,6 +21,17 @@ The toolbox has other capabilities like simple 1-DOF turbine simulations for qui Source code for ROSCO toolset can be found in this `github repository `_ and it can be installed following the instructions provided in :ref:`install`. +Building the Documentation +-------------------------- + +To build these docs locally, navigate to the :code:`docs` directory and run: + +.. code-block:: bash + + sphinx-build . ./_build/ + +This generates HTML documentation in the :code:`_build` folder. + **Documentation Directory** .. toctree:: From 0b4011d524b6cd8a3303ef1321ecd9e28d38130d Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 3 Sep 2026 17:04:57 -0600 Subject: [PATCH 03/21] Add more generic param_sweeps --- rosco/toolbox/ofTools/case_gen/CaseLibrary.py | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/rosco/toolbox/ofTools/case_gen/CaseLibrary.py b/rosco/toolbox/ofTools/case_gen/CaseLibrary.py index d5fb3a62a..2b59aeccf 100644 --- a/rosco/toolbox/ofTools/case_gen/CaseLibrary.py +++ b/rosco/toolbox/ofTools/case_gen/CaseLibrary.py @@ -547,16 +547,19 @@ def test_pitch_offset(start_group, **control_sweep_opts): def sweep_yaml_input(start_group, **control_sweep_opts): ''' - Sweep any single tuning yaml input + Sweep any single tuning yaml input, or a list of full controller configurations control_sweep_opts: control_param: name of parameter param_values: values of parameter (1D array) + or + param_sweeps: list of controller_params overrides, one dict per case ''' - required_inputs = [('control_param','discon_param'), 'param_values'] - check_inputs(control_sweep_opts,required_inputs) + if 'param_sweeps' not in control_sweep_opts: + required_inputs = [('control_param','discon_param'), 'param_values'] + check_inputs(control_sweep_opts,required_inputs) # load default params control_param_yaml = control_sweep_opts['tuning_yaml'] @@ -573,14 +576,26 @@ def sweep_yaml_input(start_group, **control_sweep_opts): case_inputs = {} discon_lists = {} - for param_value in control_sweep_opts['param_values']: - controller_params = control_sweep_opts['controller_params'].copy() + base_params = control_sweep_opts['controller_params'] - if 'control_param' in control_sweep_opts: - controller_params[control_sweep_opts['control_param']] = param_value - elif 'discon_param' in control_sweep_opts: - controller_params.setdefault('DISCON', {}) - controller_params['DISCON'][control_sweep_opts['discon_param']] = param_value + if 'param_sweeps' in control_sweep_opts: + # ponytail: shallow merge, a nested override (e.g. DISCON) replaces the whole sub-dict + param_sweeps = [{**base_params, **overrides} for overrides in control_sweep_opts['param_sweeps']] + else: + param_sweeps = [] + for param_value in control_sweep_opts['param_values']: + controller_params = base_params.copy() + + if 'control_param' in control_sweep_opts: + controller_params[control_sweep_opts['control_param']] = param_value + elif 'discon_param' in control_sweep_opts: + controller_params['DISCON'] = { + **base_params.get('DISCON', {}), + control_sweep_opts['discon_param']: param_value, + } + param_sweeps.append(controller_params) + + for controller_params in param_sweeps: controller = ROSCO_controller.Controller(controller_params) # tune default controller From 26b511fe302d7ca3e9bca2714a145eebee8749f3 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 3 Sep 2026 17:05:11 -0600 Subject: [PATCH 04/21] Clean up and sweep all cases in ex 31 --- Examples/31_fixed_pitch_mhk.py | 159 ++++++++++++++------------------- 1 file changed, 65 insertions(+), 94 deletions(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index 8fd572f96..c88139d65 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -55,8 +55,7 @@ def main(): - FULL_TEST = False # Run a full test locally (True) or a shorter one for CI - sim_config = 1 # Choose which simulation configuration (1-6) + FULL_TEST = True # Run a full test locally (True) or a shorter one for CI # Input yaml and output directory parameter_filename = os.path.join(this_dir, 'Tune_Cases/RM1_MHK_FBP.yaml') @@ -80,78 +79,57 @@ def main(): ) + ### Control configurations: overrides applied to the tuning yaml controller_params + control_configs = { + 'Constant Power Underspeed': { # should be the default + 'VS_FBP': 3, # Power reference + 'VS_FBP_speed_mode': 0, + 'VS_FBP_U': [2.0, 4.0], + 'VS_FBP_P': [1.0, 1.0], + }, + 'Constant Power Overspeed': { + 'VS_FBP': 2, # WSE reference + 'VS_FBP_speed_mode': 1, + 'VS_FBP_U': [2.0, 4.0], + 'VS_FBP_P': [1.0, 1.0], + }, + 'Linear Increasing Power': { + 'VS_FBP_speed_mode': 0, + 'VS_FBP_U': [2.0, 4.0], + 'VS_FBP_P': [1.0, 2.0], + }, + 'Increasing Leveled Power': { + 'VS_FBP_U': [2.0, 3.0], + 'VS_FBP_P': [1.0, 2.0], + }, + 'Generic User-Defined': { + 'VS_FBP': 2, # WSE reference + 'VS_FBP_U': [2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0], + 'VS_FBP_P': [1.0, 1.3, 1.6, 1.8, 1.9, 2.0, 1.9, 1.8, 1.7, 1.6, 1.5], + }, + 'Constant Power Overspeed, Lookup Table': { + 'VS_FBP': 1, # Constant power overspeed + 'VS_FBP_speed_mode': 1, + 'VS_FBP_U': [2.0, 4.0], + 'VS_FBP_P': [1.0, 1.0], + 'VS_ControlMode': 1, + }, + } + ### Tune controller cases - # Constant power underspeed (should be the default) - controller_params_1 = controller_params.copy() - controller_params_1['VS_FBP'] = 3 # Power reference - controller_params_1['VS_FBP_speed_mode'] = 0 - controller_params_1['VS_FBP_U'] = [2.0, 4.0] - controller_params_1['VS_FBP_P'] = [1.0, 1.0] - controller_1 = ROSCO_controller.Controller(controller_params_1) - controller_1.tune_controller(turbine) - - # Constant power overspeed - controller_params_2 = controller_params.copy() - controller_params_2['VS_FBP'] = 2 # Switch to WSE reference - controller_params_2['VS_FBP_speed_mode'] = 1 - controller_params_2['VS_FBP_U'] = [2.0, 4.0] - controller_params_2['VS_FBP_P'] = [1.0, 1.0] - controller_2 = ROSCO_controller.Controller(controller_params_2) - controller_2.tune_controller(turbine) - - # Linear increasing power - controller_params_3 = controller_params.copy() - controller_params_3['VS_FBP_speed_mode'] = 0 - controller_params_2['VS_FBP_U'] = [2.0, 4.0] - controller_params_3['VS_FBP_P'] = [1.0, 2.0] - controller_3 = ROSCO_controller.Controller(controller_params_3) - controller_3.tune_controller(turbine) - - # Linear increasing power, leveling out - controller_params_4 = controller_params.copy() - controller_params_4['VS_FBP_U'] = [2.0, 3.0] - controller_params_4['VS_FBP_P'] = [1.0, 2.0] - controller_4 = ROSCO_controller.Controller(controller_params_4) - controller_4.tune_controller(turbine) - - # Generic numeric function - controller_params_5 = controller_params.copy() - controller_params_5['VS_FBP'] = 2 # WSE reference - controller_params_5['VS_FBP_U'] = [2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0] - controller_params_5['VS_FBP_P'] = [1.0, 1.3, 1.6, 1.8, 1.9, 2.0, 1.9, 1.8, 1.7, 1.6, 1.5] - controller_5 = ROSCO_controller.Controller(controller_params_5) - controller_5.tune_controller(turbine) - - # Constant power overspeed, nonlinear lookup table control - controller_params_6 = controller_params.copy() - controller_params_6['VS_FBP'] = 1 # Constant power overspeed - controller_params_6['VS_FBP_speed_mode'] = 1 - controller_params_6['VS_FBP_U'] = [2.0, 4.0] - controller_params_6['VS_FBP_P'] = [1.0, 1.0] - controller_params_6['VS_ControlMode'] = 1 - controller_6 = ROSCO_controller.Controller(controller_params_6) - controller_6.tune_controller(turbine) - - - plot_labels = ['Constant Power Underspeed', 'Constant Power Overspeed', 'Linear Increasing Power', 'Increasing Leveled Power', 'Generic User-Defined'] + controllers = [] + for overrides in control_configs.values(): + controller = ROSCO_controller.Controller({**controller_params, **overrides}) + controller.tune_controller(turbine) + controllers.append(controller) + fig, axs = plt.subplots(3,1) - axs[0].plot(controller_1.v, controller_1.power_op, label=plot_labels[0]) - axs[0].plot(controller_2.v, controller_2.power_op, label=plot_labels[1], linestyle='--') - axs[0].plot(controller_3.v, controller_3.power_op, label=plot_labels[2]) - axs[0].plot(controller_4.v, controller_4.power_op, label=plot_labels[3]) - axs[0].plot(controller_5.v, controller_5.power_op, label=plot_labels[4]) + for (label, cont, line_style) in zip(control_configs, controllers, ['-','--','-','-','-',':']): + axs[0].plot(cont.v, cont.power_op, label=label, linestyle=line_style) + axs[1].plot(cont.v, cont.omega_gen_op, label=label, linestyle=line_style) + axs[2].plot(cont.v, cont.tau_op, label=label, linestyle=line_style) axs[0].set_ylabel('Gen Power [W]') - axs[1].plot(controller_1.v, controller_1.omega_gen_op, label=plot_labels[0]) - axs[1].plot(controller_2.v, controller_2.omega_gen_op, label=plot_labels[1], linestyle='--') - axs[1].plot(controller_3.v, controller_3.omega_gen_op, label=plot_labels[2]) - axs[1].plot(controller_4.v, controller_4.omega_gen_op, label=plot_labels[3]) - axs[1].plot(controller_5.v, controller_5.omega_gen_op, label=plot_labels[4]) axs[1].set_ylabel('Gen Speed [rad/s]') - axs[2].plot(controller_1.v, controller_1.tau_op, label=plot_labels[0]) - axs[2].plot(controller_2.v, controller_2.tau_op, label=plot_labels[1], linestyle='--') - axs[2].plot(controller_3.v, controller_3.tau_op, label=plot_labels[2]) - axs[2].plot(controller_4.v, controller_4.tau_op, label=plot_labels[3]) - axs[2].plot(controller_5.v, controller_5.tau_op, label=plot_labels[4]) axs[2].set_ylabel('Gen Torque [N m]') axs[2].set_xlabel('Flow Speed [m/s]') axs[0].legend(loc='upper left', bbox_to_anchor=(.2, 2.35)) @@ -163,12 +141,12 @@ def main(): if False: plt.show() else: - fig_fname = os.path.join(example_out_dir, '30_fixed_pitch_mhk_sched.png') + fig_fname = os.path.join(example_out_dir, '31_fixed_pitch_mhk_sched.png') print('Saving figure ' + fig_fname) plt.savefig(fig_fname,bbox_inches='tight',) - # Write parameter input file for constant power underspeed controller - run_dir = os.path.join(example_out_dir, f'31_MHK/{sim_config}_config') + # Simulate all control configurations, in parallel + run_dir = os.path.join(example_out_dir, '31_MHK') os.makedirs(run_dir,exist_ok=True) # simulation set up @@ -177,20 +155,6 @@ def main(): else: TMax = 5 - all_controller_params = [ - controller_params_1, - controller_params_2, - controller_params_3, - controller_params_4, - controller_params_5, - controller_params_6, - ] - - if sim_config in range(1,len(all_controller_params)+1): - controller_params = all_controller_params[sim_config-1] - else: - raise Exception(f'Invalid sim_config of {sim_config}. Note the 1-indexing.') - r = run_FAST_ROSCO() r.tuning_yaml = parameter_filename r.wind_case_fcn = cl.power_curve @@ -199,24 +163,31 @@ def main(): 'TMax': TMax, } r.case_inputs = {} - r.controller_params = controller_params + r.control_sweep_fcn = cl.sweep_yaml_input + r.control_sweep_opts = {'param_sweeps': list(control_configs.values())} r.save_dir = run_dir r.rosco_dir = rosco_dir + r.n_cores = min(len(control_configs), os.cpu_count()) r.run_FAST() op = output_processing.output_processing() - fast_out = op.load_fast_out([os.path.join(run_dir,'RM1_MHK_FBP_0.out')], tmin=0) + out_files = [os.path.join(run_dir,f'RM1_MHK_FBP_{i}.out') for i in range(len(control_configs))] + fast_out = op.load_fast_out(out_files, tmin=0) + assert len(fast_out) == len(control_configs) + fig, axs = plt.subplots(4,1) - axs[0].plot(fast_out[0]['Time'], fast_out[0]['Wind1VelX'], label='Constant Power Underspeed') + for label, fo in zip(control_configs, fast_out): + axs[0].plot(fo['Time'], fo['Wind1VelX'], label=label) + axs[1].plot(fo['Time'], fo['GenSpeed'] * 2*np.pi/60, label=label) + axs[2].plot(fo['Time'], fo['GenTq'] * 1e3, label=label) + axs[3].plot(fo['Time'], fo['GenPwr'] * 1e3, label=label) axs[0].set_ylabel('Flow Speed [m/s]',rotation=0, labelpad=50) - axs[1].plot(fast_out[0]['Time'], fast_out[0]['GenSpeed'] * 2*np.pi/60, label='Constant Power Underspeed') axs[1].set_ylabel('Gen Speed [rad/s]',rotation=0, labelpad=50) - axs[2].plot(fast_out[0]['Time'], fast_out[0]['GenTq'] * 1e3, label='Constant Power Underspeed') axs[2].set_ylabel('Gen Torque [N m]',rotation=0, labelpad=50) - axs[3].plot(fast_out[0]['Time'], fast_out[0]['GenPwr'] * 1e3, label='Constant Power Underspeed') axs[3].set_ylabel('Gen Power [W]',rotation=0, labelpad=50) axs[3].set_xlabel('Time [s]') + axs[0].legend(loc='upper left', bbox_to_anchor=(.2, 1.9)) plt.subplots_adjust(hspace=0.5) fig.align_ylabels() @@ -227,7 +198,7 @@ def main(): if False: plt.show() else: - fig_fname = os.path.join(example_out_dir, '30_fixed_pitch_mhk_sim.png') + fig_fname = os.path.join(example_out_dir, '31_fixed_pitch_mhk_sim.png') print('Saving figure ' + fig_fname) plt.savefig(fig_fname, bbox_inches='tight') From 89cb6f8e625fb083b36308e34af515c27c3f992b Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 3 Sep 2026 17:05:24 -0600 Subject: [PATCH 05/21] Add a few more checks to FBP modes --- rosco/toolbox/controller.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/rosco/toolbox/controller.py b/rosco/toolbox/controller.py index 189221c4e..3a0d2a590 100644 --- a/rosco/toolbox/controller.py +++ b/rosco/toolbox/controller.py @@ -112,11 +112,21 @@ def __init__(self, controller_params): self.min_pitch = controller_params['min_pitch'] if self.VS_FBP > 0: - - # Fail if generator torque enabled in Region 3 but pitch control not disabled (may enable these modes to operate together in the future) - if self.PC_ControlMode != 0: - raise Exception( - 'rosco.toolbox:controller: PC_ControlMode must be 0 if VS_FBP > 0') + + # Mode conflicts asserted in ReadSetParameters.f90; ROSCO aborts on these at runtime, so fail here instead of writing a config that cannot run + for mode_name in ['PC_ControlMode', 'VS_ConstPower', 'PRC_Mode']: + if getattr(self, mode_name) != 0: + raise Exception( + f'rosco.toolbox:controller: {mode_name} must be 0 if VS_FBP > 0') + + # VS_FBP = 1 runs the fixed control law tau = min(P_rated/omega, K*omega^2), so ROSCO ignores the power curve and speed mode. + # Generate the schedule it will actually follow: the table still sets the initial generator torque and the tuned gain schedule. + if self.VS_FBP == 1: + if self.fbp_speed_mode != 1 or self.fbp_power_mode != 0 or np.any(np.atleast_1d(self.fbp_P) != 1.0): + print('WARNING: VS_FBP = 1 always follows a constant rated power, overspeed schedule. Ignoring VS_FBP_speed_mode, VS_FBP_power_mode, and VS_FBP_P.') + self.fbp_speed_mode = 1 + self.fbp_power_mode = 0 + self.fbp_P = np.ones(len(np.atleast_1d(self.fbp_U))) if self.Flp_Mode > 0: if 'flp_kp_norm' in controller_params and 'flp_tau' in controller_params: @@ -270,10 +280,6 @@ def tune_controller(self, turbine): # Construct power schedule differently based on pitch control configuration if self.VS_FBP > 0: # If using torque control in Region 3 - # Check if constant power control disabled (may be implemented to work concurrently in the future) - if self.VS_ConstPower != 0: - raise Exception("VS_ConstPower must be 0 when VS_FBP > 0") - # Begin with user-defined power curve from input yaml (default constant rated power) f_P_user_defined = interpolate.interp1d(self.fbp_U, self.fbp_P, fill_value=(self.fbp_P[0], self.fbp_P[-1]), bounds_error=False) P_user_defined = f_P_user_defined(v) @@ -384,7 +390,7 @@ def tune_controller(self, turbine): if self.VS_FBP == 3: # The simulation will crash if we have a nonmonotonic schedule, so fail to generate the config and alert the user if np.any(np.diff(tau_op) <= 0): - raise Exception("VS controller reference torque interpolation is selected (VS_FBP_ref_mode == 1), but computed generator torque schedule is not monotonically increasing. Reconfigure power curve, ensure VS_FBP_speed_mode == 0, or switch VS_FBP to 2.") + raise Exception("VS_FBP = 3 (torque-lookup reference tracking) is selected, but the computed generator torque schedule is not strictly increasing, so it cannot be inverted. Set VS_FBP_speed_mode = 0 (underspeed) with a nondecreasing power curve, or switch VS_FBP to 2.") # Full Cx surface gradients From c08808f79f3a07bda96e7bbc27c822581b0d1159 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 3 Sep 2026 17:05:34 -0600 Subject: [PATCH 06/21] Review MHK control docs --- docs/source/marine_hydro.rst | 353 +++++++++++++++++++++++++++++------ 1 file changed, 300 insertions(+), 53 deletions(-) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index cbde54968..6982a15e7 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -3,15 +3,42 @@ ROSCO Control of Marine Hydrokinetic Turbines (MHKs) ==================================================== +.. note:: + The fixed-blade-pitch (FBP) control method, its implementation in ROSCO and the + ROSCO toolbox, and the verification study documented here were developed by + **David Stockhouse** (University of Colorado). + This page is adapted from his presentation *Fixed-Blade-Pitch Control in ROSCO + for MHK Turbines* [Stockhouse2025]_. + + Introduction --------------- -Here, we detail the control of MHK turbines in above rated flow speeds. -In below rated flow speeds, torque control is used to operate the turbine at it's maximum tip speed ratio (TSR) and pitch angle. - -For MHK turbines equipped with pitch actuators, those turbine can can use control schemes similar to those used by wind turbines, which reduce the Cp surface by increasing the pitch angle (black). -For MHK turbines without pitch actuation, we provide a few control methods for controlling the power of the turbine using only torque control. -Overspeed control increases the TSR along the fixed pitch line on the Cp surface below (blue circle) by decreasing the generator torque. +Here, we detail the control of MHK turbines in above-rated flow speeds. +In below-rated flow speeds, torque control is used to operate the turbine at its +maximum tip speed ratio (TSR) and pitch angle, as it is for wind turbines +[Johnson2006]_. + +The ROSCO baseline for marine turbines is adapted from the variable-pitch wind +turbine controller [Abbas2022]_. +Many marine turbine designs, however, lack pitch actuators: the cost, complexity, +and reliability burden of a submerged pitch system is often prohibitive. +Assuming a fixed (optimal) blade pitch, we instead redefine the operating schedule +of TSR to find the equilibrium between hydrodynamic and generator torque. +This trades pitch actuation for trade-offs in blade loads, rotor speed, generator +sizing, and environmental impact, which are discussed throughout this page. + +Regulating power above rated requires a means of reducing the power coefficient +:math:`C_p`. +For MHK turbines equipped with pitch actuators, those turbines can use control +schemes similar to those used by wind turbines, which reduce the :math:`C_p` +surface by increasing the pitch angle and decreasing TSR (black). +For MHK turbines without pitch actuation, we provide a few control methods for +controlling the power of the turbine using only torque control. +Any such controller is constrained by the requirement that steady-state operating +points be in equilibrium with the inflow torque. +Overspeed control increases the TSR along the fixed pitch line on the :math:`C_p` +surface below (blue circle) by decreasing the generator torque. Underspeed control decreases the TSR (red circle) by increasing the torque. .. _cp_surface_annotated: @@ -19,12 +46,19 @@ Underspeed control decreases the TSR (red circle) by increasing the torque. :align: center :width: 90% +The feature set added to ROSCO consists of an FBP generator torque controller that +is configurable to automatically generate an overspeed or underspeed operating +schedule, a configurable control approach for realizing that operating schedule, +and automatically tuned feedback gains. + Over/Underspeed Reference Setpoints ----------------------------------- -The steady state generator-speed setpoints are determined by the Cp contour. -Overspeed achieves up to 3x rated speed, which has additional consequences for blade loads (e.g., cavitation) +The steady state generator-speed setpoints are determined by the :math:`C_p` +contours intersecting with the fine-pitch line. +Overspeed achieves up to 3x rated speed, which has additional consequences for +blade loads (e.g., cavitation, high thrust). .. _cp_wg_sched: .. figure:: /images/mhk/03_cp_wg_sched.png @@ -38,7 +72,7 @@ Torque setpoints (:math:`\bar{\tau}`) determined by constant-power relationship :align: center :width: 90% -In Region 3, the relationship between torque and speed are nonmonotic. +In Region 3, the relationship between torque and speed is nonmonotonic. Thus, more careful reference control design is required for managing the transition region. There are examples in the literature for saturation/smoothing during the transition region. @@ -56,9 +90,18 @@ Over/Underspeed Dynamics .. :align: center .. :width: 90% -At each operating point, the sensitivity is computed using the gradients of the Cp surface. +At each operating point, the sensitivity is computed using the gradients of the +:math:`C_p` surface, in the same manner as the Region-2 controller. The first-order system decay rate is represented by a single pole on the real axis: more negative means more rapidly stable (positive means unstable). Underspeed set points are open-loop unstable at high flow speeds. +The sign follows from what each set point is asking the rotor to do. +Underspeed set points sit below :math:`TSR_{opt}`, where hydrodynamic torque still +grows with speed, and generator torque is the only thing holding the speed down; a +small speed perturbation grows rather than decays, so losing torque control lets +the rotor run away. +Overspeed set points are already past :math:`TSR_{opt}`, where hydrodynamic torque +falls off with speed, so the rotor is let out to a self-limiting operating point by +design and any speed perturbation decays on its own. .. _cp_Agen_sched_annotated: .. figure:: /images/mhk/07_cp_Agen_sched_annotated.png @@ -70,7 +113,15 @@ Underspeed set points are open-loop unstable at high flow speeds. .. :align: center .. :width: 90% -@DS: what is shown here? +The same result can be viewed in the flow-speed/generator-speed plane. +Contours of constant hydrodynamic rotor torque intersect the constant-power +generator torque schedule at the equilibrium operating points; the overspeed and +underspeed branches are the two intersections available at each flow speed. +The local slope of the torque surface across each intersection determines whether +that equilibrium is open-loop stable. + +.. TODO(DS): please confirm the description above matches what is plotted in + 09_cp_wg_Ta_contour_annotated.png (slide 11 carried no bullets). .. _cp_wg_Ta_contour_annotated: .. figure:: /images/mhk/09_cp_wg_Ta_contour_annotated.png @@ -81,7 +132,7 @@ Underspeed set points are open-loop unstable at high flow speeds. Fixed-Blade-Pitch (FBP) Control -------------------------------- -At each setpoint, the torque controller gains are determined using the process as the normal torque control in ROSCO. +At each setpoint, the torque controller gains are determined using the same process as the normal torque control in ROSCO. High magnitude gains are required to compensate for the open-loop instability of the underspeed system (red). .. _cp_kp_ki_sched: @@ -94,13 +145,52 @@ Alternate Region 3 Operating Schedules --------------------------------------- Using the ROSCO toolbox, we enable the user to determine their own operational power curve, besides a constant rated power. +Throughout this section, the *power requirement* is the generator power that the +selected Region-3 power curve demands at a given flow speed, i.e. the +:code:`VS_FBP_P` input evaluated at that speed. +It is an upper bound on what is asked of the machine, not on what is available: +the toolbox clips it to the power the inflow can actually supply at fine pitch, +so the MPPT curve is the ceiling and no request can exceed it. +Relaxing the constant-power constraint in Region 3 raises the peak of the power +requirement, and with it the sustained power the generator must be sized for. +The five power curves studied, with their peak power requirement at cut-out +relative to rated power (equivalently, the generator resizing factor), are: + +.. list-table:: + :header-rows: 1 + :widths: auto + + * - Region 3 power curve + - Peak power requirement (x rated) + * - Extended Region 2 (MPPT) + - 8x + * - Quadratic extrapolation from tangent + - 7x + * - Linear extrapolation from tangent + - 4x + * - Linear to 2x rated power at cut-out + - 2x + * - Constant power + - 1x .. _ext_P: .. figure:: /images/mhk/11_ext_P.png :align: center :width: 90% -The alternative power curves result in different speed and torque set points (dashed lines represent underspeed, solid overspeed). +Each non-MPPT option can follow either the overspeed or the underspeed +equilibrium trajectory. +The alternative power curves result in different speed and torque set points +(dashed lines represent underspeed, solid overspeed). +Speed setpoints always lie within the envelope bounded by the over- and +underspeed constant-power trajectories. +Constant power has the lowest power requirement of the five, so it departs +furthest from :math:`TSR_{opt}` and produces the widest speed excursion in either +direction; the MPPT curve has the highest requirement and does not depart at all, +tracking :math:`\Omega \propto v` straight through rated. +The constant-power schedule is therefore the bounding case for rotor overspeed +and cavitation margin on the overspeed branch, and for generator torque headroom +on the underspeed branch. .. _ext_wg_sched_annotated: .. figure:: /images/mhk/16_ext_wg_sched_annotated.png @@ -118,6 +208,26 @@ The alternative power curves result in different speed and torque set points (da :align: center :width: 90% +Contours in the :math:`C_p` surface intersect with the equilibrium points in the +hydrodynamic torque surface, which is what constrains the speed and torque +schedule generated from a generic power curve input. +Plotted in the generator torque/speed plane below, with contours of constant +generator power behind them, the equilibrium trajectories run nearly *along* the +power contours rather than across them. +A small reduction in the power requirement therefore produces a large change in +the equilibrium speed and torque. +Note that the schedule above is plotted against rotor speed :math:`\Omega`, while +the torque/speed plane below is plotted against generator speed +:math:`\Omega_g = N_g \Omega`; for the RM1 the gearbox ratio is +:math:`N_g = 53`, so the two speed axes differ by that factor. +Dropping from the MPPT curve to the quadratic curve, a reduction in peak power +requirement from 8x to 7x rated, moves the overspeed setpoint at cut-out from +roughly 2.5 to 3.5 rad/s rotor speed in the schedule above (about 130 to 185 +rad/s at the generator): a 12% reduction in power buys a 40% change in speed. +The two branches also look very different in this plane: the underspeed branches +are nearly vertical (large torque change for small speed change), the overspeed +branches nearly horizontal (large speed change for small torque change). + .. _ext_wg_tg_P_contour: .. figure:: /images/mhk/13_ext_wg_tg_P_contour.png :align: center @@ -128,8 +238,11 @@ The alternative power curves result in different speed and torque set points (da .. :align: center .. :width: 90% -The stability can be represented by the sensitivity :math:`\frac{d\tau}{d\Omega}`. -Values less than 0 are open-loop stable. +The stability of the FBP set points can be represented by the sensitivity +:math:`\frac{d\tau}{d\Omega}`, following the same over/underspeed argument as +above. +Values less than 0 are open-loop stable; positive values must be stabilized by the +torque controller. .. _ext_Agen_sched: .. figure:: /images/mhk/18_ext_Agen_sched.png @@ -137,30 +250,35 @@ Values less than 0 are open-loop stable. :width: 90% The power curve selection also impacts the rotor thrust (F). -Underspeed control and lower power generally results in lower thrust. +Underspeed control and lower power generally result in lower thrust. +Conversely, overspeed trajectories approach substantially higher rotor thrust; the +overspeed MPPT trajectory in particular reaches thrust levels that are likely +unacceptable for the structure, and should be checked against the design loads +before being selected. .. _ext_wg_thrust_contour: .. figure:: /images/mhk/19_ext_wg_thrust_contour.png :align: center :width: 90% -.. .. _ext_wg_thrust_sched: -.. .. figure:: /images/mhk/20_ext_wg_thrust_sched.png -.. :align: center -.. :width: 90% +.. _ext_wg_thrust_sched: +.. figure:: /images/mhk/20_ext_wg_thrust_sched.png + :align: center + :width: 90% Toolbox Implementation ----------------------- -The ROSCO toolbox works by determining the speed and torque set points required to operate at a TSR and Cp for the desired power. +The ROSCO toolbox works by determining the speed and torque set points required to operate at a TSR and Cp for the desired power across flow speeds. .. _fbp_flow_chart: .. figure:: /images/mhk/14_fbp_flow_chart.png :align: center :width: 90% -The following inputs to the ROSCO tuning yaml will generate DISCON inputs to ROSCO. +The following inputs to the ROSCO tuning yaml, under :code:`controller_params`, +will generate DISCON inputs to ROSCO. .. list-table:: :header-rows: 1 @@ -169,19 +287,74 @@ The following inputs to the ROSCO tuning yaml will generate DISCON inputs to ROS * - Parameter - Description * - VS_FBP - - FBP Control Mode (0 = variable pitch, 1 = constant power overspeed (nonlinear), 2 = WSE-lookup reference tracking, 3 = torque-lookup reference tracking) - * - FBP_speed_mode - - Over/underspeed mode (0 = underspeed, 1 = overspeed) - * - FBP_power_mode - - Normalized or exact power curve values (0 = relative to rated, 1 = exact) - * - FBP_U - - Flow speed setpoints for power curve lookup table - * - FBP_P - - Power curve lookup table + - FBP Control Mode: + + - 0 = variable pitch (disabled) + - 1 = constant power overspeed (nonlinear) + - 2 = WSE-lookup reference tracking + - 3 = torque-lookup reference tracking + * - VS_FBP_speed_mode + - Over/underspeed mode: + + - 0 = underspeed + - 1 = overspeed + * - VS_FBP_power_mode + - Normalized or exact power curve values: + + - 0 = relative to rated + - 1 = exact + * - VS_FBP_U + - Flow speed setpoints for power curve lookup table [m/s] + * - VS_FBP_P + - Power curve lookup table, interpreted per :code:`VS_FBP_power_mode` + +The following constraints apply when FBP control is enabled: + +* :code:`PC_ControlMode`, :code:`VS_ConstPower`, and :code:`PRC_Mode` must all be + 0 whenever :code:`VS_FBP > 0`: blade pitch is fixed to fine pitch, and neither + constant-power torque control nor power reference control may run concurrently. + ROSCO aborts on any of these at runtime, and the toolbox raises an exception + rather than write a configuration that cannot run. +* :code:`VS_FBP = 1` is the fixed control law + :math:`\tau = \min(P_{rated}/\Omega_g,\ K\Omega_g^2)`, which is inherently + constant-power and inherently overspeed. ROSCO ignores + :code:`VS_FBP_speed_mode`, :code:`VS_FBP_power_mode`, and :code:`VS_FBP_P` in + this mode, so the toolbox overrides them to the constant-power overspeed + schedule and warns if they were set otherwise. The generated operating + schedule is still used, both to seed the initial generator torque and to tune + the torque gains, so it must match the law ROSCO will actually run. +* For :code:`VS_FBP = 3` (torque-lookup reference tracking), the generator torque + schedule must be strictly increasing, since ROSCO inverts it to look up the + speed reference. The toolbox checks the computed schedule and raises an + exception otherwise. A nondecreasing power curve on the *underspeed* branch + (:code:`VS_FBP_speed_mode = 0`) is sufficient to guarantee this, because + generator speed falls with flow speed there, so :math:`\tau = P/\Omega_g` rises + on both counts. Overspeed generally is not sufficient: generator speed rises + with flow speed, so the torque falls unless the power curve rises faster than + the speed, which in practice only holds near the MPPT curve. + +The Region-2 torque control mode should be chosen to match the Region-3 FBP mode, +so that the two controllers hand off consistently through the transition region: +.. list-table:: + :header-rows: 1 + :widths: auto + + * - :code:`VS_FBP` + - Recommended :code:`VS_ControlMode` + * - 1 (constant power overspeed, nonlinear) + - 1 (:math:`k\Omega^2`) + * - 2 (WSE-lookup reference tracking) + - 2 (WSE TSR-tracking) + * - 3 (torque-lookup reference tracking) + - 4 (torque TSR-tracking) Note that the ROSCO input schema (:ref:`rt_tuning_yaml`) contains the latest input definitions. +A worked example of all six configurations is provided in +:code:`Examples/31_fixed_pitch_mhk.py`, which tunes against the RM1 marine turbine +tuning case :code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml`. + ROSCO Implementation ----------------------- @@ -195,7 +368,12 @@ The following DISCON parameters are generated using the ROSCO toolbox, or can be * - Parameter - Description * - VS_FBP - - FBP Control Mode (0 = variable pitch, 1 = constant power overspeed (nonlinear), 2 = WSE-lookup reference tracking, 3 = torque-lookup reference tracking) + - FBP Control Mode: + + - 0 = variable pitch + - 1 = constant power overspeed (nonlinear) + - 2 = WSE-lookup reference tracking + - 3 = torque-lookup reference tracking * - VS_FBP_n - Number of values in operating schedule lookup table * - VS_FBP_U @@ -203,7 +381,7 @@ The following DISCON parameters are generated using the ROSCO toolbox, or can be * - VS_FBP_Omega - Generator speed operating points in lookup table * - VS_FBP_Tau - - Generator torque operating points in lookup table + - Generator torque operating points in lookup table (must be monotonic for :code:`VS_FBP = 3`) Note that the ROSCO input schema (:ref:`rt_tuning_yaml`) contains the latest input definitions (under :code:`controller_params`, :code:`DISCON`). @@ -212,8 +390,8 @@ Simulation Verification ----------------------- A handful of example controller case studies have been developed using the RM1 -marine turbine to showcase the implemented features of FBP control. These -configurations are +marine turbine [Neary2014]_ to showcase the implemented features of FBP control. +These configurations are * Example 1: Overspeed, constant power * Example 2: Underspeed, torque-based reference tracking @@ -223,7 +401,7 @@ For each example, a power curve is defined and input to the ROSCO toolbox to generate operating schedules and auto-tune the gains used by the torque controller. The operating schedules for generator power, speed, and torque for each example -test case are shown in the following figure. +test case are shown in the following figure. .. _cases_P_wg_tg_sched: .. figure:: /images/mhk/21_cases_P_wg_tg_sched.png @@ -233,13 +411,26 @@ test case are shown in the following figure. Each example controller is then simulated with the RM1 marine turbine model using OpenFAST in both steady and turbulent inflow. The steady-state performance of each controller is compared to the operating schedules generated by the ROSCO -toolbox. The turbulent inflow uses the intensity shown in the following figure. +toolbox. The turbulent inflow uses the intensity shown in the following figure. .. _turb_intensity: .. figure:: /images/mhk/25_turb_intensity.png :align: center :width: 90% +Tidal flow design load cases differ from their wind counterparts in ways that +matter for controller verification. Turbulence characteristics are not those +typically encountered in atmospheric flow, the mean probability distribution is +more closely isolated by environmental condition, and turbulence intensity varies +heavily with site conditions, with the better tidal sites generally lower in +turbulence intensity than wind sites [Milne2013]_. The turbulence intensity and +wave statistics used here were varied to mimic buoy data collected in the Orkney +Islands, Scotland. Design requirements for tidal current converters are given in +[IEC62600-2]_. + +.. TODO(DS): please supply the citation or dataset identifier for the Orkney buoy + data so it can be referenced properly. + Example 1 ^^^^^^^^^ @@ -247,16 +438,19 @@ Example 1 The first example test case uses the naturally stable nonlinear feedback control law. This controller is confined to operating in the constant power, overspeed configuration. The explicit (non-reference-tracking) control law is analogous to -the `k\Omega^2` control law sometimes used in Region 2 for wind and marine -turbines. +the :math:`k\Omega^2` control law sometimes used in Region 2 for wind and marine +turbines [Johnson2006]_, and is best paired with a :math:`k\Omega^2` Region-2 +controller (:code:`VS_ControlMode = 1`). +It is easy to design and requires no tuning, but is rigid and inflexible, and its +overspeed operating points carry high tip speed and blade thrust. .. _case1_P_wg_tg_ss: .. figure:: /images/mhk/22_case1_P_wg_tg_ss.png :align: center :width: 90% -This controller has the best power tracking in Region 3, but it only allows constant power. -The power-focused feedback approach accommodates offsets in equilibrium speed and torque made by inaccuracies in the simplified tuning model. +This controller has the best power tracking in Region 3, but it only allows constant power. +The power-focused feedback approach accommodates offsets in equilibrium speed and torque made by inaccuracies in the simplified tuning model. .. _case1_P_wg_tg_turb: .. figure:: /images/mhk/26_case1_P_wg_tg_turb.png @@ -267,12 +461,17 @@ The power-focused feedback approach accommodates offsets in equilibrium speed an Example 2 ^^^^^^^^^ -The second example test case +The second example test case uses underspeed operation with a torque-based +reference and a semi-arbitrary power curve: -* Region-2 mode: TSR-tracking with torque-based reference -* Region-3 FBP mode: reference tracking with torque lookup +* Region-2 mode: TSR-tracking with torque-based reference (:code:`VS_ControlMode = 4`) +* Region-3 FBP mode: reference tracking with torque lookup (:code:`VS_FBP = 3`) * The power curve may be arbitrarily specified, but should be a nondecreasing function so that the torque schedule is monotonically increasing +This configuration is highly general, but requires an aggressive torque controller +to stabilize the open-loop-unstable underspeed setpoints, and therefore requires a +high maximum torque signal (see :code:`max_torque_factor` in the tuning yaml). + .. _case2_P_wg_tg_ss: .. figure:: /images/mhk/23_case2_P_wg_tg_ss.png :align: center @@ -294,12 +493,17 @@ The second example test case Example 3 ^^^^^^^^^ -The third example test case +The third example test case uses underspeed operation with a wind speed estimator +(WSE) reference [Ortega2013]_ and a fully arbitrary power curve: -* Region-2 mode: TSR-tracking with WSE-based reference -* Region-3 FBP mode: reference tracking with WSE lookup +* Region-2 mode: TSR-tracking with WSE-based reference (:code:`VS_ControlMode = 2`) +* Region-3 FBP mode: reference tracking with WSE lookup (:code:`VS_FBP = 2`) * The power curve can be completely arbitrarily specified +This is the most general configuration and the easiest to combine with a Region-2 +controller or with alternate references, but it is less robust to errors in the +tuning model than the torque lookup, and shares the torque lookup's need for an +aggressive controller and high torque headroom. .. _case3_P_wg_tg_ss: .. figure:: /images/mhk/24_case3_P_wg_tg_ss.png @@ -321,22 +525,65 @@ The third example test case Recommendations ----------------------- -FBP control is well suited to marine turbines without blade pitch actuators. +FBP control is well suited to marine turbines without blade pitch actuators. In certain applications, the ability to follow a generic power curve with a -limit actuation capability is more advantageous than using variable-blade-pitch -(VBP) control. +limited actuation capability is more advantageous than using variable-blade-pitch +(VBP) control. VBP control allows constant-power operation in Region 3 matched with constant speed and torque for a flat operating schedule. Pitch-actuated turbines also experience smaller blade loads in Region 3. The FBP approach satisfies applications in which the cost and complexity of the actuators themselves are -prohibitive. +prohibitive. -Generic user input allows flexibility for variety of applications. +Generic user input allows flexibility for variety of applications. FBP controller implementation in ROSCO with auto-tuning and automatic generation of operating schedule to follow power curve. Because the Region-2 and Region-3 controllers utilize the same actuator, the transition region is markedly different than what is required for a VBP -Region-3 controller. +Region-3 controller. + + +Future Work +----------------------- +* Enhanced multi-constraint configuration, including constraining Region-3 rotor + thrust to a constant level rather than only constraining power. +* Explicit power reference tracking. + + +References +----------------------- +.. [Stockhouse2025] Stockhouse, D. *Fixed-Blade-Pitch Control in ROSCO for MHK + Turbines.* National Renewable Energy Laboratory presentation, 2025. + (Source material for this page.) + +.. [Abbas2022] Abbas, N. J., Zalkind, D. S., Pao, L., and Wright, A. *A reference + open-source controller for fixed and floating offshore wind turbines.* + Wind Energy Science, 7(1), 53–73, 2022. https://doi.org/10.5194/wes-7-53-2022 + +.. [Johnson2006] Johnson, K. E., Pao, L. Y., Balas, M. J., and Fingersh, L. J. + *Control of variable-speed wind turbines: standard and adaptive techniques for + maximizing energy capture.* IEEE Control Systems Magazine, 26(3), 70–81, 2006. + https://doi.org/10.1109/MCS.2006.1636311 + +.. [Ortega2013] Ortega, R., Mancilla-David, F., and Jaramillo, F. *A globally + convergent wind speed estimator for wind turbine systems.* International + Journal of Adaptive Control and Signal Processing, 27(5), 413–425, 2013. + https://doi.org/10.1002/acs.2319 + +.. [Neary2014] Neary, V. S., Previsic, M., Jepsen, R. A., Lawson, M. J., Yu, Y.-H., + Copping, A. E., Fontaine, A. A., Hallett, K. C., and Murray, D. K. *Methodology + for Design and Economic Analysis of Marine Energy Conversion (MEC) + Technologies.* SAND2014-9040, Sandia National Laboratories, 2014. + (Source of the RM1 reference marine turbine model.) + +.. [Milne2013] Milne, I. A., Sharma, R. N., Flay, R. G. J., and Bickerton, S. + *Characteristics of the turbulence in the flow at a tidal stream power site.* + Philosophical Transactions of the Royal Society A, 371:20120196, 2013. + https://doi.org/10.1098/rsta.2012.0196 + +.. [IEC62600-2] IEC TS 62600-2:2019, *Marine energy – Wave, tidal and other water + current converters – Part 2: Marine energy systems – Design requirements.* + International Electrotechnical Commission, 2019. From 5ba01bccc8f8dbccbeb4853402be0cfc4f1afb9f Mon Sep 17 00:00:00 2001 From: dzalkind Date: Tue, 8 Sep 2026 15:17:07 -0600 Subject: [PATCH 07/21] Add cavitation check and docs --- docs/source/marine_hydro.rst | 152 ++++++++++++++++++++++++++++++++++- rosco/toolbox/controller.py | 67 +++++++++++++++ rosco/toolbox/turbine.py | 43 ++++++++++ 3 files changed, 260 insertions(+), 2 deletions(-) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index 6982a15e7..01f23fce9 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -57,8 +57,11 @@ Over/Underspeed Reference Setpoints The steady state generator-speed setpoints are determined by the :math:`C_p` contours intersecting with the fine-pitch line. -Overspeed achieves up to 3x rated speed, which has additional consequences for -blade loads (e.g., cavitation, high thrust). +For the RM1, overspeed reaches up to 3x rated speed. How far a given rotor can +actually use that range is limited by cavitation, discussed in +:ref:`speed_limits_cavitation`; on the RM1 the limit binds well before 3x. +Rotor speed also has consequences for +blade loads (high thrust). .. _cp_wg_sched: .. figure:: /images/mhk/03_cp_wg_sched.png @@ -173,6 +176,11 @@ relative to rated power (equivalently, the generator resizing factor), are: * - Constant power - 1x +These multipliers follow from the RM1 :math:`C_p` surface and its +:math:`v_{rated}` to :math:`v_{cut-out}` range, and are reported here to show the +relative spread between the curves. They are not general: the same five curve +definitions applied to another rotor will produce different factors. + .. _ext_P: .. figure:: /images/mhk/11_ext_P.png :align: center @@ -224,6 +232,8 @@ Dropping from the MPPT curve to the quadratic curve, a reduction in peak power requirement from 8x to 7x rated, moves the overspeed setpoint at cut-out from roughly 2.5 to 3.5 rad/s rotor speed in the schedule above (about 130 to 185 rad/s at the generator): a 12% reduction in power buys a 40% change in speed. +These values are specific to the RM1; the qualitative sensitivity is general, the +numbers are not. The two branches also look very different in this plane: the underspeed branches are nearly vertical (large torque change for small speed change), the overspeed branches nearly horizontal (large speed change for small torque change). @@ -267,6 +277,99 @@ before being selected. :width: 90% +.. _speed_limits_cavitation: + +Rotor Speed Limits and Cavitation +---------------------------------- + +Power and thrust are not the only constraints on an FBP operating schedule. For a +marine turbine, the rotor speed is also bounded by cavitation, and this bound is +what usually decides whether the overspeed branch is available at all. + +Cavitation occurs where the local pressure on the blade falls below the vapour +pressure of water. Writing the suction peak in terms of the section's minimum +pressure coefficient :math:`C_{p,min}`, + +.. math:: + + p_{min} = p_{atm} + \rho g h + C_{p,min}\left(\tfrac{1}{2}\rho W^2\right) < p_{vap} + +which rearranges into the usual cavitation number criterion, + +.. math:: + + \sigma = \frac{p_{atm} + \rho g h - p_{vap}}{\tfrac{1}{2}\rho W^2} > -C_{p,min} + +Two things follow, and both matter for control design. + +**Depth enters only as hydrostatic head.** The submergence :math:`h` appears +nowhere except in :math:`\rho g h`, which sets the pressure budget available to be +spent on suction. Seawater is dense enough that 10 m of depth adds about 101 kPa, +roughly one additional atmosphere, so submergence is a strong lever on the +available margin. Because the relevant depth is the *shallowest* point the blade +reaches, the binding condition is the blade tip at the top of its rotation. + +**The criterion collapses to a rotor speed limit.** Since the relative velocity at +the tip is dominated by :math:`\Omega R`, substituting +:math:`W^2 = (\Omega R)^2 + v^2` and solving for :math:`\Omega` gives a maximum +usable rotor speed: + +.. math:: + + \Omega_{cav} = \frac{1}{R}\sqrt{\frac{2\left(p_{atm} + \rho g h_{tip} - p_{vap}\right)}{\rho\,\sigma_v} - v^2} + +where :math:`\sigma_v = -C_{p,min}` for the outboard blade sections and +:math:`h_{tip}` is the tip depth at the top of its rotation. Note that neither the +power curve nor the choice of over/underspeed branch appears in this expression: +**cavitation constrains rotor speed directly**, and any operating schedule that +exceeds :math:`\Omega_{cav}` is affected regardless of how it was generated. + +The ROSCO toolbox evaluates this limit and warns if the generated speed schedule +violates it (see :ref:`the toolbox schedule checks `). + +Selecting Over- vs Underspeed +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Comparing :math:`\Omega_{cav}` against the speed schedule a given power curve +requires is the practical way to decide whether overspeed is usable for a +particular rotor: + +* If the overspeed schedule stays below :math:`\Omega_{cav}` across Region 3, + overspeed is available, and the thrust and drivetrain-speed considerations + discussed above govern the choice. +* If it does not, the overspeed branch is unavailable for that rotor at that + submergence, and the underspeed branch should be selected + (:code:`VS_FBP_speed_mode = 0`). + +Because :math:`\Omega_{cav}` scales with :math:`\sqrt{\rho g h_{tip}}`, machines +that are deeply submerged, or that use low design tip speeds, retain more of the +overspeed range than shallow, high-tip-speed rotors. + +For the RM1 at its nominal 24 m hub depth, :math:`\Omega_{cav}` is approximately +2.26 rad/s, or about 1.9x rated rotor speed, whereas the constant-power overspeed +schedule requires roughly 5.7x rated speed at cut-out. Overspeed is therefore not +available for this rotor, and the underspeed configurations (Examples 2 and 3) are +the applicable ones. This is a property of the RM1 and its submergence, not a +general result. + +.. note:: + For a floating MHK turbine (:code:`MHK = 2`), the hub depth is not constant: + platform heave and tidal range move the rotor vertically and change + :math:`h_{tip}` directly. A speed limit computed at nominal depth will be + optimistic at the shallow end of that excursion, so the limit should be + evaluated at the shallowest expected submergence rather than the mean. + +The expression above is a tip, attached-flow estimate. It is well suited to the +high-TSR overspeed regime, where the outboard sections operate near zero lift and +:math:`-C_{p,min}` is close to its minimum value. It is optimistic for deeply +stalled underspeed setpoints, whose larger :math:`-C_{p,min}` it does not capture, +though those setpoints have much lower relative velocity and correspondingly large +margin. A schedule that is marginal against this estimate should be verified with +an AeroDyn cavitation check (:code:`CavitCheck = True`), which evaluates the full +criterion at every blade node using the angle-of-attack-dependent +:math:`C_{p,min}` from the polars. + + Toolbox Implementation ----------------------- @@ -333,6 +436,29 @@ The following constraints apply when FBP control is enabled: with flow speed, so the torque falls unless the power curve rises faster than the speed, which in practice only holds near the MPPT curve. +.. _cavitation_warning: + +The toolbox also screens the generated schedule against two limits that it warns +about rather than enforces, since both depend on hardware choices outside the +controller: + +* If the generator torque schedule exceeds :code:`max_torque_factor` times rated + torque, the toolbox warns that the schedule may not be realizable within + saturation limits. +* For MHK turbines (:code:`MHK > 0`), if the rotor speed schedule exceeds the + estimated tip cavitation limit :math:`\Omega_{cav}` from + :ref:`speed_limits_cavitation`, the toolbox warns and reports the worst + offending operating point. The limit is computed from the water density, + atmospheric and vapour pressures, and hub submergence in the OpenFAST model, + together with :math:`-C_{p,min}` taken from the outboard AeroDyn polars. The + check is skipped if the polars carry no :math:`C_{p,min}` column + (:code:`InCol_Cpmin = 0`). + +Note that the torque check alone will not catch an overspeed schedule: overspeed +*reduces* generator torque while raising speed, so an overspeed configuration can +sit far above the cavitation limit while the torque schedule stays well within +bounds. The two checks are complementary. + The Region-2 torque control mode should be chosen to match the Region-3 FBP mode, so that the two controllers hand off consistently through the transition region: @@ -444,6 +570,14 @@ controller (:code:`VS_ControlMode = 1`). It is easy to design and requires no tuning, but is rigid and inflexible, and its overspeed operating points carry high tip speed and blade thrust. +.. note:: + Because this mode is inherently overspeed, it should be screened against the + cavitation speed limit of :ref:`speed_limits_cavitation` before selection. For + the RM1 the required speeds exceed that limit, so this example is included to + demonstrate and verify the control law rather than as a recommended + configuration for this rotor. It remains appropriate for rotors whose + overspeed schedule stays within :math:`\Omega_{cav}`. + .. _case1_P_wg_tg_ss: .. figure:: /images/mhk/22_case1_P_wg_tg_ss.png :align: center @@ -472,6 +606,20 @@ This configuration is highly general, but requires an aggressive torque controll to stabilize the open-loop-unstable underspeed setpoints, and therefore requires a high maximum torque signal (see :code:`max_torque_factor` in the tuning yaml). +.. warning:: + Sizing the torque limit is a safety consideration, not only a tracking one. + Every underspeed setpoint sits where hydrodynamic torque *increases* with rotor + speed, so if the commanded torque saturates below what the inflow demands, the + rotor accelerates rather than settling. Because the aerodynamic torque curve + only turns over past :math:`TSR_{opt}`, the nearest stable equilibrium is on + the *overspeed* side of the :math:`C_p` peak. Insufficient torque authority on + an underspeed schedule therefore does not stall the rotor to a stop: it + accelerates through the :math:`C_p` peak and settles at high speed, in the + high-thrust and cavitation-prone regime that the underspeed schedule was chosen + to avoid. The same applies to any fault that removes generator torque. Size + :code:`max_torque_factor` above the peak hydrodynamic torque over the operating + range, and ensure the shutdown path can arrest the rotor without it. + .. _case2_P_wg_tg_ss: .. figure:: /images/mhk/23_case2_P_wg_tg_ss.png :align: center diff --git a/rosco/toolbox/controller.py b/rosco/toolbox/controller.py index 3a0d2a590..e08dea6ab 100644 --- a/rosco/toolbox/controller.py +++ b/rosco/toolbox/controller.py @@ -119,6 +119,13 @@ def __init__(self, controller_params): raise Exception( f'rosco.toolbox:controller: {mode_name} must be 0 if VS_FBP > 0') + # Region 2 and Region 3 use the same actuator, so the torque controllers must hand off consistently + recommended_vs_mode = {1: 1, 2: 2, 3: 4}[self.VS_FBP] + if self.VS_ControlMode != recommended_vs_mode: + print(f'WARNING: VS_ControlMode = {self.VS_ControlMode} is not the recommended Region 2 ' + f'mode for VS_FBP = {self.VS_FBP}; use VS_ControlMode = {recommended_vs_mode} so the ' + 'Region 2 and Region 3 references agree through the transition.') + # VS_FBP = 1 runs the fixed control law tau = min(P_rated/omega, K*omega^2), so ROSCO ignores the power curve and speed mode. # Generate the schedule it will actually follow: the table still sets the initial generator torque and the tuned gain schedule. if self.VS_FBP == 1: @@ -244,6 +251,54 @@ def __init__(self, controller_params): 'U_pc, omega_pc, and zeta_pc are all list-like and are not of equal length') + def cavitation_speed_limit(self, turbine, v): + """ + Estimate the rotor speed at which tip cavitation begins, for each inflow speed. + + Cavitation occurs where the local pressure falls below the vapour pressure: + + p_min = p_atm + rho*g*h + Cp_min*(0.5*rho*W^2) < p_vap + + which rearranges to a cavitation number criterion, sigma > -Cp_min, with + + sigma = (p_atm + rho*g*h - p_vap) / (0.5*rho*W^2) + + Depth enters only through the hydrostatic head rho*g*h. The worst case is the + blade tip at the top of its rotation, where h is smallest and W is largest. + Setting sigma = sigma_v and W^2 = (Omega*R)^2 + v^2 gives Omega directly. + + This is a tip, attached-flow screening estimate. It is accurate in the high-TSR + (overspeed) regime where the tip sits near zero lift, and optimistic for + deeply-stalled underspeed setpoints, whose higher -Cp_min it does not capture. + It also assumes a fixed hub depth; for a floating MHK turbine, heave and tide + move the tip shallower and reduce the true limit. Verify a marginal schedule + with an AeroDyn CavitCheck run. + + Parameters: + ----------- + turbine : Turbine + Turbine object, must carry MHK cavitation properties. + v : array_like + Inflow speeds of the operating schedule, m/s. + + Returns: + -------- + omega_cav : ndarray or None + Rotor speed limit at each inflow speed, rad/s. None if the turbine + is not an MHK turbine or the polars carry no Cp_min data. + """ + if not getattr(turbine, 'MHK', 0) or getattr(turbine, 'cavit_sigma_v', None) is None: + return None + + # Available pressure budget at the tip's shallowest point + dp = turbine.Patm + turbine.rho * 9.80665 * turbine.tip_depth - turbine.Pvap + if dp <= 0: + return None + + w_max_sq = 2 * dp / (turbine.rho * turbine.cavit_sigma_v) + return np.sqrt(np.maximum(w_max_sq - np.asarray(v)**2, 0.0)) / turbine.rotor_radius + + def tune_controller(self, turbine): """ Given a turbine model, tune a controller based on the NREL generic controller tuning process @@ -386,6 +441,18 @@ def tune_controller(self, turbine): print('WARNING: Torque operating schedule is above maximum generator torque and may not be realizable within saturation limits.') # DBS: Future - add input constraints to satisfy maximum torque, speed, and thrust (peak shaving) in addition to power + # Check the speed schedule against tip cavitation for MHK turbines. Overspeed + # schedules reduce torque while raising speed, so the torque check above cannot + # catch them. + omega_cav = self.cavitation_speed_limit(turbine, v) + if omega_cav is not None and np.any(omega_op > omega_cav): + i = np.argmax(omega_op - omega_cav) + print('WARNING: Rotor speed operating schedule exceeds the estimated tip ' + 'cavitation limit. Worst point is {:.2f} rad/s at {:.1f} m/s inflow, ' + 'against a limit of {:.2f} rad/s. Verify with an AeroDyn CavitCheck ' + 'run; see the MHK documentation on rotor speed limits.'.format( + omega_op[i], v[i], omega_cav[i])) + # Check if options allow a nonmonotonic torque schedule if self.VS_FBP == 3: # The simulation will crash if we have a nonmonotonic schedule, so fail to generate the config and alert the user diff --git a/rosco/toolbox/turbine.py b/rosco/toolbox/turbine.py index d37790740..042ea89e6 100644 --- a/rosco/toolbox/turbine.py +++ b/rosco/toolbox/turbine.py @@ -224,6 +224,20 @@ def load_from_fast( self.rated_torque = self.rated_power/(self.GenEff/100*self.rated_rotor_speed*self.Ng) self.rotor_radius = self.TipRad + # MHK cavitation properties (used to screen the operating schedule) + self.MHK = fast.fst_vt['Fst']['MHK'] + if self.MHK: + self.Patm = fast.fst_vt['Fst']['Patm'] + self.Pvap = fast.fst_vt['Fst']['Pvap'] + self.MSL2SWL = fast.fst_vt['Fst']['MSL2SWL'] + # Shallowest depth reached by the blade tip, at the top of its rotation. + # hubHt is negative below MSL for an MHK turbine. + self.tip_depth = abs(self.hubHt + self.MSL2SWL) - self.TipRad + self.cavit_sigma_v = self.load_cavitation_number(fast) + else: + self.Patm = self.Pvap = self.MSL2SWL = None + self.tip_depth = self.cavit_sigma_v = None + # Load blade information self.load_blade_info() @@ -256,6 +270,35 @@ def load_from_fast( # Compute operational Cp (may not be optimal if TSR_operational set by user) self.Cp_operational = self.Cp.interp_surface(self.Cp.pitch_opt, self.TSR_operational) + def load_cavitation_number(self, fast): + ''' + Smallest cavitation number the blade sections can tolerate, sigma_v = -Cp_min. + + Taken as the minimum of -Cp_min over the outboard airfoil polars, i.e. the most + permissive (thinnest suction peak) condition the blade ever presents. Used to + screen the operating schedule for tip cavitation in + :code:`Controller.tune_controller`. + + Returns None if the AeroDyn polars carry no Cp_min column. + ''' + if not fast.fst_vt['AeroDyn'].get('InCol_Cpmin', 0): + return None + + # Airfoils used outboard of 70% span, where relative velocity is highest + blade = fast.fst_vt['AeroDynBlade'] + span = np.array(blade['BlSpn']) + afid = np.array(blade['BlAFID'], dtype=int) + outboard = set(afid[span >= 0.7 * span[-1]]) + + sigma_v = [] + for af in outboard: + for tab in fast.fst_vt['AeroDyn']['af_data'][af - 1]: + cpmin = np.array([c for c in tab['Cpmin'] if c is not None], dtype=float) + if cpmin.size: + sigma_v.append(-cpmin.max()) # max Cp_min -> min -Cp_min + + return min(sigma_v) if sigma_v else None + # Load rotor performance data from CCBlade def load_from_ccblade(self): ''' From d30ca108d02e3233cc1bdd48e71c36b5c87a6b42 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Tue, 8 Sep 2026 15:25:14 -0600 Subject: [PATCH 08/21] Align VS_ControlMode with docs --- Examples/31_fixed_pitch_mhk.py | 6 ++++-- Examples/Tune_Cases/RM1_MHK_FBP.yaml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index c88139d65..e59be44cd 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -82,13 +82,14 @@ def main(): ### Control configurations: overrides applied to the tuning yaml controller_params control_configs = { 'Constant Power Underspeed': { # should be the default - 'VS_FBP': 3, # Power reference + 'VS_FBP': 3, # Torque-lookup reference 'VS_FBP_speed_mode': 0, 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 1.0], }, 'Constant Power Overspeed': { 'VS_FBP': 2, # WSE reference + 'VS_ControlMode': 2, # Region 2 mode paired with VS_FBP = 2 'VS_FBP_speed_mode': 1, 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 1.0], @@ -104,15 +105,16 @@ def main(): }, 'Generic User-Defined': { 'VS_FBP': 2, # WSE reference + 'VS_ControlMode': 2, # Region 2 mode paired with VS_FBP = 2 'VS_FBP_U': [2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0], 'VS_FBP_P': [1.0, 1.3, 1.6, 1.8, 1.9, 2.0, 1.9, 1.8, 1.7, 1.6, 1.5], }, 'Constant Power Overspeed, Lookup Table': { 'VS_FBP': 1, # Constant power overspeed + 'VS_ControlMode': 1, # Region 2 mode paired with VS_FBP = 1 'VS_FBP_speed_mode': 1, 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 1.0], - 'VS_ControlMode': 1, }, } diff --git a/Examples/Tune_Cases/RM1_MHK_FBP.yaml b/Examples/Tune_Cases/RM1_MHK_FBP.yaml index de6bca870..a24569e11 100644 --- a/Examples/Tune_Cases/RM1_MHK_FBP.yaml +++ b/Examples/Tune_Cases/RM1_MHK_FBP.yaml @@ -32,7 +32,7 @@ controller_params: F_LPFType: 1 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 1 # Notch filter on generator speed and/or tower fore-aft motion (for floating) {0: disable, 1: generator speed, 2: tower-top fore-aft motion, 3: generator speed and tower-top fore-aft motion} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} + VS_ControlMode: 4 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control}, paired with VS_FBP = 3 below VS_ConstPower: 0 # Generator torque control mode in above rated conditions with variable blade pitch {0: constant torque, 1: constant power} PC_ControlMode: 0 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} From bf4126f8045926d82f0450694a93a2be79a05ca3 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 10 Sep 2026 11:37:48 -0600 Subject: [PATCH 09/21] Try mamba install with fewer solves --- .readthedocs.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 09e3b92fc..c9a39adfe 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,8 +13,7 @@ build: jobs: # Read the docs needs a couple packages not in the environment file pre_install: - - conda install -y cmake compilers sphinx sphinxcontrib-bibtex sphinx-jsonschema - - conda install sphinx_rtd_theme>=1.3 + - mamba install -y compilers sphinx sphinxcontrib-bibtex sphinx-jsonschema - pip install json-schema-for-humans pre_build: - pwd From 7dad3ddbfebf202a9e3795152788fabdf93052da Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 10 Sep 2026 11:39:01 -0600 Subject: [PATCH 10/21] Re-organize example and docs to follow three main examples --- Examples/31_fixed_pitch_mhk.py | 84 +++++++++++++++++++++------------- docs/source/marine_hydro.rst | 33 ++++++++++++- 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index e59be44cd..f1a9c5236 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -5,14 +5,19 @@ This example demonstrates the fixed-pitch control of a marine hydrodkinetic (MHK) turbine. There are several ways to control the power output of a turbine in above-rated conditions. -In this example we demonstrate the following control configurations: +The first three configurations demonstrated here are the case studies verified against +simulation in :ref:`marine_hydro`: -#. Constant power underspeed (should be the default) -#. Constant power overspeed -#. Linear increasing power -#. Linear increasing power, leveling out -#. Generic numeric function -#. Constant power overspeed, nonlinear lookup table control +#. Example 1: Constant power overspeed, nonlinear control law +#. Example 2: Linear increasing power, underspeed with torque-lookup reference +#. Example 3: Generic user-defined power curve, underspeed with WSE-lookup reference + +The remaining three are experimental. They exercise the rest of the fixed-pitch input +space and are not part of that verification campaign: + +* Constant power underspeed +* Constant power overspeed, via WSE-lookup reference tracking +* Linear increasing power, leveling out below cut-out More details about the controller methods can be found in :ref:`marine_hydro`. @@ -20,8 +25,8 @@ .. image:: ../images/examples/31_fixed_pitch_mhk_sched.png -In the first case, the reference generator speed is decreased (underspeed) to maintain a constant rated power above rated. -To slow down the generator, a higher torque must be used: +In the first case, the reference generator speed is increased (overspeed) to maintain a constant +rated power above rated, following the fixed control law tau = min(P_rated/omega, K*omega^2): .. image:: ../images/examples/31_fixed_pitch_mhk_sim.png @@ -30,8 +35,8 @@ ''' # Copying images, from docs/: -# cp ../Examples/examples_out/30_fixed_pitch_mhk_sched.png images/ -# cp ../Examples/examples_out/30_fixed_pitch_mhk_sim.png images/ +# cp ../Examples/examples_out/31_fixed_pitch_mhk_sched.png images/examples/ +# cp ../Examples/examples_out/31_fixed_pitch_mhk_sim.png images/examples/ import os from rosco.toolbox.ofTools.case_gen.run_FAST import run_FAST_ROSCO @@ -80,42 +85,53 @@ def main(): ### Control configurations: overrides applied to the tuning yaml controller_params + # The first three are the verified case studies of :ref:`marine_hydro`; the rest + # demonstrate the rest of the FBP input space and are not part of that campaign. + # Each config states VS_FBP and its paired VS_ControlMode explicitly, rather than + # inheriting either from the tuning yaml. control_configs = { - 'Constant Power Underspeed': { # should be the default - 'VS_FBP': 3, # Torque-lookup reference - 'VS_FBP_speed_mode': 0, - 'VS_FBP_U': [2.0, 4.0], - 'VS_FBP_P': [1.0, 1.0], - }, - 'Constant Power Overspeed': { - 'VS_FBP': 2, # WSE reference - 'VS_ControlMode': 2, # Region 2 mode paired with VS_FBP = 2 + 'Example 1: Constant Power Overspeed': { # marine_hydro Example 1 + 'VS_FBP': 1, # Constant power overspeed, nonlinear control law + 'VS_ControlMode': 1, # Region 2 mode paired with VS_FBP = 1 'VS_FBP_speed_mode': 1, 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 1.0], }, - 'Linear Increasing Power': { + 'Example 2: Linear Increasing Power': { # marine_hydro Example 2 + 'VS_FBP': 3, # Torque-lookup reference + 'VS_ControlMode': 4, # Region 2 mode paired with VS_FBP = 3 'VS_FBP_speed_mode': 0, 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 2.0], }, - 'Increasing Leveled Power': { - 'VS_FBP_U': [2.0, 3.0], - 'VS_FBP_P': [1.0, 2.0], - }, - 'Generic User-Defined': { - 'VS_FBP': 2, # WSE reference + 'Example 3: Generic User-Defined': { # marine_hydro Example 3 + 'VS_FBP': 2, # WSE-lookup reference 'VS_ControlMode': 2, # Region 2 mode paired with VS_FBP = 2 + 'VS_FBP_speed_mode': 0, 'VS_FBP_U': [2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0], 'VS_FBP_P': [1.0, 1.3, 1.6, 1.8, 1.9, 2.0, 1.9, 1.8, 1.7, 1.6, 1.5], }, - 'Constant Power Overspeed, Lookup Table': { - 'VS_FBP': 1, # Constant power overspeed - 'VS_ControlMode': 1, # Region 2 mode paired with VS_FBP = 1 - 'VS_FBP_speed_mode': 1, + 'Experimental: Constant Power Underspeed': { # Example 2 held at rated power + 'VS_FBP': 3, # Torque-lookup reference + 'VS_ControlMode': 4, # Region 2 mode paired with VS_FBP = 3 + 'VS_FBP_speed_mode': 0, + 'VS_FBP_U': [2.0, 4.0], + 'VS_FBP_P': [1.0, 1.0], + }, + 'Experimental: Constant Power Overspeed (WSE)': { # Example 1 schedule via reference tracking + 'VS_FBP': 2, # WSE-lookup reference + 'VS_ControlMode': 2, # Region 2 mode paired with VS_FBP = 2 + 'VS_FBP_speed_mode': 1, # Overspeed: check against the cavitation limit, exceeded for the RM1 'VS_FBP_U': [2.0, 4.0], 'VS_FBP_P': [1.0, 1.0], }, + 'Experimental: Increasing Leveled Power': { # Example 2 leveling out below cut-out + 'VS_FBP': 3, # Torque-lookup reference + 'VS_ControlMode': 4, # Region 2 mode paired with VS_FBP = 3 + 'VS_FBP_speed_mode': 0, + 'VS_FBP_U': [2.0, 3.0], + 'VS_FBP_P': [1.0, 2.0], + }, } ### Tune controller cases @@ -125,8 +141,12 @@ def main(): controller.tune_controller(turbine) controllers.append(controller) + # The WSE overspeed config tracks the same schedule as Example 1, so dash it to keep both visible + line_styles = {'Experimental: Constant Power Overspeed (WSE)': '--'} + fig, axs = plt.subplots(3,1) - for (label, cont, line_style) in zip(control_configs, controllers, ['-','--','-','-','-',':']): + for label, cont in zip(control_configs, controllers): + line_style = line_styles.get(label, '-') axs[0].plot(cont.v, cont.power_op, label=label, linestyle=line_style) axs[1].plot(cont.v, cont.omega_gen_op, label=label, linestyle=line_style) axs[2].plot(cont.v, cont.tau_op, label=label, linestyle=line_style) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index 01f23fce9..604f6e44c 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -477,9 +477,11 @@ so that the two controllers hand off consistently through the transition region: Note that the ROSCO input schema (:ref:`rt_tuning_yaml`) contains the latest input definitions. -A worked example of all six configurations is provided in +The three case studies verified below are provided as worked examples in :code:`Examples/31_fixed_pitch_mhk.py`, which tunes against the RM1 marine turbine -tuning case :code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml`. +tuning case :code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml`. That script also ships three +experimental configurations covering the rest of the input space; see +:ref:`fbp_experimental_configs`. ROSCO Implementation @@ -670,6 +672,33 @@ aggressive controller and high torque headroom. :width: 90% +.. _fbp_experimental_configs: + +Additional Configurations +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:code:`Examples/31_fixed_pitch_mhk.py` ships three further configurations beyond the +three examples above: + +* Constant power underspeed: the Example 2 configuration + (:code:`VS_FBP = 3`, :code:`VS_ControlMode = 4`) with the power curve held flat at + rated rather than increasing. +* Constant power overspeed via reference tracking: the Example 1 operating schedule + reached through the WSE lookup (:code:`VS_FBP = 2`, :code:`VS_ControlMode = 2`) + instead of the fixed control law. +* Linear increasing power, leveling out: the Example 2 power curve reaching 2x rated + below cut-out and flat thereafter. + +These are experimental. They are included to demonstrate the range of power curves and +mode pairings the toolbox accepts, and have not been through the steady and turbulent +verification campaign that Examples 1 through 3 have. Treat their operating schedules as +illustrative rather than validated. + +The cavitation caveat attached to Example 1 applies to the overspeed reference-tracking +configuration as well: for the RM1 rotor, both exceed the tip cavitation limit of +:ref:`speed_limits_cavitation`, and the toolbox warns accordingly when they are tuned. + + Recommendations ----------------------- From ecccafdc5935e8d2865cb77be91b205ac8bdc6bb Mon Sep 17 00:00:00 2001 From: dzalkind Date: Tue, 15 Sep 2026 10:43:35 -0600 Subject: [PATCH 11/21] Add soft-start to FBP modes --- Examples/31_fixed_pitch_mhk.py | 25 ++++++++++++++++++++-- docs/source/marine_hydro.rst | 11 +++++++--- rosco/controller/src/Controllers.f90 | 4 ++-- rosco/controller/src/ReadSetParameters.f90 | 4 ++-- rosco/toolbox/controller.py | 7 +++++- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index f1a9c5236..457f7867d 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -30,6 +30,10 @@ .. image:: ../images/examples/31_fixed_pitch_mhk_sim.png +All configurations are started softly: an open loop input ramps the torque rating +(``R_Torque``) from ``SOFT_START_R0`` to 1.0 with a sigma function over ``SOFT_START_DUR`` +seconds, de-rating the generator torque limit while the turbine spins up. + ''' @@ -58,6 +62,10 @@ example_out_dir = os.path.join(this_dir, 'examples_out') os.makedirs(example_out_dir,exist_ok=True) +# Soft start up: R_Torque is ramped from SOFT_START_R0 to 1.0 over SOFT_START_DUR seconds +SOFT_START_R0 = 0.5 +SOFT_START_DUR = 100. + def main(): FULL_TEST = True # Run a full test locally (True) or a shorter one for CI @@ -168,17 +176,30 @@ def main(): plt.savefig(fig_fname,bbox_inches='tight',) # Simulate all control configurations, in parallel - run_dir = os.path.join(example_out_dir, '31_MHK') + run_dir = os.path.join(example_out_dir, '31_MHK_2_reorder') os.makedirs(run_dir,exist_ok=True) # simulation set up if FULL_TEST: - TMax = 60 + TMax = SOFT_START_DUR + 100 else: TMax = 5 + # Soft start up: phase in the torque rating with an open loop input + olc = ROSCO_controller.OpenLoopControl(t_max=SOFT_START_DUR) + olc.interp_series('R_torque', [0, SOFT_START_DUR], [SOFT_START_R0, 1.0], 'sigma') + ol_dict = olc.write_input(os.path.join(run_dir, '31_OL_Input.dat')) + r = run_FAST_ROSCO() r.tuning_yaml = parameter_filename + r.controller_params = { + 'PRC_Mode': 2, # power reference control + 'OL_Mode': 1, + 'open_loop': ol_dict, + 'DISCON': { + 'PRC_Comm': 1, # open loop inputs + }, + } r.wind_case_fcn = cl.power_curve r.wind_case_opts = { 'U': [3.0], diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index 604f6e44c..ba22843a7 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -413,11 +413,16 @@ will generate DISCON inputs to ROSCO. The following constraints apply when FBP control is enabled: -* :code:`PC_ControlMode`, :code:`VS_ConstPower`, and :code:`PRC_Mode` must all be - 0 whenever :code:`VS_FBP > 0`: blade pitch is fixed to fine pitch, and neither - constant-power torque control nor power reference control may run concurrently. +* :code:`PC_ControlMode` and :code:`VS_ConstPower` must both be 0 whenever + :code:`VS_FBP > 0`: blade pitch is fixed to fine pitch, so constant-power + torque control may not run concurrently. :code:`PRC_Mode` must not be 1, since + its speed setpoint lookup would override the FBP speed reference. ROSCO aborts on any of these at runtime, and the toolbox raises an exception rather than write a configuration that cannot run. +* :code:`PRC_Mode = 2` is allowed with FBP control: :code:`R_Torque` de-rates the + generator torque limit (:code:`VS_MaxTq`) and the Region 3 constant-power + torque, which is how :code:`Examples/31_fixed_pitch_mhk.py` ramps the rating in + for a soft start up. :code:`R_Pitch` has no effect, since the blade pitch is fixed. * :code:`VS_FBP = 1` is the fixed control law :math:`\tau = \min(P_{rated}/\Omega_g,\ K\Omega_g^2)`, which is inherently constant-power and inherently overspeed. ROSCO ignores diff --git a/rosco/controller/src/Controllers.f90 b/rosco/controller/src/Controllers.f90 index b1e40c96f..08651d11c 100644 --- a/rosco/controller/src/Controllers.f90 +++ b/rosco/controller/src/Controllers.f90 @@ -245,8 +245,8 @@ SUBROUTINE VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst, ErrVar) LocalVar%VS_MaxTq = CntrPar%VS_RtTq * LocalVar%PRC_R_Torque END IF ELSE - ! Constant pitch, max torque is control parameter - LocalVar%VS_MaxTq = CntrPar%VS_MaxTq + ! Constant pitch, max torque is control parameter, de-rated by the torque rating + LocalVar%VS_MaxTq = CntrPar%VS_MaxTq * LocalVar%PRC_R_Torque ENDIF ! Optimal Tip-Speed-Ratio tracking controller (reference generated in subroutine ComputeVariablesSetpoints) diff --git a/rosco/controller/src/ReadSetParameters.f90 b/rosco/controller/src/ReadSetParameters.f90 index 626adaac4..3f1939cd5 100644 --- a/rosco/controller/src/ReadSetParameters.f90 +++ b/rosco/controller/src/ReadSetParameters.f90 @@ -1032,9 +1032,9 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ErrVar%ErrMsg = 'VS_FBP and PC_ControlMode cannot both be greater than 0.' ENDIF - IF ((CntrPar%VS_FBP > 0) .AND. (CntrPar%PRC_Mode > 0)) THEN + IF ((CntrPar%VS_FBP > 0) .AND. (CntrPar%PRC_Mode == 1)) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'Fixed blade pitch control (VS_FBP) and power reference control (PRC_Mode) cannot both be enabled.' + ErrVar%ErrMsg = 'Fixed blade pitch control (VS_FBP) and the PRC_Mode = 1 speed setpoint lookup cannot both be enabled.' ENDIF IF ((CntrPar%VS_FBP > 0) .AND. (CntrPar%VS_ConstPower > 0)) THEN diff --git a/rosco/toolbox/controller.py b/rosco/toolbox/controller.py index e08dea6ab..8a9aeab19 100644 --- a/rosco/toolbox/controller.py +++ b/rosco/toolbox/controller.py @@ -114,11 +114,16 @@ def __init__(self, controller_params): if self.VS_FBP > 0: # Mode conflicts asserted in ReadSetParameters.f90; ROSCO aborts on these at runtime, so fail here instead of writing a config that cannot run - for mode_name in ['PC_ControlMode', 'VS_ConstPower', 'PRC_Mode']: + for mode_name in ['PC_ControlMode', 'VS_ConstPower']: if getattr(self, mode_name) != 0: raise Exception( f'rosco.toolbox:controller: {mode_name} must be 0 if VS_FBP > 0') + # PRC_Mode = 1 overrides the FBP speed reference with its own lookup; PRC_Mode = 2 only de-rates and is allowed + if self.PRC_Mode == 1: + raise Exception( + 'rosco.toolbox:controller: PRC_Mode must not be 1 if VS_FBP > 0') + # Region 2 and Region 3 use the same actuator, so the torque controllers must hand off consistently recommended_vs_mode = {1: 1, 2: 2, 3: 4}[self.VS_FBP] if self.VS_ControlMode != recommended_vs_mode: From 74f30bb7373bb46ecfd3e4a6bdc671de696db029 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 16 Sep 2026 14:55:02 -0600 Subject: [PATCH 12/21] Update comment, defaults for MHK settings, VS_ControlMode --- Examples/26_marine_hydro.py | 2 +- Examples/Test_Cases/BAR_10/BAR_10_DISCON.IN | 6 ++-- .../IEA-15-240-RWT-Monopile_DISCON.IN | 4 +-- .../IEA-15-240-RWT-Monopile_ROSCO.yaml | 2 +- .../IEA-15-240-RWT-UMaineSemi_DISCON.IN | 4 +-- .../IEA-15-240-RWT-UMaineSemi_ROSCO.yaml | 2 +- Examples/Test_Cases/MHK_RM1/MHK_RM1_DISCON.IN | 30 +++++++++---------- Examples/Test_Cases/NREL-5MW/DISCON.IN | 6 ++-- .../NREL_2p8_127/NREL-2p8-127_DISCON.IN | 6 ++-- Examples/Tune_Cases/BAR.yaml | 2 +- Examples/Tune_Cases/IEA15MW.yaml | 2 +- Examples/Tune_Cases/IEA15MW_ExtInterface.yaml | 2 +- Examples/Tune_Cases/IEA15MW_FOCAL.yaml | 2 +- Examples/Tune_Cases/IEA15MW_MultiOmega.yaml | 2 +- Examples/Tune_Cases/IEA15MW_OL.yaml | 2 +- Examples/Tune_Cases/IEA15MW_PRC.yaml | 2 +- Examples/Tune_Cases/IEA15MW_ballast.yaml | 2 +- Examples/Tune_Cases/IEA15MW_cable.yaml | 2 +- Examples/Tune_Cases/IEA15MW_robust.yaml | 2 +- Examples/Tune_Cases/NREL5MW.yaml | 2 +- Examples/Tune_Cases/NREL5MW_PassThrough.yaml | 2 +- Examples/Tune_Cases/RM1_MHK.yaml | 8 ++--- rosco/toolbox/inputs/toolbox_schema.yaml | 4 +-- rosco/toolbox/utilities.py | 6 ++-- 24 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Examples/26_marine_hydro.py b/Examples/26_marine_hydro.py index 874e2696b..61ef7427b 100644 --- a/Examples/26_marine_hydro.py +++ b/Examples/26_marine_hydro.py @@ -1,7 +1,7 @@ """ 26_marine_hydro --------------- -Run MHK turbine in OpenFAST with ROSCO torque controller +Run MHK turbine in OpenFAST with ROSCO pitch and torque controller """ import os diff --git a/Examples/Test_Cases/BAR_10/BAR_10_DISCON.IN b/Examples/Test_Cases/BAR_10/BAR_10_DISCON.IN index 49be8061b..0b19297f6 100644 --- a/Examples/Test_Cases/BAR_10/BAR_10_DISCON.IN +++ b/Examples/Test_Cases/BAR_10/BAR_10_DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the BAR_10 wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 1 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,7 +10,7 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 2 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -2 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +2 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 0 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) @@ -114,7 +114,7 @@ 2 ! PRC_n - Number of elements in PRC_WindSpeeds and PRC_GenSpeeds array 0.07854 ! PRC_LPF_Freq - Frequency of the low pass filter on the wind speed estimate used to set PRC_GenSpeeds [rad/s] 3.0000 25.0000 ! PRC_WindSpeeds - Array of wind speeds used in rotor speed vs. wind speed lookup table [m/s] -7.9168e-01 7.9168e-01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] +7.9853e+01 7.9853e+01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] !------- WIND SPEED ESTIMATOR --------------------------------------------- 102.996 ! WE_BladeRadius - Blade length (distance from hub center to blade tip), [m] diff --git a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_DISCON.IN b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_DISCON.IN index 4cc22b597..40657985d 100644 --- a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_DISCON.IN +++ b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the IEA-15-240-RWT-Monopile wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 1 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,7 +10,7 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 2 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -2 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +2 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 0 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) diff --git a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_ROSCO.yaml b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_ROSCO.yaml index cc64b97dd..7357aa8ef 100644 --- a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_ROSCO.yaml +++ b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-Monopile/IEA-15-240-RWT-Monopile_ROSCO.yaml @@ -28,7 +28,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_DISCON.IN b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_DISCON.IN index 05b0219d8..6ee57f407 100644 --- a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_DISCON.IN +++ b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the IEA-15-240-RWT-UMaineSemi wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 2 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,7 +10,7 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 2 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -2 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +2 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 0 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) diff --git a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ROSCO.yaml b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ROSCO.yaml index 274e1b897..d1ae2a79e 100644 --- a/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ROSCO.yaml +++ b/Examples/Test_Cases/IEA-15-240-RWT/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ROSCO.yaml @@ -28,7 +28,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Test_Cases/MHK_RM1/MHK_RM1_DISCON.IN b/Examples/Test_Cases/MHK_RM1/MHK_RM1_DISCON.IN index 8b47be58a..ea7135476 100644 --- a/Examples/Test_Cases/MHK_RM1/MHK_RM1_DISCON.IN +++ b/Examples/Test_Cases/MHK_RM1/MHK_RM1_DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the MHK_RM1_Floating wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 2 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,14 +10,14 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 1 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -3 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +3 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 1 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) 0 ! Y_ControlMode - Yaw control mode (0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC) 1 ! SS_Mode - Setpoint Smoother mode (0: no setpoint smoothing, 1: introduce setpoint smoothing) 0 ! PRC_Mode - Power reference tracking mode (0: power control disabled, 1: lookup table from wind speed to generator speed setpoints, 2: change speed, torque, pitch to control power) -0 ! WE_Mode - Wind speed estimator mode (0: One-second low pass filtered hub height wind speed, 1: Immersion and Invariance Estimator, 2: Extended Kalman Filter) +2 ! WE_Mode - Wind speed estimator mode (0: One-second low pass filtered hub height wind speed, 1: Immersion and Invariance Estimator, 2: Extended Kalman Filter) 1 ! PS_Mode - Pitch saturation mode (0: no pitch saturation, 1: implement pitch saturation) 0 ! SU_Mode - Startup mode (0: no startup procedure, 1: startup enabled) 0 ! SD_Mode - Shutdown mode (0: no shutdown procedure, 1: shutdown enabled) @@ -55,9 +55,9 @@ !------- BLADE PITCH CONTROL ---------------------------------------------- 30 ! PC_GS_n - Amount of gain-scheduling table entries -0.030 0.049 0.064 0.076 0.087 0.097 0.107 0.115 0.124 0.131 0.139 0.146 0.153 0.160 0.166 0.173 0.179 0.185 0.191 0.197 0.202 0.208 0.214 0.219 0.224 0.230 0.235 0.240 0.245 0.250 ! PC_GS_angles - Gain-schedule table: pitch angles [rad]. --2.407e-02 -2.136e-02 -1.912e-02 -1.724e-02 -1.563e-02 -1.424e-02 -1.304e-02 -1.197e-02 -1.103e-02 -1.020e-02 -9.442e-03 -8.763e-03 -8.146e-03 -7.584e-03 -7.070e-03 -6.597e-03 -6.162e-03 -5.760e-03 -5.386e-03 -5.039e-03 -4.715e-03 -4.413e-03 -4.130e-03 -3.864e-03 -3.614e-03 -3.378e-03 -3.156e-03 -2.946e-03 -2.747e-03 -2.558e-03 ! PC_GS_KP - Gain-schedule table: pitch controller kp gains [s]. --3.864e-03 -3.498e-03 -3.195e-03 -2.940e-03 -2.723e-03 -2.536e-03 -2.372e-03 -2.229e-03 -2.102e-03 -1.989e-03 -1.887e-03 -1.795e-03 -1.712e-03 -1.636e-03 -1.566e-03 -1.503e-03 -1.444e-03 -1.389e-03 -1.339e-03 -1.292e-03 -1.248e-03 -1.207e-03 -1.169e-03 -1.133e-03 -1.099e-03 -1.068e-03 -1.038e-03 -1.009e-03 -9.822e-04 -9.568e-04 ! PC_GS_KI - Gain-schedule table: pitch controller ki gains [-]. +0.049 0.076 0.097 0.115 0.131 0.146 0.160 0.173 0.185 0.197 0.208 0.219 0.230 0.240 0.250 0.260 0.269 0.279 0.288 0.297 0.306 0.315 0.324 0.332 0.341 0.349 0.357 0.365 0.373 0.381 ! PC_GS_angles - Gain-schedule table: pitch angles [rad]. +-1.813e-02 -1.538e-02 -1.318e-02 -1.138e-02 -9.887e-03 -8.625e-03 -7.545e-03 -6.610e-03 -5.793e-03 -5.072e-03 -4.432e-03 -3.860e-03 -3.345e-03 -2.880e-03 -2.457e-03 -2.071e-03 -1.717e-03 -1.392e-03 -1.092e-03 -8.142e-04 -5.563e-04 -3.163e-04 -9.232e-05 1.171e-04 3.135e-04 4.978e-04 6.713e-04 8.349e-04 9.893e-04 1.135e-03 ! PC_GS_KP - Gain-schedule table: pitch controller kp gains [s]. +-2.846e-03 -2.526e-03 -2.270e-03 -2.062e-03 -1.889e-03 -1.742e-03 -1.617e-03 -1.508e-03 -1.413e-03 -1.330e-03 -1.255e-03 -1.189e-03 -1.129e-03 -1.075e-03 -1.026e-03 -9.810e-04 -9.400e-04 -9.022e-04 -8.674e-04 -8.351e-04 -8.052e-04 -7.773e-04 -7.513e-04 -7.270e-04 -7.042e-04 -6.828e-04 -6.626e-04 -6.436e-04 -6.257e-04 -6.087e-04 ! PC_GS_KI - Gain-schedule table: pitch controller ki gains [-]. 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 ! PC_GS_KD - Gain-schedule table: pitch controller kd gains 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 ! PC_GS_TF - Gain-schedule table: pitch controller tf gains (derivative filter) 1.570000000000 ! PC_MaxPit - Maximum physical pitch limit, [rad]. @@ -95,7 +95,7 @@ !------- FIXED PITCH REGION 3 TORQUE CONTROL ------------------------------------------------ 60 ! VS_FBP_n - Number of gain-scheduling table entries -0.5000 0.5517 0.6034 0.6552 0.7069 0.7586 0.8103 0.8621 0.9138 0.9655 1.0172 1.0690 1.1207 1.1724 1.2241 1.2759 1.3276 1.3793 1.4310 1.4828 1.5345 1.5862 1.6379 1.6897 1.7414 1.7931 1.8448 1.8966 1.9483 2.0000 2.0333 2.0667 2.1000 2.1333 2.1667 2.2000 2.2333 2.2667 2.3000 2.3333 2.3667 2.4000 2.4333 2.4667 2.5000 2.5333 2.5667 2.6000 2.6333 2.6667 2.7000 2.7333 2.7667 2.8000 2.8333 2.8667 2.9000 2.9333 2.9667 3.0000 ! VS_FBP_U - Operating schedule table: Wind speeds [m/s]. +0.5000 0.5517 0.6034 0.6552 0.7069 0.7586 0.8103 0.8621 0.9138 0.9655 1.0172 1.0690 1.1207 1.1724 1.2241 1.2759 1.3276 1.3793 1.4310 1.4828 1.5345 1.5862 1.6379 1.6897 1.7414 1.7931 1.8448 1.8966 1.9483 2.0000 2.0667 2.1333 2.2000 2.2667 2.3333 2.4000 2.4667 2.5333 2.6000 2.6667 2.7333 2.8000 2.8667 2.9333 3.0000 3.0667 3.1333 3.2000 3.2667 3.3333 3.4000 3.4667 3.5333 3.6000 3.6667 3.7333 3.8000 3.8667 3.9333 4.0000 ! VS_FBP_U - Operating schedule table: Wind speeds [m/s]. 1.8550e+01 2.0469e+01 2.2388e+01 2.4307e+01 2.6226e+01 2.8145e+01 3.0064e+01 3.1983e+01 3.3902e+01 3.5821e+01 3.7740e+01 3.9659e+01 4.1578e+01 4.3497e+01 4.5416e+01 4.7334e+01 4.9253e+01 5.1172e+01 5.3091e+01 5.5010e+01 5.6929e+01 5.8848e+01 6.0767e+01 6.2686e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 6.3812e+01 ! VS_FBP_Omega - Operating schedule table: Generator speeds [rad/s]. 4.4631e+02 5.4342e+02 6.5009e+02 7.6631e+02 8.9208e+02 1.0274e+03 1.1723e+03 1.3267e+03 1.4907e+03 1.6642e+03 1.8473e+03 2.0400e+03 2.2422e+03 2.4539e+03 2.6752e+03 2.9060e+03 3.1464e+03 3.3964e+03 3.6559e+03 3.9250e+03 4.2036e+03 4.4917e+03 4.7894e+03 5.0967e+03 5.4773e+03 5.9648e+03 6.4710e+03 6.9951e+03 7.5363e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 8.0937e+03 ! VS_FBP_Tau - Operating schedule table: Generator torques [N m]. @@ -113,8 +113,8 @@ 0.1752 0.1702 0.1651 0.1601 0.1547 0.1492 0.1437 0.1377 0.1316 0.1253 0.1184 0.1116 0.1038 0.0960 0.0869 0.0774 0.0663 0.0530 0.0360 0.0000 ! PRC_Pitch_Table - Table of fine pitch versus PRC_R_Table, length should be PRC_Table_n [rad]. Used if PRC_Mode = 1. 2 ! PRC_n - Number of elements in PRC_WindSpeeds and PRC_GenSpeeds array 0.07854 ! PRC_LPF_Freq - Frequency of the low pass filter on the wind speed estimate used to set PRC_GenSpeeds [rad/s] -3.0000 25.0000 ! PRC_WindSpeeds - Array of wind speeds used in rotor speed vs. wind speed lookup table [m/s] -7.9168e-01 7.9168e-01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] +0.5000 4.0000 ! PRC_WindSpeeds - Array of wind speeds used in rotor speed vs. wind speed lookup table [m/s] +6.3812e+01 6.3812e+01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] !------- WIND SPEED ESTIMATOR --------------------------------------------- 10.000 ! WE_BladeRadius - Blade length (distance from hub center to blade tip), [m] @@ -127,8 +127,8 @@ "MHK_RM1_Cp_Ct_Cq.txt" ! PerfFileName - File containing rotor performance tables (Cp,Ct,Cq) (absolute path or relative to this file) 36 49 ! PerfTableSize - Size of rotor performance tables, first number refers to number of blade pitch angles, second number referse to number of tip-speed ratios 60 ! WE_FOPoles_N - Number of first-order system poles used in EKF -0.500 0.552 0.603 0.655 0.707 0.759 0.810 0.862 0.914 0.966 1.017 1.069 1.121 1.172 1.224 1.276 1.328 1.379 1.431 1.483 1.534 1.586 1.638 1.690 1.741 1.793 1.845 1.897 1.948 2.000 2.033 2.067 2.100 2.133 2.167 2.200 2.233 2.267 2.300 2.333 2.367 2.400 2.433 2.467 2.500 2.533 2.567 2.600 2.633 2.667 2.700 2.733 2.767 2.800 2.833 2.867 2.900 2.933 2.967 3.000 ! WE_FOPoles_v - Wind speeds corresponding to first-order system poles [m/s] --2.996e-02 -3.306e-02 -3.616e-02 -3.926e-02 -4.236e-02 -4.546e-02 -4.856e-02 -5.166e-02 -5.475e-02 -5.785e-02 -6.095e-02 -6.405e-02 -6.715e-02 -7.025e-02 -7.335e-02 -7.645e-02 -7.955e-02 -8.265e-02 -8.575e-02 -8.885e-02 -9.195e-02 -9.505e-02 -9.814e-02 -1.012e-01 -1.045e-01 -1.078e-01 -1.105e-01 -1.126e-01 -1.141e-01 -1.148e-01 3.763e-02 3.168e-02 2.301e-02 1.259e-02 5.960e-04 -1.099e-02 -2.403e-02 -3.679e-02 -5.073e-02 -6.410e-02 -7.882e-02 -9.271e-02 -1.075e-01 -1.226e-01 -1.375e-01 -1.533e-01 -1.687e-01 -1.843e-01 -2.009e-01 -2.166e-01 -2.329e-01 -2.499e-01 -2.663e-01 -2.830e-01 -3.004e-01 -3.175e-01 -3.344e-01 -3.520e-01 -3.701e-01 -3.872e-01 ! WE_FOPoles - First order system poles [1/s] +0.500 0.552 0.603 0.655 0.707 0.759 0.810 0.862 0.914 0.966 1.017 1.069 1.121 1.172 1.224 1.276 1.328 1.379 1.431 1.483 1.534 1.586 1.638 1.690 1.741 1.793 1.845 1.897 1.948 2.000 2.067 2.133 2.200 2.267 2.333 2.400 2.467 2.533 2.600 2.667 2.733 2.800 2.867 2.933 3.000 3.067 3.133 3.200 3.267 3.333 3.400 3.467 3.533 3.600 3.667 3.733 3.800 3.867 3.933 4.000 ! WE_FOPoles_v - Wind speeds corresponding to first-order system poles [m/s] +-2.996e-02 -3.306e-02 -3.616e-02 -3.926e-02 -4.236e-02 -4.546e-02 -4.856e-02 -5.166e-02 -5.475e-02 -5.785e-02 -6.095e-02 -6.405e-02 -6.715e-02 -7.025e-02 -7.335e-02 -7.645e-02 -7.955e-02 -8.265e-02 -8.575e-02 -8.885e-02 -9.195e-02 -9.505e-02 -9.814e-02 -1.012e-01 -1.045e-01 -1.078e-01 -1.105e-01 -1.126e-01 -1.141e-01 -1.148e-01 3.168e-02 1.259e-02 -1.099e-02 -3.679e-02 -6.410e-02 -9.271e-02 -1.226e-01 -1.533e-01 -1.843e-01 -2.166e-01 -2.499e-01 -2.830e-01 -3.175e-01 -3.520e-01 -3.872e-01 -4.233e-01 -4.590e-01 -4.963e-01 -5.326e-01 -5.705e-01 -6.075e-01 -6.460e-01 -6.837e-01 -7.226e-01 -7.607e-01 -8.000e-01 -8.386e-01 -8.782e-01 -9.174e-01 -9.570e-01 ! WE_FOPoles - First order system poles [1/s] !------- YAW CONTROL ------------------------------------------------------ 0.00000 ! Y_uSwitch - Wind speed to switch between Y_ErrThresh. If zero, only the second value of Y_ErrThresh is used [m/s] @@ -149,8 +149,8 @@ !------- MINIMUM PITCH SATURATION ------------------------------------------- 60 ! PS_BldPitchMin_N - Number of values in minimum blade pitch lookup table (should equal number of values in PS_WindSpeeds and PS_BldPitchMin) -0.5000 0.5517 0.6034 0.6552 0.7069 0.7586 0.8103 0.8621 0.9138 0.9655 1.0172 1.0690 1.1207 1.1724 1.2241 1.2759 1.3276 1.3793 1.4310 1.4828 1.5345 1.5862 1.6379 1.6897 1.7414 1.7931 1.8448 1.8966 1.9483 2.0000 2.0333 2.0667 2.1000 2.1333 2.1667 2.2000 2.2333 2.2667 2.3000 2.3333 2.3667 2.4000 2.4333 2.4667 2.5000 2.5333 2.5667 2.6000 2.6333 2.6667 2.7000 2.7333 2.7667 2.8000 2.8333 2.8667 2.9000 2.9333 2.9667 3.0000 ! PS_WindSpeeds - Wind speeds corresponding to minimum blade pitch angles [m/s] -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0077 0.0181 0.0276 0.0368 0.0456 0.0543 0.0597 0.0650 0.0703 0.0754 0.0805 0.0857 0.0906 0.0955 0.1004 0.1053 0.1100 0.1148 0.1195 0.1242 0.1288 0.1334 0.1380 0.1425 0.1470 0.1515 0.1560 0.1604 0.1648 0.1691 0.1736 0.1778 0.1821 0.1864 0.1907 0.1950 ! PS_BldPitchMin - Minimum blade pitch angles [rad] +0.5000 0.5517 0.6034 0.6552 0.7069 0.7586 0.8103 0.8621 0.9138 0.9655 1.0172 1.0690 1.1207 1.1724 1.2241 1.2759 1.3276 1.3793 1.4310 1.4828 1.5345 1.5862 1.6379 1.6897 1.7414 1.7931 1.8448 1.8966 1.9483 2.0000 2.0667 2.1333 2.2000 2.2667 2.3333 2.4000 2.4667 2.5333 2.6000 2.6667 2.7333 2.8000 2.8667 2.9333 3.0000 3.0667 3.1333 3.2000 3.2667 3.3333 3.4000 3.4667 3.5333 3.6000 3.6667 3.7333 3.8000 3.8667 3.9333 4.0000 ! PS_WindSpeeds - Wind speeds corresponding to minimum blade pitch angles [m/s] +0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0077 0.0181 0.0276 0.0368 0.0456 0.0543 0.0650 0.0754 0.0857 0.0955 0.1053 0.1148 0.1242 0.1334 0.1425 0.1515 0.1604 0.1691 0.1778 0.1864 0.1950 0.2033 0.2117 0.2200 0.2282 0.2363 0.2444 0.2524 0.2604 0.2682 0.2760 0.2838 0.2915 0.2991 0.3067 0.3143 ! PS_BldPitchMin - Minimum blade pitch angles [rad] !------- STARTUP ----------------------------------------------------------- 120.0000000000 ! SU_StartTime - Time to start startup routine [s] @@ -168,7 +168,7 @@ 0 ! SD_EnableYawError - Shutdown when yaw error exceeds a threshold, [-] 0 ! SD_EnableGenSpeed - Shutdown when generator speed exceeds a threshold, [-] 0 ! SD_EnableTime - Shutdown at a predefined time, [-] -0.249980000000 ! SD_MaxPit - Maximum blade pitch angle to initiate shutdown, [rad] +0.381260000000 ! SD_MaxPit - Maximum blade pitch angle to initiate shutdown, [rad] 0.418880000000 ! SD_PitchCornerFreq - Cutoff Frequency for first order low-pass filter for blade pitch angle for shutdown, [rad/s] 30.00000000000 ! SD_MaxYawError - Maximum yaw error to initiate shutdown, [deg] 0.418880000000 ! SD_YawErrorCornerFreq - Cutoff Frequency for first order low-pass filter for yaw error for shutdown, [rad/s] @@ -184,7 +184,7 @@ !------- Floating ----------------------------------------------------------- 1 ! Fl_n - Number of Fl_Kp gains in gain scheduling, optional with default of 1 --0.4002 ! Fl_Kp - Nacelle velocity proportional feedback gain [s] +-0.4160 ! Fl_Kp - Nacelle velocity proportional feedback gain [s] 2.1000 ! Fl_U - Wind speeds for scheduling Fl_Kp, optional if Fl_Kp is single value [m/s] !------- FLAP ACTUATION ----------------------------------------------------- diff --git a/Examples/Test_Cases/NREL-5MW/DISCON.IN b/Examples/Test_Cases/NREL-5MW/DISCON.IN index 0a74b9c94..93a7c1dde 100644 --- a/Examples/Test_Cases/NREL-5MW/DISCON.IN +++ b/Examples/Test_Cases/NREL-5MW/DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the NREL-5MW wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 1 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,7 +10,7 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 1 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -2 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +2 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 1 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) @@ -114,7 +114,7 @@ 2 ! PRC_n - Number of elements in PRC_WindSpeeds and PRC_GenSpeeds array 0.07854 ! PRC_LPF_Freq - Frequency of the low pass filter on the wind speed estimate used to set PRC_GenSpeeds [rad/s] 3.0000 25.0000 ! PRC_WindSpeeds - Array of wind speeds used in rotor speed vs. wind speed lookup table [m/s] -7.9168e-01 7.9168e-01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] +1.2291e+02 1.2291e+02 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] !------- WIND SPEED ESTIMATOR --------------------------------------------- 63.000 ! WE_BladeRadius - Blade length (distance from hub center to blade tip), [m] diff --git a/Examples/Test_Cases/NREL_2p8_127/NREL-2p8-127_DISCON.IN b/Examples/Test_Cases/NREL_2p8_127/NREL-2p8-127_DISCON.IN index e50f61bd8..6b3369022 100644 --- a/Examples/Test_Cases/NREL_2p8_127/NREL-2p8-127_DISCON.IN +++ b/Examples/Test_Cases/NREL_2p8_127/NREL-2p8-127_DISCON.IN @@ -1,5 +1,5 @@ ! Controller parameter input file for the NREL-2p8-127 wind turbine -! - File written using ROSCO version 2.10.6 controller tuning logic on 06/09/26 +! - File written using ROSCO version 2.10.6 controller tuning logic on 09/16/26 !------- SIMULATION CONTROL ------------------------------------------------------------ 2 ! LoggingLevel - 0: write no debug files, 1: write standard output .dbg-file, 2: LoggingLevel 1 + ROSCO LocalVars (.dbg2) 3: LoggingLevel 2 + complete avrSWAP-array (.dbg3) @@ -10,7 +10,7 @@ !------- CONTROLLER FLAGS ------------------------------------------------- 1 ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals 1 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions) -3 ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) +3 ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) 0 ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque) 0 ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking) 1 ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control) @@ -114,7 +114,7 @@ 2 ! PRC_n - Number of elements in PRC_WindSpeeds and PRC_GenSpeeds array 0.07854 ! PRC_LPF_Freq - Frequency of the low pass filter on the wind speed estimate used to set PRC_GenSpeeds [rad/s] 3.0000 25.0000 ! PRC_WindSpeeds - Array of wind speeds used in rotor speed vs. wind speed lookup table [m/s] -7.9168e-01 7.9168e-01 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] +1.2291e+02 1.2291e+02 ! PRC_GenSpeeds - Array of generator speeds corresponding to PRC_WindSpeeds [rad/s] !------- WIND SPEED ESTIMATOR --------------------------------------------- 63.457 ! WE_BladeRadius - Blade length (distance from hub center to blade tip), [m] diff --git a/Examples/Tune_Cases/BAR.yaml b/Examples/Tune_Cases/BAR.yaml index bdf96e066..3fcf7de7f 100644 --- a/Examples/Tune_Cases/BAR.yaml +++ b/Examples/Tune_Cases/BAR.yaml @@ -27,7 +27,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW.yaml b/Examples/Tune_Cases/IEA15MW.yaml index c12112b0e..62c146122 100644 --- a/Examples/Tune_Cases/IEA15MW.yaml +++ b/Examples/Tune_Cases/IEA15MW.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_ExtInterface.yaml b/Examples/Tune_Cases/IEA15MW_ExtInterface.yaml index 30a8f783a..b52b1510f 100644 --- a/Examples/Tune_Cases/IEA15MW_ExtInterface.yaml +++ b/Examples/Tune_Cases/IEA15MW_ExtInterface.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_FOCAL.yaml b/Examples/Tune_Cases/IEA15MW_FOCAL.yaml index 1785f77b0..915b58c0e 100644 --- a/Examples/Tune_Cases/IEA15MW_FOCAL.yaml +++ b/Examples/Tune_Cases/IEA15MW_FOCAL.yaml @@ -30,7 +30,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_MultiOmega.yaml b/Examples/Tune_Cases/IEA15MW_MultiOmega.yaml index 8b5b6577d..8152e67eb 100644 --- a/Examples/Tune_Cases/IEA15MW_MultiOmega.yaml +++ b/Examples/Tune_Cases/IEA15MW_MultiOmega.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_OL.yaml b/Examples/Tune_Cases/IEA15MW_OL.yaml index 8bd515a2c..699730839 100644 --- a/Examples/Tune_Cases/IEA15MW_OL.yaml +++ b/Examples/Tune_Cases/IEA15MW_OL.yaml @@ -32,7 +32,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 1 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_PRC.yaml b/Examples/Tune_Cases/IEA15MW_PRC.yaml index 2a0faacba..abb78c150 100644 --- a/Examples/Tune_Cases/IEA15MW_PRC.yaml +++ b/Examples/Tune_Cases/IEA15MW_PRC.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_ballast.yaml b/Examples/Tune_Cases/IEA15MW_ballast.yaml index 95013c483..c34e6e4de 100644 --- a/Examples/Tune_Cases/IEA15MW_ballast.yaml +++ b/Examples/Tune_Cases/IEA15MW_ballast.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_cable.yaml b/Examples/Tune_Cases/IEA15MW_cable.yaml index 8b67b82bc..2ef1b13e1 100644 --- a/Examples/Tune_Cases/IEA15MW_cable.yaml +++ b/Examples/Tune_Cases/IEA15MW_cable.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/IEA15MW_robust.yaml b/Examples/Tune_Cases/IEA15MW_robust.yaml index ea2ab70ad..55bf71155 100644 --- a/Examples/Tune_Cases/IEA15MW_robust.yaml +++ b/Examples/Tune_Cases/IEA15MW_robust.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 2 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch on the measured generator speed {0: disable, 1: enable} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/NREL5MW.yaml b/Examples/Tune_Cases/NREL5MW.yaml index 69f78982c..62d57dbe6 100644 --- a/Examples/Tune_Cases/NREL5MW.yaml +++ b/Examples/Tune_Cases/NREL5MW.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 1 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch filter on generator speed and/or tower fore-aft motion (for floating) {0: disable, 1: generator speed, 2: tower-top fore-aft motion, 3: generator speed and tower-top fore-aft motion} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} VS_ConstPower: 1 # Use constant power PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} diff --git a/Examples/Tune_Cases/NREL5MW_PassThrough.yaml b/Examples/Tune_Cases/NREL5MW_PassThrough.yaml index 2db0472e9..1ce3c5a01 100644 --- a/Examples/Tune_Cases/NREL5MW_PassThrough.yaml +++ b/Examples/Tune_Cases/NREL5MW_PassThrough.yaml @@ -31,7 +31,7 @@ controller_params: F_LPFType: 1 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 0 # Notch filter on generator speed and/or tower fore-aft motion (for floating) {0: disable, 1: generator speed, 2: tower-top fore-aft motion, 3: generator speed and tower-top fore-aft motion} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 2 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 2 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} VS_ConstPower: 1 # Use constant power PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} diff --git a/Examples/Tune_Cases/RM1_MHK.yaml b/Examples/Tune_Cases/RM1_MHK.yaml index 4262abc46..2fef67ef0 100644 --- a/Examples/Tune_Cases/RM1_MHK.yaml +++ b/Examples/Tune_Cases/RM1_MHK.yaml @@ -16,7 +16,7 @@ turbine_params: rated_rotor_speed: 1.204 # 11.5 rpm # Rated rotor speed [rad/s] v_min: 0.5 # Cut-in wind speed [m/s] v_rated: 2.0 # Rated wind speed [m/s] - v_max: 3.0 # Cut-out wind speed [m/s], -- Does not need to be exact (JUST ASSUME FOR NOW) + v_max: 4.0 # Cut-out wind speed [m/s], -- Does not need to be exact (JUST ASSUME FOR NOW) max_pitch_rate: 0.1745 # Maximum blade pitch rate [rad/s] max_torque_rate: 7800. # Maximum torque rate [Nm/s], {~1/4 VS_RtTq/s} rated_power: 500000 # Rated Power [W] @@ -32,12 +32,12 @@ controller_params: F_LPFType: 1 # {1: first-order low-pass filter, 2: second-order low-pass filter}, [rad/s] (currently filters generator speed and pitch control signals) F_NotchType: 1 # Notch filter on generator speed and/or tower fore-aft motion (for floating) {0: disable, 1: generator speed, 2: tower-top fore-aft motion, 3: generator speed and tower-top fore-aft motion} IPC_ControlMode: 0 # Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) {0: off, 1: 1P reductions, 2: 1P+2P reductions} - VS_ControlMode: 3 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} - VS_ConstPower: 1 # Generator torque control mode in above rated conditions {0: constant torque, 1: constant power, 2: TSR tracking PI control} + VS_ControlMode: 3 # Generator torque control mode in below rated conditions {0: torque control disabled, 1: K*Omega^2, 2: WSE TSR-tracking PI control, 3: power TSR-tracking PI control, 4: Torque TSR-tracking PI control} + VS_ConstPower: 1 # Generator torque control mode in above rated conditions with variable blade pitch {0: constant torque, 1: constant power} PC_ControlMode: 1 # Blade pitch control mode {0: No pitch, fix to fine pitch, 1: active PI blade pitch control} Y_ControlMode: 0 # Yaw control mode {0: no yaw control, 1: yaw rate control, 2: yaw-by-IPC} SS_Mode: 1 # Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} - WE_Mode: 0 # Wind speed estimator mode {0: One-second low pass filtered hub height wind speed, 1: Immersion and Invariance Estimator (Ortega et al.)} + WE_Mode: 2 # Wind speed estimator mode {0: One-second low pass filtered hub height wind speed, 1: Immersion and Invariance Estimator (Ortega et al.), 2: Extended Kalman Filter} PS_Mode: 1 # Pitch saturation mode {0: no pitch saturation, 1: peak shaving, 2: Cp-maximizing pitch saturation, 3: peak shaving and Cp-maximizing pitch saturation} SD_Mode: 0 # Shutdown mode {0: no shutdown procedure, 1: pitch to max pitch at shutdown} Fl_Mode: 1 # Floating specific feedback mode {0: no nacelle velocity feedback, 1: nacelle velocity feedback} diff --git a/rosco/toolbox/inputs/toolbox_schema.yaml b/rosco/toolbox/inputs/toolbox_schema.yaml index 84c601289..27297b7dd 100644 --- a/rosco/toolbox/inputs/toolbox_schema.yaml +++ b/rosco/toolbox/inputs/toolbox_schema.yaml @@ -130,7 +130,7 @@ properties: minimum: 0 maximum: 4 default: 2 - description: Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) + description: Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) VS_ConstPower: type: number minimum: 0 @@ -647,7 +647,7 @@ properties: type: number minimum: 0 maximum: 4 - description: Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) + description: Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking) VS_ConstPower: type: number minimum: 0 diff --git a/rosco/toolbox/utilities.py b/rosco/toolbox/utilities.py index c2f91f078..c53d82b65 100644 --- a/rosco/toolbox/utilities.py +++ b/rosco/toolbox/utilities.py @@ -98,7 +98,7 @@ def write_DISCON(turbine, controller, param_file='DISCON.IN', txt_filename='Cp_C file.write('!------- CONTROLLER FLAGS -------------------------------------------------\n') file.write('{0:<12d} ! F_LPFType - 1: first-order low-pass filter, 2: second-order low-pass filter, [rad/s] (currently filters generator speed and pitch control signals\n'.format(int(rosco_vt['F_LPFType']))) file.write('{0:<12d} ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) (0: off, 1: 1P reductions, 2: 1P+2P reductions)\n'.format(int(rosco_vt['IPC_ControlMode']))) - file.write('{0:<12d} ! VS_ControlMode - Generator torque control mode in above rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking)\n'.format(int(rosco_vt['VS_ControlMode']))) + file.write('{0:<12d} ! VS_ControlMode - Generator torque control mode in below rated conditions (0- no torque control, 1- k*omega^2 with PI transitions, 2- WSE TSR Tracking, 3- Power-based TSR Tracking, 4- Torque-based TSR Tracking)\n'.format(int(rosco_vt['VS_ControlMode']))) file.write('{0:<12d} ! VS_ConstPower - Do constant power torque control, where above rated torque varies, 0 for constant torque)\n'.format(int(rosco_vt['VS_ConstPower']))) file.write('{0:<12d} ! VS_FBP - Fixed blade pitch configuration mode (0- variable pitch (disabled), 1- constant power overspeed, 2- WSE-lookup reference tracking, 3- torque-lookup reference tracking)\n'.format(int(rosco_vt['VS_FBP']))) file.write('{0:<12d} ! PC_ControlMode - Blade pitch control mode (0: No pitch, fix to fine pitch, 1: active PI blade pitch control)\n'.format(int(rosco_vt['PC_ControlMode']))) @@ -614,8 +614,8 @@ def DISCON_dict(turbine, controller, txt_filename=None): DISCON_dict['SS_PCGain'] = controller.ss_pcgain # -------- POWER REFERENCE TRACKING ------ DISCON_dict['PRC_n'] = 2 - DISCON_dict['PRC_WindSpeeds'] = [3,25] - DISCON_dict['PRC_GenSpeeds'] = [rpm2RadSec * 7.56] * 2 + DISCON_dict['PRC_WindSpeeds'] = [turbine.v_min, turbine.v_max] + DISCON_dict['PRC_GenSpeeds'] = [DISCON_dict['PC_RefSpd']] * 2 # ------- WIND SPEED ESTIMATOR ------- DISCON_dict['WE_BladeRadius'] = turbine.rotor_radius From adb7625449fdc1021d9ffd09e29250e382d715ea Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 16 Sep 2026 15:08:40 -0600 Subject: [PATCH 13/21] Add variable-pitch section --- docs/source/marine_hydro.rst | 176 +++++++++++++++++++++++++++++++++-- 1 file changed, 169 insertions(+), 7 deletions(-) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index ba22843a7..7bbdce8c9 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -52,16 +52,178 @@ schedule, a configurable control approach for realizing that operating schedule, and automatically tuned feedback gains. -Over/Underspeed Reference Setpoints ------------------------------------ +.. _variable_pitch_mhk: + +Variable-Pitch MHK Control +--------------------------- + +Nothing in the ROSCO controller changes for an MHK turbine that does have pitch +actuators. +The same collective pitch PI controller, gain scheduling, peak shaving, and +setpoint smoothing are used as for a wind turbine. +Only the tuning inputs change, and a few of them change enough to be worth noting. +A worked example is provided in :code:`Examples/26_marine_hydro.py`, tuned from +:code:`Examples/Tune_Cases/RM1_MHK.yaml`. +The comparisons below are against the NREL 2.8 MW land-based reference turbine +(:code:`Examples/Tune_Cases/NREL2p8.yaml`). + +Fluid Density +^^^^^^^^^^^^^^ + +Density selection is the only MHK-specific branch in the tuning toolbox. +When the OpenFAST model sets :code:`MHK > 0`, the toolbox uses the water density +:code:`WtrDens` in place of :code:`AirDens`. +The sensitivities used for gain scheduling +(:math:`\partial\tau/\partial\beta`, :math:`\partial\tau/\partial\lambda`) and the +thrust used for peak shaving all scale linearly with :math:`\rho`, so the rotor +performance tables and the density must describe the same fluid. +Otherwise the gains are wrong by nearly three orders of magnitude. +In the generated DISCON file this value appears as :code:`WE_RhoAir`, which is +1025 kg/m³ for the RM1, despite the parameter name. + +A Compressed Region 3 +^^^^^^^^^^^^^^^^^^^^^^ + +Tidal flow speeds span a much narrower range than wind speeds, which compresses +everything the above-rated controller has to work with. + +.. list-table:: + :header-rows: 1 + :widths: auto + + * - + - NREL 2.8 MW (wind) + - RM1 (MHK) + * - Cut-in / rated / cut-out + - 3 / 11.4 / 25 m/s + - 0.5 / 2.0 / 4.0 m/s + * - Cut-out / rated ratio + - 2.2 + - 2.0 + * - Gain-schedule pitch range + - 4.4° to 25.6° + - 1.7° to 14.3° + * - Rotor performance table + - 30 x 30, TSR 2–12 + - 36 x 49, TSR 0.5–24.5 + +The entire above-rated pitch travel is roughly 12°, so the pitch resolution of the +:math:`C_p` table limits the quality of the gain schedule in a way it does not for +a wind turbine. +The RM1 table is generated at 1° pitch resolution for this reason. +The pitch rate limit :code:`PC_MaxRat` is rarely the binding constraint: the RM1 +keeps the wind default of 0.1745 rad/s (10°/s), which traverses the full Region 3 +range in about a second. + +Pitch Bandwidth and Filtering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The RM1 is tuned at :code:`omega_pc` = 0.35 rad/s with :code:`zeta_pc` = 1.0, +against 0.137 rad/s and 2.0 for the 2.8 MW wind turbine, so the pitch loop is +roughly 2.5x faster. +Rated rotor speed is nearly the same for both machines (1.20 versus 1.27 rad/s). +The separation between the pitch control bandwidth and 1P therefore falls from +about a factor of nine to about a factor of three. +The RM1 is also a two-bladed rotor, so 1P and 2P both appear directly in the +generator speed measurement, without the averaging a three-bladed rotor provides. + +The MHK tuning case therefore enables filtering that the wind cases leave off. +Two notch filters are placed at 1.0 and 2.42 rad/s (1P and 2P) and applied to both +the generator speed and the tower-top measurements: + +.. code-block:: yaml + + DISCON: + F_NumNotchFilts: 2 + F_NotchFreqs: [1.0, 2.42] # 1P, 2P + F_NotchBetaNum: [0.0, 0.0] + F_NotchBetaDen: [0.25, 0.25] + F_GenSpdNotch_N: 2 + F_GenSpdNotch_Ind: [1, 2] + F_TwrTopNotch_N: 2 + F_TwrTopNotch_Ind: [1, 2] + +The low-pass corner frequency moves the other way. +:code:`F_LPFCornerFreq` is derived from the blade edgewise frequency, and +hydrokinetic blades are short and stiff: 60.3 rad/s for the RM1 against 8.3 rad/s +for the 2.8 MW rotor. +The resulting corner frequency is 15.1 rad/s rather than 2.07 rad/s, well above +the control bandwidth, so the notch filters, rather than the low-pass filter, +limit the achievable bandwidth. + +Setpoint smoothing is also more aggressive on the pitch side, +:code:`SS_PCGain` = 0.05 against 0.001, because the Region 2.5 transition is +compressed into a narrow band of flow speed. + +Minimum Pitch and Peak Shaving +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The RM1 uses :code:`PC_MinPit` = 0, whereas the wind cases allow pitch to go +negative (-10° for the 2.8 MW) to recover a small amount of :math:`C_p` below +rated. +Peak shaving is enabled with :code:`ps_percent` = 0.8, which produces a minimum +pitch schedule that peaks near 11°, compared with about 20° for the wind turbine +over its much wider speed range. + +Peak shaving limits rotor thrust in the same way it does for a wind turbine. +Whether it also provides useful margin against cavitation depends on the rotor. +As shown in :ref:`speed_limits_cavitation`, the cavitation criterion is driven by +rotor speed and blade section :math:`-C_{p,min}`, and a pitch schedule is not a +substitute for evaluating it. +ROSCO performs no cavitation check at runtime, so a marginal variable-pitch MHK +schedule should be verified with an AeroDyn cavitation check +(:code:`CavitCheck = True`), which evaluates the full criterion at every blade node. + +.. TODO(DS): the RM1 tuning case sets ``PC_MinPit = 0`` rather than a negative + fine pitch. Please confirm whether this is a deliberate constraint (cavitation + or load-driven) or simply the default, so this can be stated rather than + described. + +Torque Control and Wind Speed Estimation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Above rated, the RM1 case uses constant power (:code:`VS_ConstPower` = 1) rather +than the constant torque used by the wind cases, with +:code:`max_torque_factor` = 1.5 sizing the torque saturation limit above rated +torque. +Individual pitch control is disabled, as the rotor has two blades. + +Note that :code:`VS_ControlMode` selects the *below*-rated torque control +strategy; :code:`VS_ConstPower` selects constant-torque versus constant-power +behavior above rated. +The RM1 case uses :code:`VS_ControlMode` = 3 (power-based TSR tracking) below +rated. + +The wind speed estimator is used as it is for wind turbines +(:code:`WE_Mode` = 2, the extended Kalman filter). +The estimate drives gain scheduling, the peak shaving minimum pitch lookup, and +setpoint smoothing, so degrading it to the filtered measurement +(:code:`WE_Mode` = 0) affects all three. + +Floating MHK +^^^^^^^^^^^^^ + +For a floating MHK turbine (:code:`MHK = 2`), the platform feedback term behaves +as it does for floating wind, but with much less frequency separation to work +with. +The RM1 floating case has a platform frequency of 0.4 rad/s against a pitch +control bandwidth of 0.35 rad/s, roughly a factor of 1.15. +The negative damping problem therefore cannot be avoided by detuning the pitch +controller, and is instead handled by the floating feedback term +(:code:`Fl_Mode` = 1, :code:`Kp_float` = -0.4). +Restoring stiffness comes from the mooring system rather than from waterplane +area, and wave excitation is attenuated with depth but not eliminated. + + +Over/Underspeed Reference Setpoints for Fixed-Pitch Control +----------------------------------------------------------- The steady state generator-speed setpoints are determined by the :math:`C_p` contours intersecting with the fine-pitch line. -For the RM1, overspeed reaches up to 3x rated speed. How far a given rotor can -actually use that range is limited by cavitation, discussed in -:ref:`speed_limits_cavitation`; on the RM1 the limit binds well before 3x. -Rotor speed also has consequences for -blade loads (high thrust). +For the RM1, overspeed reaches up to 3x rated speed. +How much of that range a given rotor can use is limited by cavitation, discussed +in :ref:`speed_limits_cavitation`; on the RM1 the limit binds well before 3x. +Rotor speed also has consequences for blade loads (high thrust). .. _cp_wg_sched: .. figure:: /images/mhk/03_cp_wg_sched.png From 3fa79c7046633a5d585258975bb3829bac4b0d6a Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 16 Sep 2026 15:10:16 -0600 Subject: [PATCH 14/21] Copy-edit the marine_hydro docs --- docs/source/marine_hydro.rst | 285 +++++++++++++++++++---------------- 1 file changed, 154 insertions(+), 131 deletions(-) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index 7bbdce8c9..d87dbd85c 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -21,8 +21,9 @@ maximum tip speed ratio (TSR) and pitch angle, as it is for wind turbines The ROSCO baseline for marine turbines is adapted from the variable-pitch wind turbine controller [Abbas2022]_. -Many marine turbine designs, however, lack pitch actuators: the cost, complexity, -and reliability burden of a submerged pitch system is often prohibitive. +Many marine turbine designs, however, do not have pitch actuators, because the +cost, complexity, and reliability burden of a submerged pitch system is often +prohibitive. Assuming a fixed (optimal) blade pitch, we instead redefine the operating schedule of TSR to find the equilibrium between hydrodynamic and generator torque. This trades pitch actuation for trade-offs in blade loads, rotor speed, generator @@ -35,8 +36,8 @@ schemes similar to those used by wind turbines, which reduce the :math:`C_p` surface by increasing the pitch angle and decreasing TSR (black). For MHK turbines without pitch actuation, we provide a few control methods for controlling the power of the turbine using only torque control. -Any such controller is constrained by the requirement that steady-state operating -points be in equilibrium with the inflow torque. +Any such controller is constrained by the requirement that the steady state +operating points be in equilibrium with the inflow torque. Overspeed control increases the TSR along the fixed pitch line on the :math:`C_p` surface below (blue circle) by decreasing the generator torque. Underspeed control decreases the TSR (red circle) by increasing the torque. @@ -256,17 +257,17 @@ Over/Underspeed Dynamics .. :width: 90% At each operating point, the sensitivity is computed using the gradients of the -:math:`C_p` surface, in the same manner as the Region-2 controller. +:math:`C_p` surface, in the same manner as the Region 2 controller. The first-order system decay rate is represented by a single pole on the real axis: more negative means more rapidly stable (positive means unstable). Underspeed set points are open-loop unstable at high flow speeds. -The sign follows from what each set point is asking the rotor to do. +The sign follows from what each set point asks the rotor to do. Underspeed set points sit below :math:`TSR_{opt}`, where hydrodynamic torque still -grows with speed, and generator torque is the only thing holding the speed down; a -small speed perturbation grows rather than decays, so losing torque control lets +grows with speed and generator torque is the only thing holding the speed down. +A small speed perturbation grows rather than decays, so losing torque control lets the rotor run away. -Overspeed set points are already past :math:`TSR_{opt}`, where hydrodynamic torque -falls off with speed, so the rotor is let out to a self-limiting operating point by -design and any speed perturbation decays on its own. +Overspeed set points are past :math:`TSR_{opt}`, where hydrodynamic torque falls +off with speed, so the operating point is self-limiting and any speed perturbation +decays on its own. .. _cp_Agen_sched_annotated: .. figure:: /images/mhk/07_cp_Agen_sched_annotated.png @@ -280,8 +281,9 @@ design and any speed perturbation decays on its own. The same result can be viewed in the flow-speed/generator-speed plane. Contours of constant hydrodynamic rotor torque intersect the constant-power -generator torque schedule at the equilibrium operating points; the overspeed and -underspeed branches are the two intersections available at each flow speed. +generator torque schedule at the equilibrium operating points. +The overspeed and underspeed branches are the two intersections available at each +flow speed. The local slope of the torque surface across each intersection determines whether that equilibrium is open-loop stable. @@ -311,11 +313,11 @@ Alternate Region 3 Operating Schedules Using the ROSCO toolbox, we enable the user to determine their own operational power curve, besides a constant rated power. Throughout this section, the *power requirement* is the generator power that the -selected Region-3 power curve demands at a given flow speed, i.e. the +selected Region 3 power curve demands at a given flow speed, that is, the :code:`VS_FBP_P` input evaluated at that speed. -It is an upper bound on what is asked of the machine, not on what is available: -the toolbox clips it to the power the inflow can actually supply at fine pitch, -so the MPPT curve is the ceiling and no request can exceed it. +It is an upper bound on what is asked of the machine, not on what is available. +The toolbox clips it to the power the inflow can supply at fine pitch, so the MPPT +curve is the ceiling and no request can exceed it. Relaxing the constant-power constraint in Region 3 raises the peak of the power requirement, and with it the sustained power the generator must be sized for. The five power curves studied, with their peak power requirement at cut-out @@ -339,9 +341,10 @@ relative to rated power (equivalently, the generator resizing factor), are: - 1x These multipliers follow from the RM1 :math:`C_p` surface and its -:math:`v_{rated}` to :math:`v_{cut-out}` range, and are reported here to show the -relative spread between the curves. They are not general: the same five curve -definitions applied to another rotor will produce different factors. +:math:`v_{rated}` to :math:`v_{cut-out}` range, and are reported to show the +relative spread between the curves. +They are not general: the same five curve definitions applied to another rotor +will produce different factors. .. _ext_P: .. figure:: /images/mhk/11_ext_P.png @@ -356,11 +359,12 @@ Speed setpoints always lie within the envelope bounded by the over- and underspeed constant-power trajectories. Constant power has the lowest power requirement of the five, so it departs furthest from :math:`TSR_{opt}` and produces the widest speed excursion in either -direction; the MPPT curve has the highest requirement and does not depart at all, -tracking :math:`\Omega \propto v` straight through rated. -The constant-power schedule is therefore the bounding case for rotor overspeed -and cavitation margin on the overspeed branch, and for generator torque headroom -on the underspeed branch. +direction. +The MPPT curve has the highest requirement and does not depart at all, tracking +:math:`\Omega \propto v` straight through rated. +The constant-power schedule is therefore the bounding case for rotor overspeed and +cavitation margin on the overspeed branch, and for generator torque headroom on +the underspeed branch. .. _ext_wg_sched_annotated: .. figure:: /images/mhk/16_ext_wg_sched_annotated.png @@ -390,12 +394,12 @@ Note that the schedule above is plotted against rotor speed :math:`\Omega`, whil the torque/speed plane below is plotted against generator speed :math:`\Omega_g = N_g \Omega`; for the RM1 the gearbox ratio is :math:`N_g = 53`, so the two speed axes differ by that factor. -Dropping from the MPPT curve to the quadratic curve, a reduction in peak power -requirement from 8x to 7x rated, moves the overspeed setpoint at cut-out from -roughly 2.5 to 3.5 rad/s rotor speed in the schedule above (about 130 to 185 -rad/s at the generator): a 12% reduction in power buys a 40% change in speed. -These values are specific to the RM1; the qualitative sensitivity is general, the -numbers are not. +Dropping from the MPPT curve to the quadratic curve reduces the peak power +requirement from 8x to 7x rated, and moves the overspeed setpoint at cut-out from +roughly 2.5 to 3.5 rad/s rotor speed in the schedule above (about 130 to 185 rad/s +at the generator). +A 12% reduction in power buys a 40% change in speed. +These values are specific to the RM1. The two branches also look very different in this plane: the underspeed branches are nearly vertical (large torque change for small speed change), the overspeed branches nearly horizontal (large speed change for small torque change). @@ -423,8 +427,8 @@ torque controller. The power curve selection also impacts the rotor thrust (F). Underspeed control and lower power generally result in lower thrust. -Conversely, overspeed trajectories approach substantially higher rotor thrust; the -overspeed MPPT trajectory in particular reaches thrust levels that are likely +Overspeed trajectories approach substantially higher rotor thrust. +The overspeed MPPT trajectory in particular reaches thrust levels that are likely unacceptable for the structure, and should be checked against the design loads before being selected. @@ -444,13 +448,14 @@ before being selected. Rotor Speed Limits and Cavitation ---------------------------------- -Power and thrust are not the only constraints on an FBP operating schedule. For a -marine turbine, the rotor speed is also bounded by cavitation, and this bound is -what usually decides whether the overspeed branch is available at all. +Power and thrust are not the only constraints on an FBP operating schedule. +For a marine turbine, the rotor speed is also bounded by cavitation, and this +bound usually decides whether the overspeed branch is available at all. Cavitation occurs where the local pressure on the blade falls below the vapour -pressure of water. Writing the suction peak in terms of the section's minimum -pressure coefficient :math:`C_{p,min}`, +pressure of water. +Writing the suction peak in terms of the section's minimum pressure coefficient +:math:`C_{p,min}`, .. math:: @@ -462,17 +467,18 @@ which rearranges into the usual cavitation number criterion, \sigma = \frac{p_{atm} + \rho g h - p_{vap}}{\tfrac{1}{2}\rho W^2} > -C_{p,min} -Two things follow, and both matter for control design. +Two results follow for control design. -**Depth enters only as hydrostatic head.** The submergence :math:`h` appears -nowhere except in :math:`\rho g h`, which sets the pressure budget available to be -spent on suction. Seawater is dense enough that 10 m of depth adds about 101 kPa, -roughly one additional atmosphere, so submergence is a strong lever on the -available margin. Because the relevant depth is the *shallowest* point the blade -reaches, the binding condition is the blade tip at the top of its rotation. +Depth enters only as hydrostatic head. +The submergence :math:`h` appears nowhere except in :math:`\rho g h`, which sets +the pressure budget available to be spent on suction. +Seawater is dense enough that 10 m of depth adds about 101 kPa, roughly one +additional atmosphere, so submergence is a strong lever on the available margin. +The relevant depth is the *shallowest* point the blade reaches, so the binding +condition is the blade tip at the top of its rotation. -**The criterion collapses to a rotor speed limit.** Since the relative velocity at -the tip is dominated by :math:`\Omega R`, substituting +The criterion also collapses to a rotor speed limit. +The relative velocity at the tip is dominated by :math:`\Omega R`, so substituting :math:`W^2 = (\Omega R)^2 + v^2` and solving for :math:`\Omega` gives a maximum usable rotor speed: @@ -481,9 +487,10 @@ usable rotor speed: \Omega_{cav} = \frac{1}{R}\sqrt{\frac{2\left(p_{atm} + \rho g h_{tip} - p_{vap}\right)}{\rho\,\sigma_v} - v^2} where :math:`\sigma_v = -C_{p,min}` for the outboard blade sections and -:math:`h_{tip}` is the tip depth at the top of its rotation. Note that neither the -power curve nor the choice of over/underspeed branch appears in this expression: -**cavitation constrains rotor speed directly**, and any operating schedule that +:math:`h_{tip}` is the tip depth at the top of its rotation. +Neither the power curve nor the choice of over/underspeed branch appears in this +expression. +Cavitation constrains rotor speed directly, and any operating schedule that exceeds :math:`\Omega_{cav}` is affected regardless of how it was generated. The ROSCO toolbox evaluates this limit and warns if the generated speed schedule @@ -509,10 +516,10 @@ overspeed range than shallow, high-tip-speed rotors. For the RM1 at its nominal 24 m hub depth, :math:`\Omega_{cav}` is approximately 2.26 rad/s, or about 1.9x rated rotor speed, whereas the constant-power overspeed -schedule requires roughly 5.7x rated speed at cut-out. Overspeed is therefore not -available for this rotor, and the underspeed configurations (Examples 2 and 3) are -the applicable ones. This is a property of the RM1 and its submergence, not a -general result. +schedule requires roughly 5.7x rated speed at cut-out. +Overspeed is therefore not available for this rotor, and the underspeed +configurations (Examples 2 and 3) are the applicable ones. +This is a property of the RM1 and its submergence, not a general result. .. note:: For a floating MHK turbine (:code:`MHK = 2`), the hub depth is not constant: @@ -521,13 +528,14 @@ general result. optimistic at the shallow end of that excursion, so the limit should be evaluated at the shallowest expected submergence rather than the mean. -The expression above is a tip, attached-flow estimate. It is well suited to the -high-TSR overspeed regime, where the outboard sections operate near zero lift and -:math:`-C_{p,min}` is close to its minimum value. It is optimistic for deeply -stalled underspeed setpoints, whose larger :math:`-C_{p,min}` it does not capture, -though those setpoints have much lower relative velocity and correspondingly large -margin. A schedule that is marginal against this estimate should be verified with -an AeroDyn cavitation check (:code:`CavitCheck = True`), which evaluates the full +The expression above is a tip, attached-flow estimate. +It suits the high-TSR overspeed regime, where the outboard sections operate near +zero lift and :math:`-C_{p,min}` is close to its minimum value. +It is optimistic for deeply stalled underspeed setpoints, whose larger +:math:`-C_{p,min}` it does not capture, though those setpoints have much lower +relative velocity and correspondingly large margin. +A schedule that is marginal against this estimate should be verified with an +AeroDyn cavitation check (:code:`CavitCheck = True`), which evaluates the full criterion at every blade node using the angle-of-attack-dependent :math:`C_{p,min}` from the polars. @@ -576,32 +584,38 @@ will generate DISCON inputs to ROSCO. The following constraints apply when FBP control is enabled: * :code:`PC_ControlMode` and :code:`VS_ConstPower` must both be 0 whenever - :code:`VS_FBP > 0`: blade pitch is fixed to fine pitch, so constant-power - torque control may not run concurrently. :code:`PRC_Mode` must not be 1, since - its speed setpoint lookup would override the FBP speed reference. + :code:`VS_FBP > 0`. + Blade pitch is fixed to fine pitch, so constant-power torque control may not run + concurrently. + :code:`PRC_Mode` must not be 1, since its speed setpoint lookup would override + the FBP speed reference. ROSCO aborts on any of these at runtime, and the toolbox raises an exception rather than write a configuration that cannot run. -* :code:`PRC_Mode = 2` is allowed with FBP control: :code:`R_Torque` de-rates the - generator torque limit (:code:`VS_MaxTq`) and the Region 3 constant-power - torque, which is how :code:`Examples/31_fixed_pitch_mhk.py` ramps the rating in - for a soft start up. :code:`R_Pitch` has no effect, since the blade pitch is fixed. +* :code:`PRC_Mode = 2` is allowed with FBP control. + :code:`R_Torque` de-rates the generator torque limit (:code:`VS_MaxTq`) and the + Region 3 constant-power torque, which is how + :code:`Examples/31_fixed_pitch_mhk.py` ramps the rating in for a soft start up. + :code:`R_Pitch` has no effect, since the blade pitch is fixed. * :code:`VS_FBP = 1` is the fixed control law :math:`\tau = \min(P_{rated}/\Omega_g,\ K\Omega_g^2)`, which is inherently - constant-power and inherently overspeed. ROSCO ignores - :code:`VS_FBP_speed_mode`, :code:`VS_FBP_power_mode`, and :code:`VS_FBP_P` in - this mode, so the toolbox overrides them to the constant-power overspeed - schedule and warns if they were set otherwise. The generated operating - schedule is still used, both to seed the initial generator torque and to tune - the torque gains, so it must match the law ROSCO will actually run. + constant-power and inherently overspeed. + ROSCO ignores :code:`VS_FBP_speed_mode`, :code:`VS_FBP_power_mode`, and + :code:`VS_FBP_P` in this mode, so the toolbox overrides them to the + constant-power overspeed schedule and warns if they were set otherwise. + The generated operating schedule is still used, both to seed the initial + generator torque and to tune the torque gains, so it must match the law ROSCO + will actually run. * For :code:`VS_FBP = 3` (torque-lookup reference tracking), the generator torque schedule must be strictly increasing, since ROSCO inverts it to look up the - speed reference. The toolbox checks the computed schedule and raises an - exception otherwise. A nondecreasing power curve on the *underspeed* branch + speed reference. + The toolbox checks the computed schedule and raises an exception otherwise. + A nondecreasing power curve on the *underspeed* branch (:code:`VS_FBP_speed_mode = 0`) is sufficient to guarantee this, because generator speed falls with flow speed there, so :math:`\tau = P/\Omega_g` rises - on both counts. Overspeed generally is not sufficient: generator speed rises - with flow speed, so the torque falls unless the power curve rises faster than - the speed, which in practice only holds near the MPPT curve. + on both counts. + Overspeed generally is not sufficient: generator speed rises with flow speed, so + the torque falls unless the power curve rises faster than the speed, which in + practice only holds near the MPPT curve. .. _cavitation_warning: @@ -615,18 +629,20 @@ controller: * For MHK turbines (:code:`MHK > 0`), if the rotor speed schedule exceeds the estimated tip cavitation limit :math:`\Omega_{cav}` from :ref:`speed_limits_cavitation`, the toolbox warns and reports the worst - offending operating point. The limit is computed from the water density, - atmospheric and vapour pressures, and hub submergence in the OpenFAST model, - together with :math:`-C_{p,min}` taken from the outboard AeroDyn polars. The - check is skipped if the polars carry no :math:`C_{p,min}` column + offending operating point. + The limit is computed from the water density, atmospheric and vapour pressures, + and hub submergence in the OpenFAST model, together with :math:`-C_{p,min}` + taken from the outboard AeroDyn polars. + The check is skipped if the polars carry no :math:`C_{p,min}` column (:code:`InCol_Cpmin = 0`). -Note that the torque check alone will not catch an overspeed schedule: overspeed -*reduces* generator torque while raising speed, so an overspeed configuration can -sit far above the cavitation limit while the torque schedule stays well within -bounds. The two checks are complementary. +The torque check alone will not catch an overspeed schedule. +Overspeed *reduces* generator torque while raising speed, so an overspeed +configuration can sit far above the cavitation limit while the torque schedule +stays well within bounds. +The two checks are complementary. -The Region-2 torque control mode should be chosen to match the Region-3 FBP mode, +The Region 2 torque control mode should be chosen to match the Region 3 FBP mode, so that the two controllers hand off consistently through the transition region: .. list-table:: @@ -646,9 +662,9 @@ Note that the ROSCO input schema (:ref:`rt_tuning_yaml`) contains the latest inp The three case studies verified below are provided as worked examples in :code:`Examples/31_fixed_pitch_mhk.py`, which tunes against the RM1 marine turbine -tuning case :code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml`. That script also ships three -experimental configurations covering the rest of the input space; see -:ref:`fbp_experimental_configs`. +tuning case :code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml`. +That script also ships three experimental configurations covering the rest of the +input space; see :ref:`fbp_experimental_configs`. ROSCO Implementation @@ -703,10 +719,11 @@ test case are shown in the following figure. :align: center :width: 90% -Each example controller is then simulated with the RM1 marine turbine model -using OpenFAST in both steady and turbulent inflow. The steady-state performance -of each controller is compared to the operating schedules generated by the ROSCO -toolbox. The turbulent inflow uses the intensity shown in the following figure. +Each example controller is then simulated with the RM1 marine turbine model using +OpenFAST in both steady and turbulent inflow. +The steady state performance of each controller is compared to the operating +schedules generated by the ROSCO toolbox. +The turbulent inflow uses the intensity shown in the following figure. .. _turb_intensity: .. figure:: /images/mhk/25_turb_intensity.png @@ -714,14 +731,15 @@ toolbox. The turbulent inflow uses the intensity shown in the following figure. :width: 90% Tidal flow design load cases differ from their wind counterparts in ways that -matter for controller verification. Turbulence characteristics are not those -typically encountered in atmospheric flow, the mean probability distribution is -more closely isolated by environmental condition, and turbulence intensity varies -heavily with site conditions, with the better tidal sites generally lower in -turbulence intensity than wind sites [Milne2013]_. The turbulence intensity and -wave statistics used here were varied to mimic buoy data collected in the Orkney -Islands, Scotland. Design requirements for tidal current converters are given in -[IEC62600-2]_. +matter for controller verification. +Turbulence characteristics are not those typically encountered in atmospheric +flow, the mean probability distribution is more closely isolated by environmental +condition, and turbulence intensity varies heavily with site conditions. +The better tidal sites are generally lower in turbulence intensity than wind sites +[Milne2013]_. +The turbulence intensity and wave statistics used here were varied to mimic buoy +data collected in the Orkney Islands, Scotland. +Design requirements for tidal current converters are given in [IEC62600-2]_. .. TODO(DS): please supply the citation or dataset identifier for the Orkney buoy data so it can be referenced properly. @@ -731,10 +749,12 @@ Example 1 ^^^^^^^^^ The first example test case uses the naturally stable nonlinear feedback control -law. This controller is confined to operating in the constant power, overspeed -configuration. The explicit (non-reference-tracking) control law is analogous to -the :math:`k\Omega^2` control law sometimes used in Region 2 for wind and marine -turbines [Johnson2006]_, and is best paired with a :math:`k\Omega^2` Region-2 +law. +This controller is confined to operating in the constant power, overspeed +configuration. +The explicit (non-reference-tracking) control law is analogous to the +:math:`k\Omega^2` control law sometimes used in Region 2 for wind and marine +turbines [Johnson2006]_, and is best paired with a :math:`k\Omega^2` Region 2 controller (:code:`VS_ControlMode = 1`). It is easy to design and requires no tuning, but is rigid and inflexible, and its overspeed operating points carry high tip speed and blade thrust. @@ -779,10 +799,10 @@ high maximum torque signal (see :code:`max_torque_factor` in the tuning yaml). Sizing the torque limit is a safety consideration, not only a tracking one. Every underspeed setpoint sits where hydrodynamic torque *increases* with rotor speed, so if the commanded torque saturates below what the inflow demands, the - rotor accelerates rather than settling. Because the aerodynamic torque curve - only turns over past :math:`TSR_{opt}`, the nearest stable equilibrium is on - the *overspeed* side of the :math:`C_p` peak. Insufficient torque authority on - an underspeed schedule therefore does not stall the rotor to a stop: it + rotor accelerates rather than settling. The aerodynamic torque curve only turns + over past :math:`TSR_{opt}`, so the nearest stable equilibrium is on the + *overspeed* side of the :math:`C_p` peak. Insufficient torque authority on an + underspeed schedule therefore does not stall the rotor to a stop: the rotor accelerates through the :math:`C_p` peak and settles at high speed, in the high-thrust and cavitation-prone regime that the underspeed schedule was chosen to avoid. The same applies to any fault that removes generator torque. Size @@ -817,10 +837,10 @@ The third example test case uses underspeed operation with a wind speed estimato * Region-3 FBP mode: reference tracking with WSE lookup (:code:`VS_FBP = 2`) * The power curve can be completely arbitrarily specified -This is the most general configuration and the easiest to combine with a Region-2 -controller or with alternate references, but it is less robust to errors in the -tuning model than the torque lookup, and shares the torque lookup's need for an -aggressive controller and high torque headroom. +This is the most general configuration and the easiest to combine with a Region 2 +controller or with alternate references. +It is less robust to errors in the tuning model than the torque lookup, and shares +the torque lookup's need for an aggressive controller and high torque headroom. .. _case3_P_wg_tg_ss: .. figure:: /images/mhk/24_case3_P_wg_tg_ss.png @@ -844,8 +864,8 @@ aggressive controller and high torque headroom. Additional Configurations ^^^^^^^^^^^^^^^^^^^^^^^^^ -:code:`Examples/31_fixed_pitch_mhk.py` ships three further configurations beyond the -three examples above: +:code:`Examples/31_fixed_pitch_mhk.py` ships three further configurations beyond +the three examples above: * Constant power underspeed: the Example 2 configuration (:code:`VS_FBP = 3`, :code:`VS_ControlMode = 4`) with the power curve held flat at @@ -856,14 +876,17 @@ three examples above: * Linear increasing power, leveling out: the Example 2 power curve reaching 2x rated below cut-out and flat thereafter. -These are experimental. They are included to demonstrate the range of power curves and -mode pairings the toolbox accepts, and have not been through the steady and turbulent -verification campaign that Examples 1 through 3 have. Treat their operating schedules as -illustrative rather than validated. +These are experimental. +They demonstrate the range of power curves and mode pairings the toolbox accepts, +and have not been through the steady and turbulent verification campaign that +Examples 1 through 3 have. +Their operating schedules should be treated as illustrative rather than validated. -The cavitation caveat attached to Example 1 applies to the overspeed reference-tracking -configuration as well: for the RM1 rotor, both exceed the tip cavitation limit of -:ref:`speed_limits_cavitation`, and the toolbox warns accordingly when they are tuned. +The cavitation caveat attached to Example 1 applies to the overspeed +reference-tracking configuration as well. +For the RM1 rotor, both exceed the tip cavitation limit of +:ref:`speed_limits_cavitation`, and the toolbox warns accordingly when they are +tuned. Recommendations @@ -874,24 +897,24 @@ In certain applications, the ability to follow a generic power curve with a limited actuation capability is more advantageous than using variable-blade-pitch (VBP) control. VBP control allows constant-power operation in Region 3 matched with constant -speed and torque for a flat operating schedule. Pitch-actuated turbines also -experience smaller blade loads in Region 3. The FBP approach satisfies -applications in which the cost and complexity of the actuators themselves are -prohibitive. +speed and torque for a flat operating schedule, and pitch-actuated turbines also +experience smaller blade loads in Region 3. +The FBP approach satisfies applications in which the cost and complexity of the +actuators themselves are prohibitive. Generic user input allows flexibility for variety of applications. FBP controller implementation in ROSCO with auto-tuning and automatic generation of operating schedule to follow power curve. -Because the Region-2 and Region-3 controllers utilize the same actuator, the +Because the Region 2 and Region 3 controllers utilize the same actuator, the transition region is markedly different than what is required for a VBP -Region-3 controller. +Region 3 controller. Future Work ----------------------- -* Enhanced multi-constraint configuration, including constraining Region-3 rotor +* Enhanced multi-constraint configuration, including constraining Region 3 rotor thrust to a constant level rather than only constraining power. * Explicit power reference tracking. From 02f9513ec6542f0163099185aaa1c7822d5ab8a1 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 18 Sep 2026 13:22:23 -0600 Subject: [PATCH 15/21] Clarify 2- vs. 3-bladed remarks --- docs/source/marine_hydro.rst | 60 ++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index d87dbd85c..3b55581f3 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -122,21 +122,52 @@ Pitch Bandwidth and Filtering The RM1 is tuned at :code:`omega_pc` = 0.35 rad/s with :code:`zeta_pc` = 1.0, against 0.137 rad/s and 2.0 for the 2.8 MW wind turbine, so the pitch loop is roughly 2.5x faster. -Rated rotor speed is nearly the same for both machines (1.20 versus 1.27 rad/s). -The separation between the pitch control bandwidth and 1P therefore falls from -about a factor of nine to about a factor of three. -The RM1 is also a two-bladed rotor, so 1P and 2P both appear directly in the -generator speed measurement, without the averaging a three-bladed rotor provides. +Rated rotor speed is nearly the same for both machines (1.20 versus 1.27 rad/s), +so the faster loop sits closer to the rotor-periodic disturbances. How much +closer depends on the blade count. + +Summing the per-blade loads of a :math:`B`-bladed rotor cancels every harmonic +except multiples of :math:`B\Omega`, so only those reach rotor torque and, in +turn, the generator speed the pitch controller acts on. A three-bladed rotor +passes 3P, 6P, ...; a two-bladed rotor passes 2P, 4P, .... Both rotors average, +but a two-bladed rotor begins averaging one harmonic lower, and 1P survives on +neither except through blade-to-blade imbalance. The relevant comparison is each +machine's lowest surviving harmonic: -The MHK tuning case therefore enables filtering that the wind cases leave off. -Two notch filters are placed at 1.0 and 2.42 rad/s (1P and 2P) and applied to both -the generator speed and the tower-top measurements: +.. list-table:: + :header-rows: 1 + :widths: auto + + * - + - NREL 2.8 MW (wind) + - RM1 (MHK) + * - Blades + - 3 + - 2 + * - Lowest surviving harmonic + - 3P = 3.80 rad/s + - 2P = 2.41 rad/s + * - Separation from :code:`omega_pc` + - 28x + - 7x + +The margin is roughly four times tighter, from the combination of a faster loop +and a lower first harmonic. This advantage belongs to the blade count only at a +given rotor speed: a three-bladed rotor of the same diameter typically runs +slower, which moves 3P back down. + +Two blades carry a second penalty. The in-plane inertia of a three-bladed rotor is +isotropic about the hub, while a two-bladed rotor's varies with azimuth, so even a +perfectly balanced two-bladed rotor couples to the support structure at 2P through +rotor anisotropy alone. + +The MHK tuning case therefore enables filtering that the wind cases leave off: .. code-block:: yaml DISCON: F_NumNotchFilts: 2 - F_NotchFreqs: [1.0, 2.42] # 1P, 2P + F_NotchFreqs: [1.0, 2.42] # 2.42 rad/s is 2P F_NotchBetaNum: [0.0, 0.0] F_NotchBetaDen: [0.25, 0.25] F_GenSpdNotch_N: 2 @@ -144,6 +175,17 @@ the generator speed and the tower-top measurements: F_TwrTopNotch_N: 2 F_TwrTopNotch_Ind: [1, 2] +Both notches are applied twice: :code:`F_GenSpdNotch_Ind` filters the generator +speed used by the pitch and torque loops, and :code:`F_TwrTopNotch_Ind` filters +the nacelle fore-aft IMU acceleration used by the floating feedback term. + +.. TODO(DS): the second notch at 2.42 rad/s matches 2P at rated speed + (2 x 1.204 = 2.408). The first, at 1.0 rad/s, does not correspond to 1P + (1.204 rad/s), the platform frequency (0.4 rad/s), or the tower frequency + (3.34 rad/s), and with ``F_NotchBetaDen`` = 0.25 the notch is narrow enough + that the 17% offset from 1P is unlikely to be incidental. Please confirm what + this notch targets so it can be documented. + The low-pass corner frequency moves the other way. :code:`F_LPFCornerFreq` is derived from the blade edgewise frequency, and hydrokinetic blades are short and stiff: 60.3 rad/s for the RM1 against 8.3 rad/s From 4b9856b474969628bce98a9ad5e05f8d491607e8 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 18 Sep 2026 14:04:36 -0600 Subject: [PATCH 16/21] Fix and document v_rated change for FBP --- Examples/Tune_Cases/RM1_MHK_FBP.yaml | 4 +- docs/source/marine_hydro.rst | 66 +++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/Examples/Tune_Cases/RM1_MHK_FBP.yaml b/Examples/Tune_Cases/RM1_MHK_FBP.yaml index a24569e11..4a688a068 100644 --- a/Examples/Tune_Cases/RM1_MHK_FBP.yaml +++ b/Examples/Tune_Cases/RM1_MHK_FBP.yaml @@ -15,7 +15,9 @@ turbine_params: rotor_inertia: 92169 # Rotor inertia [kg m^2], {Available in Elastodyn .sum file} rated_rotor_speed: 1.204 # 11.5 rpm # Rated rotor speed [rad/s] v_min: 0.5 # Cut-in wind speed [m/s] - v_rated: 2.0 # Rated wind speed [m/s] + # Rated SPEED point, not rated power: rotor hits 1.204 rad/s at TSR 7, and ROSCO starts the + # FBP lookup here. Does not cap speed; see the MHK docs on the rated transition. + v_rated: 1.72 v_max: 4.0 # Cut-out wind speed [m/s], -- Does not need to be exact (JUST ASSUME FOR NOW) max_pitch_rate: 0.1745 # Maximum blade pitch rate [rad/s] max_torque_rate: 1500000. # Maximum torque rate [Nm/s], {~1/4 VS_RtTq/s} diff --git a/docs/source/marine_hydro.rst b/docs/source/marine_hydro.rst index 3b55581f3..2015ec492 100644 --- a/docs/source/marine_hydro.rst +++ b/docs/source/marine_hydro.rst @@ -263,9 +263,11 @@ Over/Underspeed Reference Setpoints for Fixed-Pitch Control The steady state generator-speed setpoints are determined by the :math:`C_p` contours intersecting with the fine-pitch line. -For the RM1, overspeed reaches up to 3x rated speed. +For the RM1, the constant-power overspeed schedule reaches roughly 5.7x rated +rotor speed at cut-out. How much of that range a given rotor can use is limited by cavitation, discussed -in :ref:`speed_limits_cavitation`; on the RM1 the limit binds well before 3x. +in :ref:`speed_limits_cavitation`; on the RM1 the limit binds at about 1.9x, well +before the schedule gets there. Rotor speed also has consequences for blade loads (high thrust). .. _cp_wg_sched: @@ -273,6 +275,66 @@ Rotor speed also has consequences for blade loads (high thrust). :align: center :width: 90% +.. _fbp_rated_speed_overshoot: + +Rotor Speed Through the Rated Transition +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Fixed-pitch control has no rated-speed saturation. +A variable-pitch controller holds the rotor at :math:`\Omega_{rated}` through +Region 2.5, letting the TSR slide below :math:`TSR_{operational}` as the flow +speed rises to rated; with the blade pitch fixed there is no second actuator to +do this, so the schedule stays on the :math:`TSR_{operational}` line until the +Region 3 power curve binds. + +Whenever the rotor reaches rated speed before the power curve binds, the schedule +therefore overshoots rated rotor speed in the band between the two. +For the RM1, the rotor reaches its rated 1.204 rad/s (11.5 rpm) at 1.72 m/s while +running at :math:`TSR_{operational} = 7`, but MPPT does not reach the 500 kW +rating until 2.0 m/s. +Across that band the schedule continues along the TSR line and peaks at +1.364 rad/s, or 13.0 rpm, about 1.13x rated, before the constant-power curve pulls +it back down. + +This is a deliberate trade in the shipped RM1 configuration, not an oversight: + +* The overshoot is well inside the cavitation limit of 2.26 rad/s + (:ref:`speed_limits_cavitation`), and generator torque within that band peaks at + about 6.8 kNm against a rated 8.3 kNm, so it is purely a question of drivetrain + and generator speed rating. + Verify that the machine tolerates a 13% speed excursion near rated before + reusing this configuration. +* The alternative is to set :code:`TSR_operational` to the design TSR rather than + the :math:`C_p`-maximizing one. + The RM1 rates at :math:`TSR = 6.02` (1.204 rad/s at 2.0 m/s), and pinning + :code:`TSR_operational` there removes the overshoot entirely, at the cost of + about 2.5% in below-rated :math:`C_p` (0.4358 against 0.4471). + The variable-pitch RM1 case reaches this same operating point on its own, + through rated-speed saturation. + +Note that :code:`v_rated` means different things to the two control paths, so the +two RM1 tuning cases set it differently. +For variable pitch it is the rated *power* speed, which is where pitch regulation +and the pitch gain schedule begin, and +:code:`Examples/Tune_Cases/RM1_MHK.yaml` sets 2.0 m/s. +For fixed pitch, power regulation is set by :code:`VS_FBP_U` and +:code:`VS_FBP_P` instead, so :code:`v_rated` is the rated *speed* point: the flow +speed at which the rotor reaches :code:`rated_rotor_speed` while tracking +:code:`TSR_operational`. +:code:`Examples/Tune_Cases/RM1_MHK_FBP.yaml` sets 1.72 m/s, which makes it agree +with :code:`VS_RefSpd`, the speed at which ROSCO begins using the FBP lookup +table. + +Setting :code:`v_rated` here does not cap the rotor speed, which is the easy +misreading. +It is where the toolbox stops holding the TSR at :code:`TSR_operational` and +begins solving for it from the :math:`C_p` inversion, and where ROSCO switches to +the lookup table. +Until the power curve binds, that solved TSR is still :code:`TSR_operational`, so +the schedule continues up the same line and the rotor keeps accelerating past +rated speed. +The lookup table contains that overshoot, and ROSCO tracks it deliberately. + Torque setpoints (:math:`\bar{\tau}`) determined by constant-power relationship :math:`\bar{\tau} = \frac{P_{rated}}{{\bar{\omega}}}`, where :math:`P_{rated}` is the rated power and :math:`\bar{\omega}` is the steady state generator speed. .. _cp_tg_sched: From e38b9866c0820cab29008ccf11332d7f90203b4d Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 18 Sep 2026 15:41:37 -0600 Subject: [PATCH 17/21] Fix Cp_op in below rated FBP --- rosco/toolbox/controller.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rosco/toolbox/controller.py b/rosco/toolbox/controller.py index 8a9aeab19..0815d70f5 100644 --- a/rosco/toolbox/controller.py +++ b/rosco/toolbox/controller.py @@ -353,8 +353,6 @@ def tune_controller(self, turbine): P_op = np.min([P_user_defined, P_max], axis=0) # Operation along Cp surface (with fixed pitch) Cp_op = (P_op / P_max) * Cp_operational - Cp_op_br = Cp_op[:len(v_below_rated)] - Cp_op_ar = Cp_op[len(v_below_rated):] # Identify TSR matching the Cp values (similar to variable pitch angle interpolation below) Cp_FBP = np.ndarray.flatten(turbine.Cp.interp_surface(self.min_pitch, turbine.TSR_initial)) # all Cp values for fine blade pitch @@ -370,7 +368,16 @@ def tune_controller(self, turbine): f_cp_TSR = interpolate.interp1d(Cp_FBP[:Cp_maxidx+1], turbine.TSR_initial[:Cp_maxidx+1]) # interpolate function for Cp(tsr) values TSR_op = f_cp_TSR(Cp_op) # Defer to operational TSR for below rated, even if other optimum (should keep Cp the same, but may lead to discontinuities in operating schedule if min_pitch is not optimal) - TSR_op[v < turbine.v_rated] = turbine.TSR_operational + below_rated = v < turbine.v_rated + TSR_op[below_rated] = turbine.TSR_operational + # FBP only engages once the Region 2 speed reference exceeds rated, so below rated the + # turbine tracks TSR_operational regardless of what the power curve asks for. Hold Cp on + # the surface at that TSR: otherwise the (Cp, TSR) pair is not an equilibrium and the + # resulting speed and torque setpoints are not reachable. + if np.any(P_user_defined[below_rated] < P_max[below_rated] * (1 - 1e-6)): + print('WARNING: FBP control cannot curtail below rated. The power curve is ignored ' + 'below v_rated, where the turbine tracks TSR_operational.') + Cp_op[below_rated] = Cp_operational TSR_below_rated = TSR_op[:len(v_below_rated)] # Should be constant at TSR_operational TSR_above_rated = TSR_op[len(v_below_rated):] From cfab32ae958b7b4e6604e0b16c095fb0485a7375 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 18 Sep 2026 15:44:26 -0600 Subject: [PATCH 18/21] Relocate min rotor speed limits to before they are used in all cases --- rosco/toolbox/controller.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rosco/toolbox/controller.py b/rosco/toolbox/controller.py index 0815d70f5..5951243b0 100644 --- a/rosco/toolbox/controller.py +++ b/rosco/toolbox/controller.py @@ -328,6 +328,13 @@ def tune_controller(self, turbine): self.min_pitch = turbine.Cp.pitch_opt turbine.min_pitch = self.min_pitch + # minimum rotor speed saturation limits, set before the operating schedule saturates against them + if self.vs_minspd: + self.vs_minspd = np.maximum(self.vs_minspd, (turbine.TSR_operational * turbine.v_min / turbine.rotor_radius)) + else: + self.vs_minspd = (turbine.TSR_operational * turbine.v_min / turbine.rotor_radius) + self.pc_minspd = self.vs_minspd + # -------------Define Operation Points ------------- # TSR_rated = rated_rotor_speed*R/turbine.v_rated # TSR at rated @@ -544,13 +551,6 @@ def tune_controller(self, turbine): self.vs_refspd = min(turbine.TSR_operational * turbine.v_rated/R, turbine.rated_rotor_speed) * Ng # -- Define some setpoints -- - # minimum rotor speed saturation limits - if self.vs_minspd: - self.vs_minspd = np.maximum(self.vs_minspd, (turbine.TSR_operational * turbine.v_min / turbine.rotor_radius)) - else: - self.vs_minspd = (turbine.TSR_operational * turbine.v_min / turbine.rotor_radius) - self.pc_minspd = self.vs_minspd - # Set IPC ramp inputs if not already defined if max(self.IPC_Vramp) == 0.0: self.IPC_Vramp = [turbine.v_rated*0.8, turbine.v_rated] From e851370ce133c2f5cf2071571d8312e64f731f6f Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 21 Sep 2026 11:26:34 -0600 Subject: [PATCH 19/21] Fix comment, FULL_TEST --- Examples/31_fixed_pitch_mhk.py | 2 +- rosco/toolbox/ofTools/case_gen/CaseLibrary.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index 457f7867d..20304fd54 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -68,7 +68,7 @@ def main(): - FULL_TEST = True # Run a full test locally (True) or a shorter one for CI + FULL_TEST = False # Run a full test locally (True) or a shorter one for CI # Input yaml and output directory parameter_filename = os.path.join(this_dir, 'Tune_Cases/RM1_MHK_FBP.yaml') diff --git a/rosco/toolbox/ofTools/case_gen/CaseLibrary.py b/rosco/toolbox/ofTools/case_gen/CaseLibrary.py index 2b59aeccf..8c74169ac 100644 --- a/rosco/toolbox/ofTools/case_gen/CaseLibrary.py +++ b/rosco/toolbox/ofTools/case_gen/CaseLibrary.py @@ -579,7 +579,7 @@ def sweep_yaml_input(start_group, **control_sweep_opts): base_params = control_sweep_opts['controller_params'] if 'param_sweeps' in control_sweep_opts: - # ponytail: shallow merge, a nested override (e.g. DISCON) replaces the whole sub-dict + # NOTE: shallow merge, a nested override (e.g. DISCON) replaces the whole sub-dict param_sweeps = [{**base_params, **overrides} for overrides in control_sweep_opts['param_sweeps']] else: param_sweeps = [] From 570c64d91dfb794fbfe6cad1489fc815603d8355 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 21 Sep 2026 11:26:58 -0600 Subject: [PATCH 20/21] Fix output name --- Examples/31_fixed_pitch_mhk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/31_fixed_pitch_mhk.py b/Examples/31_fixed_pitch_mhk.py index 20304fd54..ad77eb59a 100644 --- a/Examples/31_fixed_pitch_mhk.py +++ b/Examples/31_fixed_pitch_mhk.py @@ -176,7 +176,7 @@ def main(): plt.savefig(fig_fname,bbox_inches='tight',) # Simulate all control configurations, in parallel - run_dir = os.path.join(example_out_dir, '31_MHK_2_reorder') + run_dir = os.path.join(example_out_dir, '31_MHK') os.makedirs(run_dir,exist_ok=True) # simulation set up From 8d95127d2a1f46bcb9c51e302bef60098f7ac657 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 21 Sep 2026 15:34:42 -0600 Subject: [PATCH 21/21] Try skipping the compiler on macoc-14 --- .github/workflows/Publish_ROSCO.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/Publish_ROSCO.yml b/.github/workflows/Publish_ROSCO.yml index ef017d940..697a50c73 100644 --- a/.github/workflows/Publish_ROSCO.yml +++ b/.github/workflows/Publish_ROSCO.yml @@ -25,8 +25,10 @@ jobs: if: false == contains( matrix.os, 'windows') uses: fortran-lang/setup-fortran@v1 + # macOS runners already ship Apple clang; installing llvm via Homebrew + # builds from source on untiered images (e.g. macos-14) and breaks. - name: Install compiler - if: false == contains( matrix.os, 'windows') + if: contains( matrix.os, 'ubuntu') id: install_cc uses: rlalik/setup-cpp-compiler@master with: @@ -68,16 +70,16 @@ jobs: if: contains( matrix.os, 'macos-14') uses: pypa/cibuildwheel@v2.23.3 env: - CC: ${{ steps.install_cc.outputs.cc }} - CXX: ${{ steps.install_cc.outputs.cxx }} + CC: clang + CXX: clang++ CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET="14.0" - name: Build wheels mac-15 if: contains( matrix.os, 'macos-15') uses: pypa/cibuildwheel@v2.23.3 env: - CC: ${{ steps.install_cc.outputs.cc }} - CXX: ${{ steps.install_cc.outputs.cxx }} + CC: clang + CXX: clang++ CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET="15.0" - name: Build wheels windows