From b4486b7ef1ce0180e7f3d7848d4e5f2e25be40c6 Mon Sep 17 00:00:00 2001 From: ptrbortolotti Date: Wed, 3 Jun 2026 10:23:30 -0600 Subject: [PATCH 1/8] fix drivetrain connections --- weis/glue_code/glue_code.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/weis/glue_code/glue_code.py b/weis/glue_code/glue_code.py index c8b8776ae..11418ee69 100644 --- a/weis/glue_code/glue_code.py +++ b/weis/glue_code/glue_code.py @@ -758,12 +758,6 @@ def setup(self): self.connect("drivetrain.mb2Type", "drivese_post.bear2.bearing_type") self.connect("drivetrain.lss_diameter", "drivese_post.lss_diameter") self.connect("drivetrain.lss_wall_thickness", "drivese_post.lss_wall_thickness") - if modeling_options["WISDEM"]["DriveSE"]["direct"]: - self.connect("drivetrain.nose_diameter", "drivese_post.bear1.D_shaft", src_indices=[0]) - self.connect("drivetrain.nose_diameter", "drivese_post.bear2.D_shaft", src_indices=[-1]) - else: - self.connect("drivetrain.lss_diameter", "drivese_post.bear1.D_shaft", src_indices=[0]) - self.connect("drivetrain.lss_diameter", "drivese_post.bear2.D_shaft", src_indices=[-1]) self.connect("drivetrain.uptower", "drivese_post.uptower") self.connect("drivetrain.brake_mass_user", "drivese_post.brake_mass_user") self.connect("drivetrain.hvac_mass_coeff", "drivese_post.hvac_mass_coeff") From 4875145d50affff818d0ddf66f027d2ca03236bf Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Mon, 15 Jun 2026 09:07:41 -0600 Subject: [PATCH 2/8] make test solver COBYLA from scipy for most guaranteed omdao support --- weis/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weis/main.py b/weis/main.py index fb621c59c..d658fd818 100644 --- a/weis/main.py +++ b/weis/main.py @@ -83,7 +83,7 @@ def set_modopt_test_runs(fname_input_modeling, fname_input_analysis, modeling_ov multi_objective_solvers = ['NSGA2'] current_solver = analysis_options.get('driver', {}).get('optimization', {}).get('solver', '') if current_solver not in multi_objective_solvers: - test_analysis_overrides['driver']['optimization']['solver'] = 'LN_COBYLA' # Gradient free + test_analysis_overrides['driver']['optimization']['solver'] = 'COBYLA' # Gradient free modeling_override = recursive_merge(modeling_override, test_modeling_overrides) analysis_override = recursive_merge(analysis_override, test_analysis_overrides) From abbe86ade4b85358739411e29cc9d9affd83f86a Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 17 Jun 2026 10:06:20 -0600 Subject: [PATCH 3/8] Make openmdao connections between tune rosco and openfast --- weis/aeroelasticse/openmdao_openfast.py | 9 ++++++--- weis/control/tune_rosco.py | 10 +++++++++- weis/glue_code/glue_code.py | 3 +++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/weis/aeroelasticse/openmdao_openfast.py b/weis/aeroelasticse/openmdao_openfast.py index dceb699d5..249f0e91e 100644 --- a/weis/aeroelasticse/openmdao_openfast.py +++ b/weis/aeroelasticse/openmdao_openfast.py @@ -222,6 +222,9 @@ def setup(self): # ServoDyn Inputs self.add_input('generator_efficiency', val=1.0, desc='Generator efficiency') self.add_input('max_pitch_rate', val=0.0, units='deg/s', desc='Maximum allowed blade pitch rate') + self.add_input('VS_RefSpd', val=0.0, units='rad/s', desc='Rated generator speed from ROSCO tuning') + self.add_input('VS_RtTq', val=0.0, units='N*m', desc='Rated generator torque from ROSCO tuning') + self.add_input('VS_Rgn2K', val=0.0, units='N*m/(rad/s)**2', desc='Region 2 torque constant from ROSCO tuning') # StC or TMD inputs; structural control and tuned mass dampers @@ -1112,9 +1115,9 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs): fst_vt['ServoDyn']['PitManRat(2)'] = float(inputs['max_pitch_rate'][0]) fst_vt['ServoDyn']['PitManRat(3)'] = float(inputs['max_pitch_rate'][0]) # Tune simple variable speed controller in ServoDyn, mostly to support free-free rotor configurations during linearization - fst_vt['ServoDyn']['VS_RtGnSp'] = fst_vt['DISCON_in']['VS_RefSpd'] * 30. / np.pi # rpm - fst_vt['ServoDyn']['VS_RtTq'] = fst_vt['DISCON_in']['VS_RtTq'] # Nm - fst_vt['ServoDyn']['VS_Rgn2K'] = fst_vt['DISCON_in']['VS_Rgn2K'] / (30./np.pi)**2. # N-m/rpm^2 + fst_vt['ServoDyn']['VS_RtGnSp'] = float(inputs['VS_RefSpd'][0]) * 30. / np.pi # rpm + fst_vt['ServoDyn']['VS_RtTq'] = float(inputs['VS_RtTq'][0]) # Nm + fst_vt['ServoDyn']['VS_Rgn2K'] = float(inputs['VS_Rgn2K'][0]) / (30./np.pi)**2. # N-m/rpm^2 # Prevent error in OpenFAST if fst_vt['ServoDyn']['VS_Rgn2K']*fst_vt['ServoDyn']['VS_RtGnSp']**2. > fst_vt['ServoDyn']['VS_RtTq']: fst_vt['ServoDyn']['VS_Rgn2K'] = fst_vt['ServoDyn']['VS_RtTq']/fst_vt['ServoDyn']['VS_RtGnSp']**2. diff --git a/weis/control/tune_rosco.py b/weis/control/tune_rosco.py index 74dc037df..afb157731 100644 --- a/weis/control/tune_rosco.py +++ b/weis/control/tune_rosco.py @@ -226,7 +226,10 @@ def setup(self): self.add_output('PC_GS_Ki', val=np.zeros(rosco_init_options['PC_GS_n']), desc='Gain-schedule table: pitch controller ki gains') self.add_output('Fl_Kp', val=0.0, desc='Floating feedback gain') - # self.add_output('VS_Rgn2K', val=0.0, units='N*m/(rad/s)**2', desc='Generator torque constant in Region 2 (HSS side), [N-m/(rad/s)^2]') + # ServoDyn parameters passed to FASTLoadCases via formal OpenMDAO connection + self.add_output('VS_RefSpd', val=0.0, units='rad/s', desc='Rated generator speed for ServoDyn') + self.add_output('VS_RtTq', val=0.0, units='N*m', desc='Rated generator torque for ServoDyn') + self.add_output('VS_Rgn2K', val=0.0, units='N*m/(rad/s)**2', desc='Generator torque constant in Region 2 (HSS side)') def compute(self,inputs,outputs, discrete_inputs, discrete_outputs): ''' @@ -426,6 +429,11 @@ def compute(self,inputs,outputs, discrete_inputs, discrete_outputs): # outputs['VS_Rgn2K'] = controller.vs_rgn2K outputs['VS_Kp'] = controller.vs_gain_schedule.Kp[0] outputs['VS_Ki'] = controller.vs_gain_schedule.Ki[0] + + # ServoDyn parameters for FASTLoadCases + outputs['VS_RefSpd'] = ROSCO_input['VS_RefSpd'] + outputs['VS_RtTq'] = ROSCO_input['VS_RtTq'] + outputs['VS_Rgn2K'] = ROSCO_input['VS_Rgn2K'] class Cp_Ct_Cq_Tables(ExplicitComponent): def initialize(self): diff --git a/weis/glue_code/glue_code.py b/weis/glue_code/glue_code.py index 11418ee69..d03f7fc51 100644 --- a/weis/glue_code/glue_code.py +++ b/weis/glue_code/glue_code.py @@ -496,6 +496,9 @@ def setup(self): self.connect("drivetrain.gear_ratio", "aeroelastic.gearbox_ratio") self.connect("rotorse.rp.powercurve.rated_efficiency", "aeroelastic.generator_efficiency") self.connect("tune_rosco_ivc.max_pitch_rate" , "aeroelastic.max_pitch_rate") + self.connect("sse_tune.tune_rosco.VS_RefSpd", "aeroelastic.VS_RefSpd") + self.connect("sse_tune.tune_rosco.VS_RtTq", "aeroelastic.VS_RtTq") + self.connect("sse_tune.tune_rosco.VS_Rgn2K", "aeroelastic.VS_Rgn2K") self.connect("drivetrain.gearbox_efficiency", "aeroelastic.gearbox_efficiency") self.connect("drivetrain.uptilt", "aeroelastic.tilt") self.connect("drivetrain.overhang", "aeroelastic.overhang") From 6de745735635da148fdb72ac9ded0d4c513c2158 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 17 Jun 2026 10:06:35 -0600 Subject: [PATCH 4/8] Change test solver back to LN_COBYLA --- weis/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weis/main.py b/weis/main.py index d658fd818..fb621c59c 100644 --- a/weis/main.py +++ b/weis/main.py @@ -83,7 +83,7 @@ def set_modopt_test_runs(fname_input_modeling, fname_input_analysis, modeling_ov multi_objective_solvers = ['NSGA2'] current_solver = analysis_options.get('driver', {}).get('optimization', {}).get('solver', '') if current_solver not in multi_objective_solvers: - test_analysis_overrides['driver']['optimization']['solver'] = 'COBYLA' # Gradient free + test_analysis_overrides['driver']['optimization']['solver'] = 'LN_COBYLA' # Gradient free modeling_override = recursive_merge(modeling_override, test_modeling_overrides) analysis_override = recursive_merge(analysis_override, test_analysis_overrides) From fd867f24cbc6597907a57f66fe2c19b20e880b2c Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 17 Jun 2026 10:09:26 -0600 Subject: [PATCH 5/8] Use LN_COBYLA, TEST_RUN in OC3 RAFT example --- .../04_frequency_domain_analysis_design/oc3_raft_analysis.yaml | 2 +- examples/04_frequency_domain_analysis_design/oc3_raft_driver.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/04_frequency_domain_analysis_design/oc3_raft_analysis.yaml b/examples/04_frequency_domain_analysis_design/oc3_raft_analysis.yaml index c66633e5e..fff790adf 100644 --- a/examples/04_frequency_domain_analysis_design/oc3_raft_analysis.yaml +++ b/examples/04_frequency_domain_analysis_design/oc3_raft_analysis.yaml @@ -39,7 +39,7 @@ driver: max_major_iter: 10 # Maximum number of major design iterations (SNOPT) max_minor_iter: 100 # Maximum number of minor design iterations (SNOPT) max_iter: 10 # Maximum number of iterations (SLSQP) - solver: SLSQP # Optimization solver. Other options are 'SLSQP' - 'CONMIN' + solver: LN_COBYLA # Optimization solver. Other options are 'SLSQP' - 'CONMIN' step_size: 1.e-3 # Step size for finite differencing form: central # Finite differencing mode, either forward or central diff --git a/examples/04_frequency_domain_analysis_design/oc3_raft_driver.py b/examples/04_frequency_domain_analysis_design/oc3_raft_driver.py index 967cc8290..d9c22e509 100644 --- a/examples/04_frequency_domain_analysis_design/oc3_raft_driver.py +++ b/examples/04_frequency_domain_analysis_design/oc3_raft_driver.py @@ -2,7 +2,7 @@ from weis import weis_main # TEST_RUN will reduce the number and duration of simulations -TEST_RUN = False +TEST_RUN = True ## File management run_dir = os.path.dirname( os.path.realpath(__file__) ) From 0bc1eb0c812155f53bc7caea43e745cf51523f61 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Wed, 17 Jun 2026 10:53:28 -0600 Subject: [PATCH 6/8] Try scipy COBYLA again --- weis/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weis/main.py b/weis/main.py index fb621c59c..d658fd818 100644 --- a/weis/main.py +++ b/weis/main.py @@ -83,7 +83,7 @@ def set_modopt_test_runs(fname_input_modeling, fname_input_analysis, modeling_ov multi_objective_solvers = ['NSGA2'] current_solver = analysis_options.get('driver', {}).get('optimization', {}).get('solver', '') if current_solver not in multi_objective_solvers: - test_analysis_overrides['driver']['optimization']['solver'] = 'LN_COBYLA' # Gradient free + test_analysis_overrides['driver']['optimization']['solver'] = 'COBYLA' # Gradient free modeling_override = recursive_merge(modeling_override, test_modeling_overrides) analysis_override = recursive_merge(analysis_override, test_analysis_overrides) From f5109688741aef7bfd9afce848e5880e0e930dcf Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 2 Jul 2026 13:33:00 -0600 Subject: [PATCH 7/8] Revert "Try scipy COBYLA again" This reverts commit 0bc1eb0c812155f53bc7caea43e745cf51523f61. --- weis/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weis/main.py b/weis/main.py index d658fd818..fb621c59c 100644 --- a/weis/main.py +++ b/weis/main.py @@ -83,7 +83,7 @@ def set_modopt_test_runs(fname_input_modeling, fname_input_analysis, modeling_ov multi_objective_solvers = ['NSGA2'] current_solver = analysis_options.get('driver', {}).get('optimization', {}).get('solver', '') if current_solver not in multi_objective_solvers: - test_analysis_overrides['driver']['optimization']['solver'] = 'COBYLA' # Gradient free + test_analysis_overrides['driver']['optimization']['solver'] = 'LN_COBYLA' # Gradient free modeling_override = recursive_merge(modeling_override, test_modeling_overrides) analysis_override = recursive_merge(analysis_override, test_analysis_overrides) From b7d74c15264454496c11c30c0c4929da5ddbd99b Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 8 Jul 2026 10:24:23 -0600 Subject: [PATCH 8/8] boosting wisdem version for numpy 2.5 support --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index eec45c3ad..fd83ecf7c 100644 --- a/environment.yml +++ b/environment.yml @@ -28,7 +28,7 @@ dependencies: - pyvista - rosco>=2.10.1 - trimesh - - wisdem>=4.2 + - wisdem>=4.2.5 - pip: - control - dash-vtk