From f24a213ffa6b1e828df8e83893449551cfd3a60a Mon Sep 17 00:00:00 2001 From: dzalkind <65573423+dzalkind@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:26:58 -0700 Subject: [PATCH] Fix connections to drivese_post with WISDEM 3.17 (#317) * Fix connections to drivese_post * Set run directories relative to input files * Get member ids from modopts * Tidy OC3 example * Re-enable testing of OC3 spar * Send aero-only hub loads to WISDEM * Disable potential flow modeling for fixed substructures * Disable second tower mode in OC3 example for now --- .../03_NREL5MW_OC3_spar/modeling_options.yaml | 6 ++--- weis/aeroelasticse/openmdao_openfast.py | 26 +++++++++++++++---- weis/glue_code/gc_LoadInputs.py | 21 ++++++++------- weis/glue_code/glue_code.py | 4 +-- weis/test/run_examples.py | 2 +- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/examples/03_NREL5MW_OC3_spar/modeling_options.yaml b/examples/03_NREL5MW_OC3_spar/modeling_options.yaml index fb50fb28e..be01c1533 100644 --- a/examples/03_NREL5MW_OC3_spar/modeling_options.yaml +++ b/examples/03_NREL5MW_OC3_spar/modeling_options.yaml @@ -2,7 +2,6 @@ General: verbosity: False # When set to True, the code prints to screen many infos openfast_configuration: OF_run_fst: NREL5MW_OC3_spar - OF_run_dir: outputs/03_NREL5MW_OC3_spar use_exe: True WISDEM: @@ -18,7 +17,6 @@ WISDEM: flag: True FloatingSE: flag: True - rank_and_file: True BOS: flag: True @@ -55,9 +53,9 @@ Level3: # Options for WEIS fidelity level 3 = nonlinear time domain GenDOF: True YawDOF: False TwFADOF1 : True - TwFADOF2 : True + TwFADOF2 : False TwSSDOF1 : True - TwSSDOF2 : True + TwSSDOF2 : False PtfmSgDOF: True PtfmSwDOF: True PtfmHvDOF: True diff --git a/weis/aeroelasticse/openmdao_openfast.py b/weis/aeroelasticse/openmdao_openfast.py index 498a1b79c..3caa73d01 100644 --- a/weis/aeroelasticse/openmdao_openfast.py +++ b/weis/aeroelasticse/openmdao_openfast.py @@ -365,9 +365,9 @@ def setup(self): OFmgmt = modopt['General']['openfast_configuration'] self.model_only = OFmgmt['model_only'] FAST_directory_base = OFmgmt['OF_run_dir'] - # If the path is relative, make it an absolute path to current working directory + # If the path is relative, make it an absolute path to modeling options file if not os.path.isabs(FAST_directory_base): - FAST_directory_base = os.path.join(os.getcwd(), FAST_directory_base) + FAST_directory_base = os.path.join(os.path.dirname(modopt['fname_input_modeling']), FAST_directory_base) # Flag to clear OpenFAST run folder. Use it only if disk space is an issue self.clean_FAST_directory = False self.FAST_InputFile = OFmgmt['OF_run_fst'] @@ -472,6 +472,8 @@ def setup(self): # Hub outputs self.add_output('hub_Fxyz', val=np.zeros(3), units='kN', desc = 'Maximum hub forces in the non rotating frame') self.add_output('hub_Mxyz', val=np.zeros(3), units='kN*m', desc = 'Maximum hub moments in the non rotating frame') + self.add_output('hub_Fxyz_aero', val=np.zeros(3), units='N', desc = 'Aero-only maximum hub forces in the non rotating frame') + self.add_output('hub_Mxyz_aero', val=np.zeros(3), units='N*m', desc = 'Aero-only maximum hub moments in the non rotating frame') self.add_output('max_TwrBsMyt',val=0.0, units='kN*m', desc='maximum of tower base bending moment in fore-aft direction') self.add_output('max_TwrBsMyt_ratio',val=0.0, desc='ratio of maximum of tower base bending moment in fore-aft direction to maximum allowable bending moment') @@ -1428,8 +1430,10 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs): else: PropPotBool = [False] * fst_vt['HydroDyn']['NMembers'] for k in range(fst_vt['HydroDyn']['NMembers']): - idx = discrete_inputs['platform_elem_memid'][k] - PropPotBool[k] = modopt["Level1"]["model_potential"][idx] + # Potential modeling of fixed substructres not supported + if modopt['flags']['floating']: + idx = modopt['floating']['members']['platform_elem_memid'][k] + PropPotBool[k] = modopt["Level1"]["model_potential"][idx] fst_vt['HydroDyn']['PropPot'] = PropPotBool if fst_vt['HydroDyn']['NBody'] > 1: @@ -1651,6 +1655,7 @@ def output_channels(self,fst_vt): channels_out += ["Spn1MLxb3", "Spn2MLxb3", "Spn3MLxb3", "Spn4MLxb3", "Spn5MLxb3", "Spn6MLxb3", "Spn7MLxb3", "Spn8MLxb3", "Spn9MLxb3"] channels_out += ["Spn1MLyb3", "Spn2MLyb3", "Spn3MLyb3", "Spn4MLyb3", "Spn5MLyb3", "Spn6MLyb3", "Spn7MLyb3", "Spn8MLyb3", "Spn9MLyb3"] channels_out += ["RtFldCp", "RtFldCt"] + channels_out += ["RtFldFxh", "RtFldFyh", "RtFldFzh", "RtFldMxh", "RtFldMyh", "RtFldMzh"] channels_out += ["RotSpeed", "GenSpeed", "NacYaw", "Azimuth"] channels_out += ["GenPwr", "GenTq", "BldPitch1", "BldPitch2", "BldPitch3"] channels_out += ["Wind1VelX", "Wind1VelY", "Wind1VelZ"] @@ -1663,7 +1668,6 @@ def output_channels(self,fst_vt): channels_out += ["TwHt1MLxt", "TwHt2MLxt", "TwHt3MLxt", "TwHt4MLxt", "TwHt5MLxt", "TwHt6MLxt", "TwHt7MLxt", "TwHt8MLxt", "TwHt9MLxt"] channels_out += ["TwHt1MLyt", "TwHt2MLyt", "TwHt3MLyt", "TwHt4MLyt", "TwHt5MLyt", "TwHt6MLyt", "TwHt7MLyt", "TwHt8MLyt", "TwHt9MLyt"] channels_out += ["TwHt1MLzt", "TwHt2MLzt", "TwHt3MLzt", "TwHt4MLzt", "TwHt5MLzt", "TwHt6MLzt", "TwHt7MLzt", "TwHt8MLzt", "TwHt9MLzt"] - channels_out += ["RtFldFxh", "RtFldFyh", "RtFldFzh"] channels_out += ["RotThrust", "LSShftFxs", "LSShftFys", "LSShftFzs", "LSShftFxa", "LSShftFya", "LSShftFza"] channels_out += ["RotTorq", "LSSTipMxs", "LSSTipMys", "LSSTipMzs", "LSSTipMxa", "LSSTipMya", "LSSTipMza"] channels_out += ["B1N1Alpha", "B1N2Alpha", "B1N3Alpha", "B1N4Alpha", "B1N5Alpha", "B1N6Alpha", "B1N7Alpha", "B1N8Alpha", "B1N9Alpha", "B2N1Alpha", "B2N2Alpha", "B2N3Alpha", "B2N4Alpha", "B2N5Alpha", "B2N6Alpha", "B2N7Alpha", "B2N8Alpha","B2N9Alpha"] @@ -2097,6 +2101,10 @@ def run_FAST(self, inputs, discrete_inputs, fst_vt): else: magnitude_channels[f'LSShft{s}{k}{x}a'] = ['LSShftFya', 'LSShftFza'] if ik==0 else ['LSSTipMya', 'LSSTipMza'] + # Aero-only hub loads + magnitude_channels["RtFldF"] = ["RtFldFxh", "RtFldFyh", "RtFldFzh"] + magnitude_channels["RtFldM"] = ["RtFldMxh", "RtFldMyh", "RtFldMzh"] + # Fatigue at the tower base # Convert ultstress and S_intercept values to kPa with 1e-3 factor tower_fatigue_base = FatigueParams(load2stress=1.0, @@ -2279,6 +2287,14 @@ def get_blade_loading(self, sum_stats, extreme_table, inputs, discrete_inputs, o outputs['hub_Mxyz'] = np.array([extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['RotTorq'], extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['LSSTipMys'], extreme_table['LSShftM'][np.argmax(sum_stats['LSShftM']['max'])]['LSSTipMzs']])*1.e3 + + # Aero-only for WISDEM (outputs are in N and N-m) + outputs['hub_Fxyz_aero'] = np.array([extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFxh'], + extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFyh'], + extreme_table['RtFldF'][np.argmax(sum_stats['RtFldF']['max'])]['RtFldFzh']]) + outputs['hub_Mxyz_aero'] = np.array([extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMxh'], + extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMyh'], + extreme_table['RtFldM'][np.argmax(sum_stats['RtFldM']['max'])]['RtFldMzh']]) ## Post process aerodynamic data # Angles of attack - max, std, mean diff --git a/weis/glue_code/gc_LoadInputs.py b/weis/glue_code/gc_LoadInputs.py index 81771dea3..d4151ae8a 100644 --- a/weis/glue_code/gc_LoadInputs.py +++ b/weis/glue_code/gc_LoadInputs.py @@ -58,6 +58,17 @@ def set_weis_data(self): # Directory of modeling option input, if we want to use it for relative paths mod_opt_dir = osp.split(self.modeling_options['fname_input_modeling'])[0] + # OpenFAST prefixes + if self.modeling_options['General']['openfast_configuration']['OF_run_fst'] in ['','None','NONE','none']: + self.modeling_options['General']['openfast_configuration']['OF_run_fst'] = 'weis_job' + + if self.modeling_options['General']['openfast_configuration']['OF_run_dir'] in ['','None','NONE','none']: + self.modeling_options['General']['openfast_configuration']['OF_run_dir'] = osp.join( + mod_opt_dir, # If it's a relative path, will be relative to mod_opt directory + self.analysis_options['general']['folder_output'], + 'openfast_runs' + ) + # BEM dir, all levels base_run_dir = os.path.join(mod_opt_dir,self.modeling_options['General']['openfast_configuration']['OF_run_dir']) if MPI: @@ -78,15 +89,7 @@ def set_weis_data(self): self.modeling_options['General']['openfast_configuration']['fst_vt'] = {} self.modeling_options['General']['openfast_configuration']['fst_vt']['outlist'] = fast.fst_vt['outlist'] - # OpenFAST prefixes - if self.modeling_options['General']['openfast_configuration']['OF_run_fst'] in ['','None','NONE','none']: - self.modeling_options['General']['openfast_configuration']['OF_run_fst'] = 'weis_job' - - if self.modeling_options['General']['openfast_configuration']['OF_run_dir'] in ['','None','NONE','none']: - self.modeling_options['General']['openfast_configuration']['OF_run_dir'] = osp.join( - self.analysis_options['general']['folder_output'], - 'openfast_runs' - ) + # User-defined control dylib (path2dll) path2dll = self.modeling_options['General']['openfast_configuration']['path2dll'] diff --git a/weis/glue_code/glue_code.py b/weis/glue_code/glue_code.py index eebe2cbaa..c33fce9f5 100644 --- a/weis/glue_code/glue_code.py +++ b/weis/glue_code/glue_code.py @@ -676,8 +676,8 @@ def setup(self): self.connect('rotorse.rp.powercurve.rated_Q', 'drivese_post.rated_torque') self.connect('configuration.rated_power', 'drivese_post.machine_rating') self.connect('tower.diameter', 'drivese_post.D_top', src_indices=[-1]) - self.connect('aeroelastic.hub_Fxyz', 'drivese_post.F_hub') - self.connect('aeroelastic.hub_Mxyz', 'drivese_post.M_hub') + self.connect('aeroelastic.hub_Fxyz_aero', 'drivese_post.F_aero_hub') + self.connect('aeroelastic.hub_Mxyz_aero', 'drivese_post.M_aero_hub') self.connect('aeroelastic.max_RootMyb', 'drivese_post.pitch_system.BRFM') self.connect('blade.pa.chord_param', 'drivese_post.blade_root_diameter', src_indices=[0]) self.connect('rotorse.blade_mass', 'drivese_post.blade_mass') diff --git a/weis/test/run_examples.py b/weis/test/run_examples.py index 535c9b963..f04d1ed34 100644 --- a/weis/test/run_examples.py +++ b/weis/test/run_examples.py @@ -6,7 +6,7 @@ "01_aeroelasticse/run_OLAF", "01_aeroelasticse/run_nodalOutputs", "02_run_openfast_cases/weis_driver_loads", - #"03_NREL5MW_OC3_spar/weis_driver", # executed in the test_OC3.py + "03_NREL5MW_OC3_spar/weis_driver", # "03_NREL5MW_OC3_spar/weis_freq_driver", # executed in examples_skinny # "04_NREL5MW_OC4_semi/weis_driver", # skipping until we resolve multiple variable ballasts "04_NREL5MW_OC4_semi/weis_freq_driver",