diff --git a/WISDEM/wisdem/ccblade/Polar.py b/WISDEM/wisdem/ccblade/Polar.py index 9b182b524..1ae31b473 100644 --- a/WISDEM/wisdem/ccblade/Polar.py +++ b/WISDEM/wisdem/ccblade/Polar.py @@ -1,12 +1,15 @@ -from __future__ import print_function, division -import numpy as np +from __future__ import division, print_function + import os -""" This module contains: +import numpy as np + + +""" This module contains: - Polar: class to represent a polar (computes steady/unsteady parameters, corrections etc.) - blend: function to blend two polars - - thicknessinterp_from_one_set: interpolate polars at different thickeness based on one set of polars - + - thicknessinterp_from_one_set: interpolate polars at different thickeness based on one set of polars + JPJ 7/20 : This class can probably be combined with Polar() from airfoilprep.py. They do not have one-to-one matching for the methods. Because both are not tested extensively, we first need to write tests for both @@ -769,7 +772,7 @@ def cl_fully_separated(self): # Ensuring everything is in harmony cl_inv = cla * (self.alpha - alpha0) - f_st = (self.cl - cl_fs) / (cl_inv - cl_fs) + f_st = (self.cl - cl_fs) / (cl_inv - cl_fs + 1e-10) f_st[np.where(f_st < 1e-15)] = 0 # Storing self.f_st = f_st @@ -942,6 +945,7 @@ def _find_alpha0(alpha, coeff, window): alpha = alpha[iwindow] coeff = coeff[iwindow] alpha_zc, i_zc = _zero_crossings(x=alpha, y=coeff, direction="up") + if len(alpha_zc) > 1: raise Exception( "Cannot find alpha0, {} zero crossings of Coeff in the range of alpha values: [{} {}] ".format( diff --git a/WISDEM/wisdem/glue_code/gc_LoadInputs.py b/WISDEM/wisdem/glue_code/gc_LoadInputs.py index d0c412e11..7f17b42b1 100644 --- a/WISDEM/wisdem/glue_code/gc_LoadInputs.py +++ b/WISDEM/wisdem/glue_code/gc_LoadInputs.py @@ -148,9 +148,12 @@ def set_openmdao_vectors(self): + " is not a multiple of 4 and an equally spaced grid is adopted." ) Re_all = [] + self.modeling_options["WISDEM"]["RotorSE"]["AFTabMod"] = 1 for i in range(self.modeling_options["WISDEM"]["RotorSE"]["n_af"]): for j in range(len(self.wt_init["airfoils"][i]["polars"])): Re_all.append(self.wt_init["airfoils"][i]["polars"][j]["re"]) + if len(self.wt_init["airfoils"][i]["polars"]) > 1: + self.modeling_options["WISDEM"]["RotorSE"]["AFTabMod"] = 2 self.modeling_options["WISDEM"]["RotorSE"]["n_Re"] = len(np.unique(Re_all)) self.modeling_options["WISDEM"]["RotorSE"]["n_tab"] = 1 self.modeling_options["WISDEM"]["RotorSE"]["n_xy"] = self.modeling_options["WISDEM"]["RotorSE"]["n_xy"] diff --git a/WISDEM/wisdem/glue_code/gc_WT_InitModel.py b/WISDEM/wisdem/glue_code/gc_WT_InitModel.py index ff6d9cb55..caa565af9 100644 --- a/WISDEM/wisdem/glue_code/gc_WT_InitModel.py +++ b/WISDEM/wisdem/glue_code/gc_WT_InitModel.py @@ -1224,7 +1224,7 @@ def assign_airfoil_values(wt_opt, modeling_options, airfoils): j_Re = np.zeros(n_Re_i, dtype=int) for j in range(n_Re_i): Re_j[j] = airfoils[i]["polars"][j]["re"] - j_Re[j] = np.argmin(Re - Re_j) + j_Re[j] = np.argmin(abs(Re - Re_j[j])) for k in range(n_tab): cl[i, :, j_Re[j], k] = np.interp( aoa, airfoils[i]["polars"][j]["c_l"]["grid"], airfoils[i]["polars"][j]["c_l"]["values"] @@ -1242,7 +1242,7 @@ def assign_airfoil_values(wt_opt, modeling_options, airfoils): "WARNING: Airfoil " + name[i] + " has the lift coefficient at Re " - + str(Re_j) + + str(Re_j[j]) + " different between + and - pi rad. This is fixed automatically, but please check the input data." ) if abs(cd[i, 0, j, k] - cd[i, -1, j, k]) > 1.0e-5: @@ -1251,7 +1251,7 @@ def assign_airfoil_values(wt_opt, modeling_options, airfoils): "WARNING: Airfoil " + name[i] + " has the drag coefficient at Re " - + str(Re_j) + + str(Re_j[j]) + " different between + and - pi rad. This is fixed automatically, but please check the input data." ) if abs(cm[i, 0, j, k] - cm[i, -1, j, k]) > 1.0e-5: @@ -1260,7 +1260,7 @@ def assign_airfoil_values(wt_opt, modeling_options, airfoils): "WARNING: Airfoil " + name[i] + " has the moment coefficient at Re " - + str(Re_j) + + str(Re_j[j]) + " different between + and - pi rad. This is fixed automatically, but please check the input data." ) @@ -1302,7 +1302,7 @@ def assign_airfoil_values(wt_opt, modeling_options, airfoils): wt_opt["airfoils.name"] = name wt_opt["airfoils.ac"] = ac wt_opt["airfoils.r_thick"] = r_thick - wt_opt["airfoils.Re"] = Re # Not yet implemented! + wt_opt["airfoils.Re"] = Re wt_opt["airfoils.cl"] = cl wt_opt["airfoils.cd"] = cd wt_opt["airfoils.cm"] = cm diff --git a/WISDEM/wisdem/rotorse/rotor_power.py b/WISDEM/wisdem/rotorse/rotor_power.py index 79c220524..bb771f966 100644 --- a/WISDEM/wisdem/rotorse/rotor_power.py +++ b/WISDEM/wisdem/rotorse/rotor_power.py @@ -9,6 +9,7 @@ from openmdao.api import Group, ExplicitComponent from scipy.optimize import brentq, minimize, minimize_scalar from scipy.interpolate import PchipInterpolator +from wisdem.ccblade.Polar import Polar from wisdem.ccblade.ccblade import CCBlade, CCAirfoil from wisdem.commonse.utilities import smooth_abs, smooth_min, linspace_with_deriv from wisdem.commonse.distribution import RayleighCDF, WeibullWithMeanCDF @@ -843,25 +844,20 @@ def eval_unsteady(alpha, cl, cd, cm): # calculate unsteady coefficients from polars for OpenFAST's Aerodyn unsteady = {} - - alpha_rad = np.radians(alpha) - cn = cl * np.cos(alpha_rad) + cd * np.sin(alpha_rad) - - # alpha0, Cd0, Cm0 - aoa_l = [-30.0] - aoa_h = [30.0] - idx_low = np.argmin(np.abs(alpha - aoa_l)) - idx_high = np.argmin(np.abs(alpha - aoa_h)) - - if np.abs(np.gradient(cl)).max() > 0.0: - unsteady["alpha0"] = np.interp(0.0, cl[idx_low:idx_high], alpha[idx_low:idx_high]) - unsteady["Cd0"] = np.interp(0.0, cl[idx_low:idx_high], cd[idx_low:idx_high]) - unsteady["Cm0"] = np.interp(0.0, cl[idx_low:idx_high], cm[idx_low:idx_high]) - else: - unsteady["alpha0"] = 0.0 - unsteady["Cd0"] = cd[np.argmin(np.abs(alpha - 0.0))] - unsteady["Cm0"] = 0.0 - + Re = 1e6 # Does not factor into any calculations + try: + mypolar = Polar(Re, alpha, cl, cd, cm, compute_params=True, radians=False) + (alpha0, alpha1, alpha2, cnSlope, cn1, cn2, cd0, cm0) = mypolar.unsteadyParams() + except: + alpha0 = alpha1 = alpha2 = cnSlope = cn1 = cn2 = cd0 = cm0 = 0.0 + unsteady["alpha0"] = alpha0 + unsteady["alpha1"] = alpha1 + unsteady["alpha2"] = alpha2 + unsteady["Cd0"] = cd0 + unsteady["Cm0"] = cm0 + unsteady["Cn1"] = cn1 + unsteady["Cn2"] = cn2 + unsteady["C_nalpha"] = cnSlope unsteady["eta_e"] = 1 unsteady["T_f0"] = "Default" unsteady["T_V0"] = "Default" @@ -877,97 +873,6 @@ def eval_unsteady(alpha, cl, cd, cm): unsteady["S2"] = 0 unsteady["S3"] = 0 unsteady["S4"] = 0 - - def find_breakpoint(x, y, idx0, idx1, multi=1.0): - lin_fit = np.interp(x[idx0:idx1], [x[idx0], x[idx1]], [y[idx0], y[idx1]]) - idx_break = 0 - lin_diff = 0 - # GB: Seems like this for loop can be replaced in two lines: - test_diff = np.abs(y[idx0:idx1] - lin_fit) if multi == 0.0 else multi * (y[idx0:idx1] - lin_fit) - idx_break2 = np.argmax(test_diff) + idx0 - for i, (fit, yi) in enumerate(zip(lin_fit, y[idx0:idx1])): - if multi == 0: - diff_i = np.abs(yi - fit) - else: - diff_i = multi * (yi - fit) - if diff_i > lin_diff: - lin_diff = diff_i - idx_break = i - idx_break += idx0 - # print(idx_break, idx_break2) - return idx_break - - # Cn1 - idx_alpha0 = np.argmin(np.abs(alpha - unsteady["alpha0"])) - - if np.abs(np.gradient(cm)).max() > 1.0e-10: - # print('aoa_h',aoa_h, alpha[idx_alpha0] + 35.0) - aoa_h = alpha[idx_alpha0] + 35.0 - idx_high = high0 = np.argmin(np.abs(alpha - aoa_h)) - - # GB: Don't really understand what this is doing. Also seems like idx_high will always be the last index? - cm_temp = cm[idx_low:idx_high] - idx_cm_min = [ - i - for i, local_min in enumerate( - np.r_[True, cm_temp[1:] < cm_temp[:-1]] & np.r_[cm_temp[:-1] < cm_temp[1:], True] - ) - if local_min - ] + idx_low - idx_high = idx_cm_min[-1] - # print('0', high0, idx_high) - idx_Cn1 = find_breakpoint(alpha, cm, idx_alpha0, idx_high) - unsteady["Cn1"] = cn[idx_Cn1] - else: - idx_Cn1 = np.argmin(np.abs(alpha - 0.0)) - unsteady["Cn1"] = 0.0 - - # Cn2 - if np.abs(np.gradient(cm)).max() > 1.0e-10: - # print('aoa_l',aoa_l, np.mean([alpha[idx_alpha0], alpha[idx_Cn1]]) - 30.0) - aoa_l = np.mean([alpha[idx_alpha0], alpha[idx_Cn1]]) - 30.0 - idx_low = np.argmin(np.abs(alpha - aoa_l)) - - # GB: Don't really understand what this is doing. Also seems like idx_high will always be the last index? - # GB: idx_high isn't even used here. Should this be idx_low? - # cm_temp = cm[idx_low:idx_high] - # idx_cm_min = [ - # i - # for i, local_min in enumerate( - # np.r_[True, cm_temp[1:] < cm_temp[:-1]] & np.r_[cm_temp[:-1] < cm_temp[1:], True] - # ) - # if local_min - # ] + idx_low - # idx_high = idx_cm_min[-1] - - idx_Cn2 = find_breakpoint(alpha, cm, idx_low, idx_alpha0, multi=0.0) - unsteady["Cn2"] = cn[idx_Cn2] - else: - idx_Cn2 = np.argmin(np.abs(alpha - 0.0)) - unsteady["Cn2"] = 0.0 - - # C_nalpha - # GB: Added index check to avoid backwards cases - if np.abs(np.gradient(cm)).max() > 1.0e-10 and (idx_Cn1 > idx_alpha0): - # unsteady['C_nalpha'] = np.gradient(cn, alpha_rad)[idx_alpha0] - unsteady["C_nalpha"] = np.gradient(cn[idx_alpha0:idx_Cn1], alpha_rad[idx_alpha0:idx_Cn1]).max() - else: - unsteady["C_nalpha"] = 0.0 - - # alpha1, alpha2 - # finding the break point in drag as a proxy for Trailing Edge separation, f=0.7 - # 3d stall corrections cause erroneous f calculations - # GB: Added index check to avoid backwards cases - if np.abs(np.gradient(cm)).max() > 1.0e-10 and (alpha[idx_Cn1] > 0.0): - aoa_l = [0.0] - idx_low = np.argmin(np.abs(alpha - aoa_l)) - idx_alpha1 = find_breakpoint(alpha, cd, idx_low, idx_Cn1, multi=-1.0) - unsteady["alpha1"] = alpha[idx_alpha1] - else: - idx_alpha1 = np.argmin(np.abs(alpha - 0.0)) - unsteady["alpha1"] = 0.0 - unsteady["alpha2"] = -1.0 * unsteady["alpha1"] - unsteady["St_sh"] = "Default" unsteady["k0"] = 0 unsteady["k1"] = 0 @@ -977,7 +882,6 @@ def find_breakpoint(x, y, idx0, idx1, multi=1.0): unsteady["x_cp_bar"] = "Default" unsteady["UACutout"] = "Default" unsteady["filtCutOff"] = "Default" - unsteady["Alpha"] = alpha unsteady["Cl"] = cl unsteady["Cd"] = cd diff --git a/examples/01_aeroelasticse/run_DLC.py b/examples/01_aeroelasticse/run_DLC.py index ad0f8007f..597c37e2a 100644 --- a/examples/01_aeroelasticse/run_DLC.py +++ b/examples/01_aeroelasticse/run_DLC.py @@ -150,6 +150,7 @@ path2dll = os.path.join(run_dir1, 'local/lib/libdiscon.so') case_inputs[("ServoDyn","DLL_FileName")] = {'vals':[path2dll], 'group':0} +case_inputs[("AeroDyn15","AFAeroMod")] = {'vals':[2], 'group':0} case_inputs[("AeroDyn15","TwrAero")] = {'vals':["True"], 'group':0} case_inputs[("AeroDyn15","TwrPotent")] = {'vals':[1], 'group':0} case_inputs[("AeroDyn15","TwrShadow")] = {'vals':[1], 'group':0} diff --git a/examples/02_control_opt/analysis_options.yaml b/examples/02_control_opt/analysis_options.yaml index ee0706351..4d403526e 100644 --- a/examples/02_control_opt/analysis_options.yaml +++ b/examples/02_control_opt/analysis_options.yaml @@ -51,9 +51,9 @@ design_variables: max_end: 0.99 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: True diff --git a/examples/03_NREL5MW_OC3_spar/analysis_options.yaml b/examples/03_NREL5MW_OC3_spar/analysis_options.yaml index 56d22ee1a..4e2ca0fc2 100644 --- a/examples/03_NREL5MW_OC3_spar/analysis_options.yaml +++ b/examples/03_NREL5MW_OC3_spar/analysis_options.yaml @@ -51,9 +51,9 @@ design_variables: max_end: 1.0 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/examples/04_NREL5MW_OC4_semi/analysis_options.yaml b/examples/04_NREL5MW_OC4_semi/analysis_options.yaml index 703dadff6..d0e14c77e 100644 --- a/examples/04_NREL5MW_OC4_semi/analysis_options.yaml +++ b/examples/04_NREL5MW_OC4_semi/analysis_options.yaml @@ -51,9 +51,9 @@ design_variables: max_end: 1.0 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/examples/04_NREL5MW_OC4_semi/analysis_options_semi.yaml b/examples/04_NREL5MW_OC4_semi/analysis_options_semi.yaml deleted file mode 100644 index 7b3b96f20..000000000 --- a/examples/04_NREL5MW_OC4_semi/analysis_options_semi.yaml +++ /dev/null @@ -1,203 +0,0 @@ -general: - folder_output: outputs_floating_example - fname_output: optimized_semi -design_variables: - floating: - joints: - z_coordinate: - # Adjusts the z-coordinate of the listed joints: Useful for draft, freeboard, fairlead - linked: - # This will set the same z-value for all of the joints listed in the same names entry - - names: [col1_keel, col2_keel, col3_keel] - flag: True - lower_bound = -100.0 - upper_bound = -5.0 - - names: [col1_freeboard, col2_freeboard, col3_freeboard] - flag: True - lower_bound = 5.0 - upper_bound = 30.0 - independent: - # This will adjust the z-coordinate of the joints separately - - name: main_keel - flag: True - lower_bound = -100.0 - upper_bound = -5.0 - - name: main_freeboard - flag: True - lower_bound = 5.0 - upper_bound = 30.0 - r_coordinate: - # Adjusts the r-coordinate of the listed joints to be a desired radius from (0,0). - linked: - # This will set the x,y-value for all of the joints listed in the same names entry at constant angle relative to (0,0) - - names: [col1_keel, col1_freeboard, col2_keel, col2_freeboard, col3_keel, col3_freeboard] - flag: True - lower_bound = 5.0 - upper_bound = 75.0 - members: - diameter: - linked_members: - # This will set the same diameter profile for all of the members listed in the same names entry - - names: [column1, column2, column3] - flag: True - lower_bound: 1.0 - upper_bound: 20.0 - - names: [delta_pontoon_lower12, delta_pontoon_lower23, delta_pontoon_lower31] - flag: True - lower_bound: 1.0 - upper_bound: 5.0 - - names: [delta_pontoon_upper12, delta_pontoon_upper23, delta_pontoon_upper31] - flag: True - lower_bound: 1.0 - upper_bound: 5.0 - - names: [Y_pontoon_lower12, Y_pontoon_lower23, Y_pontoon_lower31] - flag: True - lower_bound: 1.0 - upper_bound: 5.0 - - names: [Y_pontoon_upper12, Y_pontoon_upper23, Y_pontoon_upper31] - flag: True - lower_bound: 1.0 - upper_bound: 5.0 - - names: [cross_pontoon1, cross_pontoon2, cross_pontoon3] - flag: True - lower_bound: 1.0 - upper_bound: 5.0 - independent_members: - # This will set the same diameter for all of the members listed in the same names entry - - name: main_column - flag: True - lower_bound: 1.0 - upper_bound: 20.0 - thickness: - linked_members: - # This will set the same thickness profile for all of the members listed in the same names entry - - names: [column1, column2, column3] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - - names: [delta_pontoon_lower12, delta_pontoon_lower23, delta_pontoon_lower31] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - - names: [delta_pontoon_upper12, delta_pontoon_upper23, delta_pontoon_upper31] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - - names: [Y_pontoon_lower12, Y_pontoon_lower23, Y_pontoon_lower31] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - - names: [Y_pontoon_upper12, Y_pontoon_upper23, Y_pontoon_upper31] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - - names: [cross_pontoon1, cross_pontoon2, cross_pontoon3] - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - independent_members: - # This will set the same thickness for all of the members listed in the same names entry - - name: main_column - flag: True - lower_bound: 0.001 - upper_bound: 0.1 - ballast: - linked: - # This will set the same ballast for all of the members listed in the same names entry. Permanent and variable ballast values will differ, but the same per group - - names: [column1, column2, column3] - flag: True - lower_bound: 1.0 - upper_bound: 1e6 - independent: - # This will set the same ballast for all of the members listed in the same names entry - - name: main_column - flag: True - lower_bound: 1.0 - upper_bound: 1e6 - axial_joints: - # Adjusts the "s-coordinate" (0..1 along the member axis) of the listed joints - # Instead of operating in absolute z-space, the coordinate value is non-dimensional along the member axis (0=joint1) - linked: - - names: [col1_lower_pontoon, col2_lower_pontoon, col3_lower_pontoon] - flag: True - lower_bound: 0.0 - upper_bound: 1.0 - - names: [col1_upper_pontoon, col2_upper_pontoon, col3_upper_pontoon] - flag: True - lower_bound: 0.0 - upper_bound: 1.0 - - names: [col1_cross_pontoon, col2_cross_pontoon, col3_cross_pontoon] - flag: True - lower_bound: 0.0 - upper_bound: 1.0 - - names: [col1_fairlead, col2_fairlead, col3_fairlead] - flag: True - lower_bound: 0.0 - upper_bound: 1.0 - stiffeners: - ring: - size: - flag: True - min_gain: 0.5 - max_gain: 3.0 - spacing: - flag: True - lower_bound: 0.1 - upper_bound: 1.0 - - longitudinal: - size: - flag: False - spacing: - flag: False - mooring: - anchor_radius: - # This adjusts the radius of all anchor nodes from (0,0) - flag: True - lower_bound: 10.0 - upper_bound: 1000.0 - line_length: - # Adjust the unstretched line length of the mooring segments - linked: - # This will set the same line length for all segments listed in the same names entry - - names: [line1, line2, line3] - flag: True - lower_bound: 100.0 - upper_bound: 2000.0 - line_diameter: - # Adjust the diameter of the mooring segments - linked: - # This will set the same diameter for all segments listed in the same names entry - - names: [line1, line2, line3] - flag: True - lower_bound: 0.1 - upper_bound: 2.0 - -merit_figure: LCOE # Merit figure of the optimization problem. The options are 'AEP' - 'LCOE' - 'Cp' - 'blade_mass' - 'blade_tip_deflection' - -constraints: - floating: - # TODO! - - control: - # TODO! - -driver: - optimization: - flag: True # Flag to enable optimization - tol: 1.e-2 # Optimality tolerance - max_major_iter: 10 # Maximum number of major design iterations (SNOPT) - max_minor_iter: 100 # Maximum number of minor design iterations (SNOPT) - max_iter: 100 # Maximum number of iterations (SLSQP) - solver: SLSQP # 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 - design_of_experiments: - flag: False # Flag to enable design of experiments - run_parallel: True # Flag to run using parallel processing - generator: Uniform # Type of input generator. (Uniform) - num_samples: 5 # number of samples for (Uniform only) - -recorder: - flag: True # Flag to activate OpenMDAO recorder - file_name: log_opt.sql # Name of OpenMDAO recorder diff --git a/examples/05_IEA-3.4-130-RWT/analysis_options.yaml b/examples/05_IEA-3.4-130-RWT/analysis_options.yaml index 0c1cb4ed6..770a72d68 100644 --- a/examples/05_IEA-3.4-130-RWT/analysis_options.yaml +++ b/examples/05_IEA-3.4-130-RWT/analysis_options.yaml @@ -51,9 +51,9 @@ design_variables: max_end: 1.0 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/examples/06_IEA-15-240-RWT/analysis_options.yaml b/examples/06_IEA-15-240-RWT/analysis_options.yaml index 3707c82c2..63e8b2a18 100644 --- a/examples/06_IEA-15-240-RWT/analysis_options.yaml +++ b/examples/06_IEA-15-240-RWT/analysis_options.yaml @@ -52,8 +52,8 @@ design_variables: control: tsr: flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/examples/07_te_flaps/analysis_options.yaml b/examples/07_te_flaps/analysis_options.yaml index 5bc6ee1ac..8f43ea4f4 100644 --- a/examples/07_te_flaps/analysis_options.yaml +++ b/examples/07_te_flaps/analysis_options.yaml @@ -41,9 +41,9 @@ design_variables: max_end: 0.99 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/examples/08_OLAF/analysis_options.yaml b/examples/08_OLAF/analysis_options.yaml index 173a20387..0b4b90708 100644 --- a/examples/08_OLAF/analysis_options.yaml +++ b/examples/08_OLAF/analysis_options.yaml @@ -41,7 +41,7 @@ constraints: driver: optimization: - flag: True + flag: False tol: 1.e-3 # Optimality tolerance max_major_iter: 10 # Maximum number of major design iterations (SNOPT) max_minor_iter: 100 # Maximum number of minor design iterations (SNOPT) diff --git a/examples/09_design_of_experiments/analysis_options.yaml b/examples/09_design_of_experiments/analysis_options.yaml index d97040ff0..b22cab1fc 100644 --- a/examples/09_design_of_experiments/analysis_options.yaml +++ b/examples/09_design_of_experiments/analysis_options.yaml @@ -51,9 +51,9 @@ design_variables: max_end: 1.0 control: tsr: - flag: False # Flag to optimize the rotor tip speed ratio - min_gain: 0.9 # Nondimensional lower bound - max_gain: 1.1 # Nondimensional upper bound + flag: False + minimum: 7 + maximum: 13 servo: pitch_control: flag: False diff --git a/weis/aeroelasticse/FAST_writer.py b/weis/aeroelasticse/FAST_writer.py index 4ede01e59..f6665fa75 100644 --- a/weis/aeroelasticse/FAST_writer.py +++ b/weis/aeroelasticse/FAST_writer.py @@ -1036,9 +1036,11 @@ def write_AeroDyn15Polar(self): # f.write('{:<22d} {:<11} {:}'.format(self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs'], 'NumTabs', '! Number of airfoil tables in this file. Each table must have lines for Re and Ctrl.\n')) - # check if airfoils with multiple flaps exists. + # Check if we have multiple tables per airfoil # if yes, allocate the number of airfoils to the respective radial stations - if self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs'] > 1: + if self.fst_vt['AeroDyn15']['AFTabMod'] == 2: + num_tab = len(self.fst_vt['AeroDyn15']['af_data'][afi]) + elif self.fst_vt['AeroDyn15']['AFTabMod'] == 3: # for tab_orig in range(self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs'] - 1): if self.fst_vt['AeroDyn15']['af_data'][afi][0]['Ctrl'] == self.fst_vt['AeroDyn15']['af_data'][afi][1]['Ctrl']: num_tab = 1 # assume that all Ctrl angles of the flaps are identical if the first two are -> no flaps! @@ -1049,8 +1051,8 @@ def write_AeroDyn15Polar(self): # f.write('{:<22d} {:<11} {:}'.format(self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs'], 'NumTabs','! Number of airfoil tables in this file. Each table must have lines for Re and Ctrl.\n')) f.write('{:<22d} {:<11} {:}'.format(num_tab, 'NumTabs','! Number of airfoil tables in this file. Each table must have lines for Re and Ctrl.\n')) - # for tab in range(self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs']): # For writting multiple tables (different Re or Ctrl values) - for tab in range(num_tab): # For writting multiple tables (different Re or Ctrl values) + # for tab in range(self.fst_vt['AeroDyn15']['af_data'][afi][0]['NumTabs']): # For writing multiple tables (different Re or Ctrl values) + for tab in range(num_tab): # For writing multiple tables (different Re or Ctrl values) f.write('! ------------------------------------------------------------------------------\n') f.write("! data for table %i \n" % (tab + 1)) f.write('! ------------------------------------------------------------------------------\n') diff --git a/weis/aeroelasticse/openmdao_openfast.py b/weis/aeroelasticse/openmdao_openfast.py index 7b114c6c2..e3aa58a12 100644 --- a/weis/aeroelasticse/openmdao_openfast.py +++ b/weis/aeroelasticse/openmdao_openfast.py @@ -141,8 +141,6 @@ def setup(self): self.add_input('airfoils_cm', val=np.zeros((n_span, n_aoa, n_Re, n_tab)), desc='moment coefficients, spanwise') self.add_input('airfoils_aoa', val=np.zeros((n_aoa)), units='deg', desc='angle of attack grid for polars') self.add_input('airfoils_Re', val=np.zeros((n_Re)), desc='Reynolds numbers of polars') - self.add_input('airfoils_Re_loc', val=np.zeros((n_span, n_Re, n_tab)), desc='temporary - matrix of Re numbers') - self.add_input('airfoils_Ma_loc', val=np.zeros((n_span, n_Re, n_tab)), desc='temporary - matrix of Ma numbers') self.add_input('airfoils_Ctrl', val=np.zeros((n_span, n_Re, n_tab)), units='deg',desc='Airfoil control paremeter (i.e. flap angle)') # Airfoil coordinates @@ -610,16 +608,34 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs): # fst_vt['AeroDyn15']['af_data'] = [{}]*len(airfoils) fst_vt['AeroDyn15']['af_data'] = [] - if self.n_tab > 1: + # Set the AD15 flag AFTabMod, deciding whether we use more Re per airfoil or user-defined tables (used for example in distributed aerodynamic control) + if fst_vt['AeroDyn15']['AFTabMod'] == 1: + # If AFTabMod is the default coming form the schema, check the value from WISDEM, which might be set to 2 if more Re per airfoil are defined in the geometry yaml + fst_vt['AeroDyn15']['AFTabMod'] = modeling_options["WISDEM"]["RotorSE"]["AFTabMod"] + if self.n_tab > 1 and fst_vt['AeroDyn15']['AFTabMod'] == 1: fst_vt['AeroDyn15']['AFTabMod'] = 3 + elif self.n_tab > 1 and fst_vt['AeroDyn15']['AFTabMod'] == 2: + raise Exception('OpenFAST does not support both multiple Re and multiple user defined tabs. Please remove DAC devices or Re polars') for i in range(self.n_span): # No of blade radial stations fst_vt['AeroDyn15']['af_data'].append([]) + if fst_vt['AeroDyn15']['AFTabMod'] == 1: + loop_index = 1 + elif fst_vt['AeroDyn15']['AFTabMod'] == 2: + loop_index = self.n_Re + else: + loop_index = self.n_tab - for j in range(self.n_tab): # No of tabs; if there are no flaps at this blade station - unsteady = eval_unsteady(inputs['airfoils_aoa'], inputs['airfoils_cl'][i,:,0,j], inputs['airfoils_cd'][i,:,0,j], inputs['airfoils_cm'][i,:,0,j]) + for j in range(loop_index): # Number of tabs or Re + if fst_vt['AeroDyn15']['AFTabMod'] == 1: + unsteady = eval_unsteady(inputs['airfoils_aoa'], inputs['airfoils_cl'][i,:,0,0], inputs['airfoils_cd'][i,:,0,0], inputs['airfoils_cm'][i,:,0,0]) + elif fst_vt['AeroDyn15']['AFTabMod'] == 2: + unsteady = eval_unsteady(inputs['airfoils_aoa'], inputs['airfoils_cl'][i,:,j,0], inputs['airfoils_cd'][i,:,j,0], inputs['airfoils_cm'][i,:,j,0]) + else: + unsteady = eval_unsteady(inputs['airfoils_aoa'], inputs['airfoils_cl'][i,:,0,j], inputs['airfoils_cd'][i,:,0,j], inputs['airfoils_cm'][i,:,0,j]) + fst_vt['AeroDyn15']['af_data'][i].append({}) @@ -629,13 +645,14 @@ def update_FAST_model(self, fst_vt, inputs, discrete_inputs): fst_vt['AeroDyn15']['af_data'][i][j]['NumCoords'] = '@"AF{:02d}_Coords.txt"'.format(i) else: fst_vt['AeroDyn15']['af_data'][i][j]['NumCoords'] = 0 - fst_vt['AeroDyn15']['af_data'][i][j]['NumTabs'] = self.n_tab - if inputs['airfoils_Re_loc'][i][0][j] == 0: # check if Re ws locally determined (e.g. for trailing edge flaps) - fst_vt['AeroDyn15']['af_data'][i][j]['Re'] = 0.75 # TODO: functionality for multiple Re tables + + fst_vt['AeroDyn15']['af_data'][i][j]['NumTabs'] = loop_index + if fst_vt['AeroDyn15']['AFTabMod'] == 3: + fst_vt['AeroDyn15']['af_data'][i][j]['Ctrl'] = inputs['airfoils_Ctrl'][i,0,j] # unsteady['Ctrl'] # added to unsteady function for variable flap controls at airfoils + fst_vt['AeroDyn15']['af_data'][i][j]['Re'] = inputs['airfoils_Re'][0] # If AFTabMod==3 the Re is neglected, but it still must be the same across tables else: - fst_vt['AeroDyn15']['af_data'][i][j]['Re'] = inputs['airfoils_Re_loc'][i,0,j]/1000000 # give in millions - fst_vt['AeroDyn15']['af_data'][i][j]['Ctrl'] = inputs['airfoils_Ctrl'][i,0,j] # unsteady['Ctrl'] # added to unsteady function for variable flap controls at airfoils - + fst_vt['AeroDyn15']['af_data'][i][j]['Re'] = inputs['airfoils_Re'][j] + fst_vt['AeroDyn15']['af_data'][i][j]['Ctrl'] = 0. fst_vt['AeroDyn15']['af_data'][i][j]['InclUAdata']= "True" fst_vt['AeroDyn15']['af_data'][i][j]['alpha0'] = unsteady['alpha0'] fst_vt['AeroDyn15']['af_data'][i][j]['alpha1'] = unsteady['alpha1'] diff --git a/weis/glue_code/glue_code.py b/weis/glue_code/glue_code.py index ceb7b51d5..acf9b2b8f 100644 --- a/weis/glue_code/glue_code.py +++ b/weis/glue_code/glue_code.py @@ -326,6 +326,7 @@ def setup(self): self.connect('sse_tune.aeroperf_tables.Cp', 'aeroelastic.Cp_aero_table') self.connect('sse_tune.aeroperf_tables.Ct', 'aeroelastic.Ct_aero_table') self.connect('sse_tune.aeroperf_tables.Cq', 'aeroelastic.Cq_aero_table') + self.connect('xf.flap_angles', 'aeroelastic.airfoils_Ctrl') if modeling_options['flags']['mooring']: self.connect("mooring.line_diameter", "aeroelastic.line_diameter") @@ -342,11 +343,6 @@ def setup(self): self.connect("mooring.nodes_drag_area", "aeroelastic.nodes_drag_area") self.connect("mooring.unstretched_length", "aeroelastic.unstretched_length") self.connect("mooring.node_names", "aeroelastic.node_names") - - # Temporary - self.connect('xf.Re_loc', 'aeroelastic.airfoils_Re_loc') - self.connect('xf.Ma_loc', 'aeroelastic.airfoils_Ma_loc') - self.connect('xf.flap_angles', 'aeroelastic.airfoils_Ctrl') if modeling_options['openfast']['dlc_settings']['run_blade_fatigue']: self.connect('re.precomp.x_tc', 'aeroelastic.x_tc') diff --git a/weis/test/run_examples.py b/weis/test/run_examples.py index f1eb280a6..51821a4f0 100644 --- a/weis/test/run_examples.py +++ b/weis/test/run_examples.py @@ -18,9 +18,9 @@ # "02_control_opt/run_lin_turbine", # "02_control_opt/runOptimization", - # "03_NREL5MW_OC3_spar/weis_driver", + "03_NREL5MW_OC3_spar/weis_driver", - # "04_NREL5MW_OC4_semi/", # there appears to be no python file within this folder + "04_NREL5MW_OC4_semi/weis_driver", "05_IEA-3.4-130-RWT/weis_driver",