def load_blade_info(self): ''' Loads wind turbine blade data from an OpenFAST model. Should be used if blade information is needed (i.e.) for flap controller tuning, but a rotor performance file is defined and and cc-blade is not run already. Parameters: ----------- self - note: needs to contain fast input file info provided by load_from_fast. ''' from wisdem.aeroelasticse.FAST_reader import InputReader_OpenFAST # Load Fast input deck # self.TurbineName = FAST_InputFile.strip('.fst') # fast = InputReader_OpenFAST(FAST_ver=FAST_ver,dev_branch=dev_branch) # self.fast.FAST_InputFile = FAST_InputFile # self.fast.FAST_directory = FAST_directory # self.fast.execute() # Make sure cc_rotor exists for DAC analysis try: if self.cc_rotor: self.af_data = self.fast.fst_vt['AeroDyn15']['af_data'] self.bld_flapwise_damp = self.fast.fst_vt['ElastoDynBlade']['BldFlDmp1']/100 * 0.7 except AttributeError: # Create CC-Blade Rotor r0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlSpn']) chord0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlChord']) theta0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlTwist']) # -- Adjust for Aerodyn15 r = r0 + self.Rhub chord_intfun = interpolate.interp1d(r0,chord0, bounds_error=None, fill_value='extrapolate', kind='zero') chord = chord_intfun(r) theta_intfun = interpolate.interp1d(r0,theta0, bounds_error=None, fill_value='extrapolate', kind='zero') theta = theta_intfun(r) af_idx = np.array(self.fast.fst_vt['AeroDynBlade']['BlAFID']).astype(int) - 1 #Reset to 0 index AFNames = self.fast.fst_vt['AeroDyn15']['AFNames'] # Use airfoil data from FAST file read, assumes AeroDyn 15, assumes 1 Re num per airfoil af_dict = {} for i, section in enumerate(self.fast.fst_vt['AeroDyn15']['af_data']): Re = [section[0]['Re']] Alpha = section[0]['Alpha'] if section[0]['NumTabs'] > 1: # sections with flaps ref_tab = int(np.floor(section[0]['NumTabs']/2)) Cl = section[ref_tab]['Cl'] Cd = section[ref_tab]['Cd'] Cm = section[ref_tab]['Cm'] af_dict[i] = CCAirfoil(Alpha, Re, Cl, Cd, Cm) else: # sections without flaps Cl = section[0]['Cl'] Cd = section[0]['Cd'] Cm = section[0]['Cm'] af_dict[i] = CCAirfoil(Alpha, Re, Cl, Cd, Cm) # define airfoils for CCBlade af = [0]*len(r) for i in range(len(r)): af[i] = af_dict[af_idx[i]] # Now save the CC-Blade rotor nSector = 8 # azimuthal discretizations self.cc_rotor = CCBlade(r, chord, theta, af, self.Rhub, self.rotor_radius, self.NumBl, rho=self.rho, mu=self.mu, precone=-self.precone, tilt=-self.tilt, yaw=self.yaw, shearExp=self.shearExp, hubHt=self.hubHt, nSector=nSector) # Save some blade data self.af_data = self.fast.fst_vt['AeroDyn15']['af_data'] self.span = r self.chord = chord self.twist = theta self.bld_flapwise_damp = self.fast.fst_vt['ElastoDynBlade']['BldFlDmp1']/100 * 0.7
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): # Create Airfoil class instances af = [None] * self.n_span for i in range(self.n_span): if self.n_tab > 1: ref_tab = int(np.floor(self.n_tab / 2)) af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, ref_tab], inputs['airfoils_cd'][i, :, :, ref_tab], inputs['airfoils_cm'][i, :, :, ref_tab]) else: af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, 0], inputs['airfoils_cd'][i, :, :, 0], inputs['airfoils_cm'][i, :, :, 0]) n_pitch = self.n_pitch n_tsr = self.n_tsr n_U = self.n_U min_TSR = self.min_TSR max_TSR = self.max_TSR min_pitch = self.min_pitch max_pitch = self.max_pitch U_vector = inputs['U_vector_in'] V_in = inputs['v_min'] V_out = inputs['v_max'] tsr_vector = inputs['tsr_vector_in'] pitch_vector = inputs['pitch_vector_in'] self.ccblade = CCBlade( inputs['r'], inputs['chord'], inputs['theta'], af, inputs['Rhub'], inputs['Rtip'], discrete_inputs['nBlades'], inputs['rho'], inputs['mu'], inputs['precone'], inputs['tilt'], inputs['yaw'], inputs['shearExp'], inputs['hub_height'], discrete_inputs['nSector'], inputs['precurve'], inputs['precurveTip'], inputs['presweep'], inputs['presweepTip'], discrete_inputs['tiploss'], discrete_inputs['hubloss'], discrete_inputs['wakerotation'], discrete_inputs['usecd']) if max(U_vector) == 0.: U_vector = np.linspace(V_in[0], V_out[0], n_U) if max(tsr_vector) == 0.: tsr_vector = np.linspace(min_TSR, max_TSR, n_tsr) if max(pitch_vector) == 0.: pitch_vector = np.linspace(min_pitch, max_pitch, n_pitch) outputs['pitch_vector'] = pitch_vector outputs['tsr_vector'] = tsr_vector outputs['U_vector'] = U_vector R = inputs['Rtip'] k = 0 for i in range(n_U): for j in range(n_tsr): k += 1 # if k/2. == int(k/2.) : print('Cp-Ct-Cq surfaces completed at ' + str(int(k / (n_U * n_tsr) * 100.)) + ' %') U = U_vector[i] * np.ones(n_pitch) Omega = tsr_vector[j] * U_vector[ i] / R * 30. / np.pi * np.ones(n_pitch) myout, _ = self.ccblade.evaluate(U, Omega, pitch_vector, coefficients=True) outputs['Cp'][j, :, i], outputs['Ct'][j, :, i], outputs['Cq'][ j, :, i] = [myout[key] for key in ['CP', 'CT', 'CQ']]
def load_from_ccblade(self): ''' Loads rotor performance information by running cc-blade aerodynamic analysis. Designed to work with Aerodyn15 blade input files. Parameters: ----------- fast: dict Dictionary containing fast model details - defined using from InputReader_OpenFAST (distributed as a part of AeroelasticSE) ''' print('Loading rotor performance data from CC-Blade.') # Create CC-Blade Rotor r0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlSpn']) chord0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlChord']) theta0 = np.array(self.fast.fst_vt['AeroDynBlade']['BlTwist']) # -- Adjust for Aerodyn15 r = r0 + self.Rhub chord_intfun = interpolate.interp1d(r0,chord0, bounds_error=None, fill_value='extrapolate', kind='zero') chord = chord_intfun(r) theta_intfun = interpolate.interp1d(r0,theta0, bounds_error=None, fill_value='extrapolate', kind='zero') theta = theta_intfun(r) af_idx = np.array(self.fast.fst_vt['AeroDynBlade']['BlAFID']).astype(int) - 1 #Reset to 0 index AFNames = self.fast.fst_vt['AeroDyn15']['AFNames'] # Use airfoil data from FAST file read, assumes AeroDyn 15, assumes 1 Re num per airfoil af_dict = {} try: for i, _ in enumerate(self.fast.fst_vt['AeroDyn15']['af_data']): Re = [self.fast.fst_vt['AeroDyn15']['af_data'][i][0]['Re']] Alpha = self.fast.fst_vt['AeroDyn15']['af_data'][i][0]['Alpha'] Cl = self.fast.fst_vt['AeroDyn15']['af_data'][i][0]['Cl'] Cd = self.fast.fst_vt['AeroDyn15']['af_data'][i][0]['Cd'] Cm = self.fast.fst_vt['AeroDyn15']['af_data'][i][0]['Cm'] af_dict[i] = CCAirfoil(Alpha, Re, Cl, Cd, Cm) except: # Read airfoil tables without tab cabalities (will remove once wisdem master branch cleans up) for i, _ in enumerate(self.fast.fst_vt['AeroDyn15']['af_data']): Re = [self.fast.fst_vt['AeroDyn15']['af_data'][i]['Re']] Alpha = self.fast.fst_vt['AeroDyn15']['af_data'][i]['Alpha'] Cl = self.fast.fst_vt['AeroDyn15']['af_data'][i]['Cl'] Cd = self.fast.fst_vt['AeroDyn15']['af_data'][i]['Cd'] Cm = self.fast.fst_vt['AeroDyn15']['af_data'][i]['Cm'] af_dict[i] = CCAirfoil(Alpha, Re, Cl, Cd, Cm) # define airfoils for CCBlade af = [0]*len(r) for i in range(len(r)): af[i] = af_dict[af_idx[i]] # Now save the CC-Blade rotor nSector = 8 # azimuthal discretizations self.cc_rotor = CCBlade(r, chord, theta, af, self.Rhub, self.rotor_radius, self.NumBl, rho=self.rho, mu=self.mu, precone=-self.precone, tilt=-self.tilt, yaw=self.yaw, shearExp=self.shearExp, hubHt=self.hubHt, nSector=nSector) print('CCBlade initiated successfully.') # Generate the look-up tables, mesh the grid and flatten the arrays for cc_rotor aerodynamic analysis TSR_initial = np.arange(3, 15,0.5) pitch_initial = np.arange(-1,25,0.5) pitch_initial_rad = pitch_initial * deg2rad ws_array = np.ones_like(TSR_initial) * self.v_rated # evaluate at rated wind speed omega_array = (TSR_initial * ws_array / self.rotor_radius) * RadSec2rpm ws_mesh, pitch_mesh = np.meshgrid(ws_array, pitch_initial) ws_flat = ws_mesh.flatten() pitch_flat = pitch_mesh.flatten() omega_mesh, _ = np.meshgrid(omega_array, pitch_initial) omega_flat = omega_mesh.flatten() # tsr_flat = (omega_flat * rpm2RadSec * self.rotor_radius) / ws_flat # Get values from cc-blade print('Running CCBlade aerodynamic analysis, this may take a minute...') try: _, _, _, _, CP, CT, CQ, _ = self.cc_rotor.evaluate(ws_flat, omega_flat, pitch_flat, coefficients=True) except ValueError: # On IEAontology4all outputs, derivs = self.cc_rotor.evaluate(ws_flat, omega_flat, pitch_flat, coefficients=True) CP = outputs['CP'] CT = outputs['CT'] CQ = outputs['CQ'] print('CCBlade aerodynamic analysis run successfully.') # Reshape Cp, Ct and Cq Cp = np.transpose(np.reshape(CP, (len(pitch_initial), len(TSR_initial)))) Ct = np.transpose(np.reshape(CT, (len(pitch_initial), len(TSR_initial)))) Cq = np.transpose(np.reshape(CQ, (len(pitch_initial), len(TSR_initial)))) # Store necessary metrics for analysis self.pitch_initial_rad = pitch_initial_rad self.TSR_initial = TSR_initial self.Cp_table = Cp self.Ct_table = Ct self.Cq_table = Cq # Save some blade parameters self.span = r self.chord = chord self.twist = theta
])) airfoils = input_yaml['airfoils'] cl = np.zeros((len(s), n_aoa, 1, 1)) cd = np.zeros((len(s), n_aoa, 1, 1)) cm = np.zeros((len(s), n_aoa, 1, 1)) for i in range(len(s)): cl[i, :, 0, 0] = np.interp(aoa, airfoils[i]['polars'][0]['c_l']['grid'], airfoils[i]['polars'][0]['c_l']['values']) cd[i, :, 0, 0] = np.interp(aoa, airfoils[i]['polars'][0]['c_d']['grid'], airfoils[i]['polars'][0]['c_d']['values']) cm[i, :, 0, 0] = np.interp(aoa, airfoils[i]['polars'][0]['c_m']['grid'], airfoils[i]['polars'][0]['c_m']['values']) af = [None] * len(s) for i in range(len(s)): af[i] = CCAirfoil(np.rad2deg(aoa), [1e+6], cl[i, :, 0, 0], cd[i, :, 0, 0], cm[i, :, 0, 0]) ########## Run CCBlade ########## get_cp_cm = CCBlade(r, chord, twist, af, hub_r, Rtip, B, rho, mu, cone, tilt, 0., shearExp, hub_height, nSector, presweep, precurve[-1], presweep, presweep[-1], tiploss, hubloss, wakerotation, usecd) # get_cp_cm.induction = True Omega = Uhub * tsr / Rtip * 30.0 / np.pi # Rotor speed myout, derivs = get_cp_cm.evaluate([Uhub], [Omega], [pitch], coefficients=True) P, T, Q, M, CP, CT, CQ, CM = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] get_cp_cm.induction_inflow = True loads, deriv = get_cp_cm.distributedAeroLoads([Uhub], Omega, pitch, 0.)
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): ''' Call ROSCO toolbox to define controller ''' # Add control tuning parameters to dictionary self.modeling_options['servose']['omega_pc'] = inputs['PC_omega'] self.modeling_options['servose']['zeta_pc'] = inputs['PC_zeta'] self.modeling_options['servose']['omega_vs'] = inputs['VS_omega'] self.modeling_options['servose']['zeta_vs'] = inputs['VS_zeta'] if self.modeling_options['servose']['Flp_Mode'] > 0: self.modeling_options['servose']['omega_flp'] = inputs['Flp_omega'] self.modeling_options['servose']['zeta_flp'] = inputs['Flp_zeta'] else: self.modeling_options['servose']['omega_flp'] = 0.0 self.modeling_options['servose']['zeta_flp'] = 0.0 # self.modeling_options['servose']['max_pitch'] = inputs['max_pitch'][0] self.modeling_options['servose']['min_pitch'] = inputs['min_pitch'][0] self.modeling_options['servose']['vs_minspd'] = inputs['vs_minspd'][0] self.modeling_options['servose']['ss_vsgain'] = inputs['ss_vsgain'][0] self.modeling_options['servose']['ss_pcgain'] = inputs['ss_pcgain'][0] self.modeling_options['servose']['ps_percent'] = inputs['ps_percent'][ 0] if self.modeling_options['servose']['Flp_Mode'] > 0: self.modeling_options['servose']['flp_maxpit'] = inputs[ 'delta_max_pos'][0] else: self.modeling_options['servose']['flp_maxpit'] = None # self.modeling_options['servose']['ss_cornerfreq'] = None self.modeling_options['servose']['sd_maxpit'] = None self.modeling_options['servose']['sd_cornerfreq'] = None # Define necessary turbine parameters WISDEM_turbine = type('', (), {})() WISDEM_turbine.v_min = inputs['v_min'][0] WISDEM_turbine.J = inputs['rotor_inertia'][0] WISDEM_turbine.rho = inputs['rho'][0] WISDEM_turbine.rotor_radius = inputs['R'][0] WISDEM_turbine.Ng = inputs['gear_ratio'][0] WISDEM_turbine.GenEff = inputs['generator_efficiency'][0] * 100. WISDEM_turbine.GBoxEff = inputs['gearbox_efficiency'][0] * 100. WISDEM_turbine.rated_rotor_speed = inputs['rated_rotor_speed'][0] WISDEM_turbine.rated_power = inputs['rated_power'][0] WISDEM_turbine.rated_torque = inputs['rated_torque'][ 0] / WISDEM_turbine.Ng * inputs['gearbox_efficiency'][0] WISDEM_turbine.v_rated = inputs['v_rated'][0] WISDEM_turbine.v_min = inputs['v_min'][0] WISDEM_turbine.v_max = inputs['v_max'][0] WISDEM_turbine.max_pitch_rate = inputs['max_pitch_rate'][0] WISDEM_turbine.TSR_operational = inputs['tsr_operational'][0] WISDEM_turbine.max_torque_rate = inputs['max_torque_rate'][0] # Load Cp tables self.Cp_table = inputs['Cp_table'] self.Ct_table = inputs['Ct_table'] self.Cq_table = inputs['Cq_table'] self.pitch_vector = WISDEM_turbine.pitch_initial_rad = inputs[ 'pitch_vector'] self.tsr_vector = WISDEM_turbine.TSR_initial = inputs['tsr_vector'] self.Cp_table = WISDEM_turbine.Cp_table = self.Cp_table.reshape( len(self.pitch_vector), len(self.tsr_vector)) self.Ct_table = WISDEM_turbine.Ct_table = self.Ct_table.reshape( len(self.pitch_vector), len(self.tsr_vector)) self.Cq_table = WISDEM_turbine.Cq_table = self.Cq_table.reshape( len(self.pitch_vector), len(self.tsr_vector)) RotorPerformance = ROSCO_turbine.RotorPerformance WISDEM_turbine.Cp = RotorPerformance(self.Cp_table, self.pitch_vector, self.tsr_vector) WISDEM_turbine.Ct = RotorPerformance(self.Ct_table, self.pitch_vector, self.tsr_vector) WISDEM_turbine.Cq = RotorPerformance(self.Cq_table, self.pitch_vector, self.tsr_vector) # Load blade info to pass to flap controller tuning process if self.modeling_options['servose']['Flp_Mode'] >= 1: # Create airfoils af = [None] * self.n_span for i in range(self.n_span): if self.n_tab > 1: ref_tab = int(np.floor(self.n_tab / 2)) af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, ref_tab], inputs['airfoils_cd'][i, :, :, ref_tab], inputs['airfoils_cm'][i, :, :, ref_tab]) else: af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, 0], inputs['airfoils_cd'][i, :, :, 0], inputs['airfoils_cm'][i, :, :, 0]) # Initialize CCBlade as cc_rotor object WISDEM_turbine.cc_rotor = CCBlade( inputs['r'], inputs['chord'], inputs['theta'], af, inputs['Rhub'], inputs['Rtip'], discrete_inputs['nBlades'], inputs['rho'], inputs['mu'], inputs['precone'], inputs['tilt'], inputs['yaw'], inputs['shearExp'], inputs['hub_height'], discrete_inputs['nSector'], inputs['precurve'], inputs['precurveTip'], inputs['presweep'], inputs['presweepTip'], discrete_inputs['tiploss'], discrete_inputs['hubloss'], discrete_inputs['wakerotation'], discrete_inputs['usecd']) # Load aerodynamic performance data for blades WISDEM_turbine.af_data = [{} for i in range(self.n_span)] for i in range(self.n_span): # Check number of flap positions for each airfoil section if self.n_tab > 1: if inputs['airfoils_Ctrl'][ i, 0, 0] == inputs['airfoils_Ctrl'][i, 0, 1]: n_tabs = 1 # If all Ctrl angles of the flaps are identical then no flaps else: n_tabs = self.n_tab else: n_tabs = 1 # Save data for each flap position for j in range(n_tabs): WISDEM_turbine.af_data[i][j] = {} WISDEM_turbine.af_data[i][j]['NumTabs'] = n_tabs WISDEM_turbine.af_data[i][j]['Ctrl'] = inputs[ 'airfoils_Ctrl'][i, 0, j] WISDEM_turbine.af_data[i][j]['Alpha'] = np.array( inputs['airfoils_aoa']).flatten().tolist() WISDEM_turbine.af_data[i][j]['Cl'] = np.array( inputs['airfoils_cl'][i, :, 0, j]).flatten().tolist() WISDEM_turbine.af_data[i][j]['Cd'] = np.array( inputs['airfoils_cd'][i, :, 0, j]).flatten().tolist() WISDEM_turbine.af_data[i][j]['Cm'] = np.array( inputs['airfoils_cm'][i, :, 0, j]).flatten().tolist() # Save some more airfoil info WISDEM_turbine.span = inputs['r'] WISDEM_turbine.chord = inputs['chord'] WISDEM_turbine.twist = inputs['theta'] WISDEM_turbine.bld_flapwise_freq = inputs['flap_freq'][ 0] * 2 * np.pi WISDEM_turbine.bld_flapwise_damp = self.modeling_options[ 'openfast']['fst_vt']['ElastoDynBlade']['BldFlDmp1'] / 100 * 0.7 # Tune Controller! controller = ROSCO_controller.Controller( self.modeling_options['servose']) controller.tune_controller(WISDEM_turbine) # DISCON Parameters # - controller if 'DISCON_in' not in self.modeling_options['openfast']['fst_vt'].keys( ): self.modeling_options['openfast']['fst_vt']['DISCON_in'] = {} self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'LoggingLevel'] = controller.LoggingLevel self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_LPFType'] = controller.F_LPFType self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_NotchType'] = controller.F_NotchType self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'IPC_ControlMode'] = controller.IPC_ControlMode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_ControlMode'] = controller.VS_ControlMode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_ControlMode'] = controller.PC_ControlMode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Y_ControlMode'] = controller.Y_ControlMode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SS_Mode'] = controller.SS_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_Mode'] = controller.WE_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PS_Mode'] = controller.PS_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SD_Mode'] = controller.SD_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Fl_Mode'] = controller.Fl_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Flp_Mode'] = controller.Flp_Mode self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_LPFDamping'] = controller.F_LPFDamping self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_SSCornerFreq'] = controller.ss_cornerfreq self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_GS_angles'] = controller.pitch_op_pc self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_GS_KP'] = controller.pc_gain_schedule.Kp self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_GS_KI'] = controller.pc_gain_schedule.Ki self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_MaxPit'] = controller.max_pitch self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_MinPit'] = controller.min_pitch self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_MinOMSpd'] = controller.vs_minspd self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_Rgn2K'] = controller.vs_rgn2K self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_RefSpd'] = controller.vs_refspd self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_KP'] = controller.vs_gain_schedule.Kp self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_KI'] = controller.vs_gain_schedule.Ki self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SS_VSGain'] = controller.ss_vsgain self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SS_PCGain'] = controller.ss_pcgain self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_FOPoles_N'] = len(controller.v) self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_FOPoles_v'] = controller.v self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_FOPoles'] = controller.A self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'ps_wind_speeds'] = controller.v self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PS_BldPitchMin'] = controller.ps_min_bld_pitch self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SD_MaxPit'] = controller.sd_maxpit + 0.1 self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'SD_CornerFreq'] = controller.sd_cornerfreq self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Fl_Kp'] = controller.Kp_float self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Flp_Kp'] = controller.Kp_flap self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Flp_Ki'] = controller.Ki_flap self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Flp_MaxPit'] = controller.flp_maxpit self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Flp_Angle'] = 0. # - turbine self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_BladeRadius'] = WISDEM_turbine.rotor_radius self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'v_rated'] = inputs['v_rated'][0] self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_FlpCornerFreq'] = [ inputs['flap_freq'][0] * 2 * np.pi / 3., 0.7 ] self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_LPFCornerFreq'] = inputs['edge_freq'][0] * 2 * np.pi / 4. self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_NotchCornerFreq'] = 0.0 # inputs(['twr_freq']) # zero for now, fix when floating introduced to WISDEM self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'F_FlCornerFreq'] = [ 0.0, 0.0 ] # inputs(['ptfm_freq']) # zero for now, fix when floating introduced to WISDEM self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_MaxRat'] = WISDEM_turbine.max_pitch_rate self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_MinRat'] = -WISDEM_turbine.max_pitch_rate self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_MaxRat'] = WISDEM_turbine.max_torque_rate self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'PC_RefSpd'] = WISDEM_turbine.rated_rotor_speed * WISDEM_turbine.Ng self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_RtPwr'] = WISDEM_turbine.rated_power self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_RtTq'] = WISDEM_turbine.rated_torque self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_MaxTq'] = WISDEM_turbine.rated_torque * 1.1 self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'VS_TSRopt'] = WISDEM_turbine.TSR_operational self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_RhoAir'] = WISDEM_turbine.rho self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_GearboxRatio'] = WISDEM_turbine.Ng self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'WE_Jtot'] = WISDEM_turbine.J self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cp_pitch_initial_rad'] = self.pitch_vector self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cp_TSR_initial'] = self.tsr_vector self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cp_table'] = WISDEM_turbine.Cp_table self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Ct_table'] = WISDEM_turbine.Ct_table self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cq_table'] = WISDEM_turbine.Cq_table self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cp'] = WISDEM_turbine.Cp self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Ct'] = WISDEM_turbine.Ct self.modeling_options['openfast']['fst_vt']['DISCON_in'][ 'Cq'] = WISDEM_turbine.Cq # Outputs outputs['Flp_Kp'] = controller.Kp_flap[-1] outputs['Flp_Ki'] = controller.Ki_flap[-1]
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): r = inputs['r'] chord = inputs['chord'] theta = inputs['theta'] Rhub = inputs['Rhub'] Rtip = inputs['Rtip'] hub_height = inputs['hub_height'] precone = inputs['precone'] tilt = inputs['tilt'] yaw = inputs['yaw'] precurve = inputs['precurve'] precurveTip = inputs['precurveTip'] # airfoils = discrete_inputs['airfoils'] B = discrete_inputs['nBlades'] rho = inputs['rho'] mu = inputs['mu'] shearExp = inputs['shearExp'] nSector = discrete_inputs['nSector'] tiploss = discrete_inputs['tiploss'] hubloss = discrete_inputs['hubloss'] wakerotation = discrete_inputs['wakerotation'] usecd = discrete_inputs['usecd'] V_load = inputs['V_load'] Omega_load = inputs['Omega_load'] pitch_load = inputs['pitch_load'] azimuth_load = inputs['azimuth_load'] if len(precurve) == 0: precurve = np.zeros_like(r) # airfoil files af = [None] * self.n_span for i in range(self.n_span): af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, 0], inputs['airfoils_cd'][i, :, :, 0], inputs['airfoils_cm'][i, :, :, 0]) ccblade = CCBlade(r, chord, theta, af, Rhub, Rtip, B, rho, mu, precone, tilt, yaw, shearExp, hub_height, nSector, precurve, precurveTip, tiploss=tiploss, hubloss=hubloss, wakerotation=wakerotation, usecd=usecd, derivatives=True) # distributed loads loads, self.derivs = ccblade.distributedAeroLoads( V_load, Omega_load, pitch_load, azimuth_load) Np = loads['Np'] Tp = loads['Tp'] # concatenate loads at root/tip outputs['loads_r'] = r # conform to blade-aligned coordinate system outputs['loads_Px'] = Np outputs['loads_Py'] = -Tp outputs['loads_Pz'] = 0 * Np
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): r = inputs['r'] chord = inputs['chord'] theta = inputs['theta'] Rhub = inputs['Rhub'] Rtip = inputs['Rtip'] hub_height = inputs['hub_height'] precone = inputs['precone'] tilt = inputs['tilt'] yaw = inputs['yaw'] precurve = inputs['precurve'] precurveTip = inputs['precurveTip'] # airfoils = discrete_inputs['airfoils'] B = discrete_inputs['nBlades'] rho = inputs['rho'] mu = inputs['mu'] shearExp = inputs['shearExp'] nSector = discrete_inputs['nSector'] tiploss = discrete_inputs['tiploss'] hubloss = discrete_inputs['hubloss'] wakerotation = discrete_inputs['wakerotation'] usecd = discrete_inputs['usecd'] Uhub = inputs['Uhub'] Omega = inputs['Omega'] pitch = inputs['pitch'] af = [None] * self.naero for i in range(self.naero): af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][:, i, :], inputs['airfoils_cd'][:, i, :], inputs['airfoils_cm'][:, i, :]) ccblade = CCBlade(r, chord, theta, af, Rhub, Rtip, B, rho, mu, precone, tilt, yaw, shearExp, hub_height, nSector, precurve, precurveTip, tiploss=tiploss, hubloss=hubloss, wakerotation=wakerotation, usecd=usecd, derivatives=True) # power, thrust, torque myoutputs, self.derivs = ccblade.evaluate(Uhub, Omega, pitch, coefficients=False) outputs['T'] = myoutputs['T'] outputs['Q'] = myoutputs['Q'] outputs['P'] = myoutputs['P']
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): # Create Airfoil class instances af = [None] * self.n_span for i in range(self.n_span): if self.n_tab > 1: ref_tab = int(np.floor(self.n_tab / 2)) af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, ref_tab], inputs['airfoils_cd'][i, :, :, ref_tab], inputs['airfoils_cm'][i, :, :, ref_tab]) else: af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][i, :, :, 0], inputs['airfoils_cd'][i, :, :, 0], inputs['airfoils_cm'][i, :, :, 0]) self.ccblade = CCBlade( inputs['r'], inputs['chord'], inputs['theta'], af, inputs['Rhub'], inputs['Rtip'], discrete_inputs['nBlades'], inputs['rho'], inputs['mu'], inputs['precone'], inputs['tilt'], inputs['yaw'], inputs['shearExp'], inputs['hub_height'], discrete_inputs['nSector'], inputs['precurve'], inputs['precurveTip'], inputs['presweep'], inputs['presweepTip'], discrete_inputs['tiploss'], discrete_inputs['hubloss'], discrete_inputs['wakerotation'], discrete_inputs['usecd']) # JPJ: what is this grid for? Seems to be a special distribution of velocities # for the hub grid0 = np.cumsum( np.abs( np.diff( np.cos(np.linspace(-np.pi / 4., np.pi / 2., self.n_pc + 1))))) grid1 = (grid0 - grid0[0]) / (grid0[-1] - grid0[0]) Uhub = grid1 * (inputs['v_max'] - inputs['v_min']) + inputs['v_min'] P_aero = np.zeros(Uhub.shape) Cp_aero = np.zeros(Uhub.shape) Ct_aero = np.zeros(Uhub.shape) Cq_aero = np.zeros(Uhub.shape) Cm_aero = np.zeros(Uhub.shape) P = np.zeros(Uhub.shape) Cp = np.zeros(Uhub.shape) T = np.zeros(Uhub.shape) Q = np.zeros(Uhub.shape) M = np.zeros(Uhub.shape) pitch = np.zeros(Uhub.shape) + inputs['control_pitch'] # Unpack variables P_rated = inputs['rated_power'] R_tip = inputs['Rtip'] tsr = inputs['tsr_operational'] driveType = discrete_inputs['drivetrainType'] driveEta = inputs['gearbox_efficiency'] * inputs['generator_efficiency'] # Set rotor speed based on TSR Omega_tsr = Uhub * tsr / R_tip # Determine maximum rotor speed (rad/s)- either by TS or by control input Omega_max = min([ inputs['control_maxTS'] / R_tip, inputs['omega_max'] * np.pi / 30. ]) # Apply maximum and minimum rotor speed limits Omega = np.maximum(np.minimum(Omega_tsr, Omega_max), inputs['omega_min'] * np.pi / 30.) Omega_rpm = Omega * 30. / np.pi # Set baseline power production myout, derivs = self.ccblade.evaluate(Uhub, Omega_rpm, pitch, coefficients=True) P_aero, T, Q, M, Cp_aero, Ct_aero, Cq_aero, Cm_aero = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] P, eff = compute_P_and_eff(P_aero, P_rated, driveType, driveEta) Cp = Cp_aero * eff # Find Region 3 index region_bool = np.nonzero(P >= P_rated)[0] if len(region_bool) == 0: i_3 = self.n_pc region3 = False else: i_3 = region_bool[0] + 1 region3 = True # Guess at Region 2.5, but we will do a more rigorous search below if Omega_max < Omega_tsr[-1]: U_2p5 = np.interp(Omega[-1], Omega_tsr, Uhub) outputs['V_R25'] = U_2p5 else: U_2p5 = Uhub[-1] i_2p5 = np.nonzero(U_2p5 <= Uhub)[0][0] # Find rated index and guess at rated speed if P_aero[-1] > P_rated: U_rated = np.interp(P_rated, P_aero * eff, Uhub) else: U_rated = Uhub[-1] i_rated = np.nonzero(U_rated <= Uhub)[0][0] # Function to be used inside of power maximization until Region 3 def maximizePower(pitch, Uhub, Omega_rpm): myout, _ = self.ccblade.evaluate([Uhub], [Omega_rpm], [pitch], coefficients=False) return -myout['P'] # Maximize power until Region 3 region2p5 = False for i in range(i_3): # No need to optimize if already doing well if Omega[i] == Omega_tsr[i]: continue # Find pitch value that gives highest power rating pitch0 = pitch[i] if i == 0 else pitch[i - 1] bnds = [pitch0 - 10., pitch0 + 10.] pitch[i] = minimize_scalar( lambda x: maximizePower(x, Uhub[i], Omega_rpm[i]), bounds=bnds, method='bounded', options={ 'disp': False, 'xatol': 1e-2, 'maxiter': 40 })['x'] # Find associated power myout, _ = self.ccblade.evaluate([Uhub[i]], [Omega_rpm[i]], [pitch[i]], coefficients=True) P_aero[i], T[i], Q[i], M[i], Cp_aero[i], Ct_aero[i], Cq_aero[ i], Cm_aero[i] = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] P[i], eff = compute_P_and_eff(P_aero[i], P_rated, driveType, driveEta) Cp[i] = Cp_aero[i] * eff # Note if we find Region 2.5 if ((not region2p5) and (Omega[i] == Omega_max) and (P[i] < P_rated)): region2p5 = True i_2p5 = i # Stop if we find Region 3 early if P[i] > P_rated: i_3 = i + 1 i_rated = i break # Solve for rated velocity # JPJ: why rename i_rated to i here? It removes clarity in the following 50 lines that we're looking at the rated properties i = i_rated if i < self.n_pc - 1: def const_Urated(x): pitch = x[0] Uhub_i = x[1] Omega_i = min([Uhub_i * tsr / R_tip, Omega_max]) myout, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) P_aero_i = myout['P'] P_i, eff = compute_P_and_eff(P_aero_i.flatten(), P_rated, driveType, driveEta) return (P_i - P_rated) if region2p5: # Have to search over both pitch and speed x0 = [0.0, Uhub[i]] bnds = [ np.sort([pitch[i - 1], pitch[i + 1]]), [Uhub[i - 1], Uhub[i + 1]] ] const = {} const['type'] = 'eq' const['fun'] = const_Urated params_rated = minimize(lambda x: x[1], x0, method='slsqp', bounds=bnds, constraints=const, tol=1e-3) if params_rated.success and not np.isnan(params_rated.x[1]): U_rated = params_rated.x[1] pitch[i] = params_rated.x[0] else: U_rated = U_rated # Use guessed value earlier pitch[i] = 0.0 else: # Just search over speed pitch[i] = 0.0 try: U_rated = brentq(lambda x: const_Urated([0.0, x]), Uhub[i - 1], Uhub[i + 1], xtol=1e-4, rtol=1e-5, maxiter=40, disp=False) except ValueError: U_rated = minimize_scalar( lambda x: np.abs(const_Urated([0.0, x])), bounds=[Uhub[i - 1], Uhub[i + 1]], method='bounded', options={ 'disp': False, 'xatol': 1e-3, 'maxiter': 40 })['x'] Omega_rated = min([U_rated * tsr / R_tip, Omega_max]) Omega[i:] = np.minimum( Omega[i:], Omega_rated) # Stay at this speed if hit rated too early Omega_rpm = Omega * 30. / np.pi myout, _ = self.ccblade.evaluate([U_rated], [Omega_rpm[i]], [pitch[i]], coefficients=True) P_aero[i], T[i], Q[i], M[i], Cp_aero[i], Ct_aero[i], Cq_aero[ i], Cm_aero[i] = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] P[i], eff = compute_P_and_eff(P_aero[i], P_rated, driveType, driveEta) Cp[i] = Cp_aero[i] * eff P[i] = P_rated # Store rated speed in array Uhub[i_rated] = U_rated # Store outputs outputs['rated_V'] = np.float64(U_rated) outputs['rated_Omega'] = Omega_rpm[i] outputs['rated_pitch'] = pitch[i] outputs['rated_T'] = T[i] outputs['rated_Q'] = Q[i] # JPJ: this part can be converted into a BalanceComp with a solver. # This will be less expensive and allow us to get derivatives through the process. if region3: # Function to be used to stay at rated power in Region 3 def rated_power_dist(pitch, Uhub, Omega_rpm): myout, _ = self.ccblade.evaluate([Uhub], [Omega_rpm], [pitch], coefficients=False) P_aero = myout['P'] P, eff = compute_P_and_eff(P_aero, P_rated, driveType, driveEta) return (P - P_rated) # Solve for Region 3 pitch options = {'disp': False} if self.regulation_reg_III: for i in range(i_3, self.n_pc): pitch0 = pitch[i - 1] try: pitch[i] = brentq(lambda x: rated_power_dist( x, Uhub[i], Omega_rpm[i]), pitch0, pitch0 + 10., xtol=1e-4, rtol=1e-5, maxiter=40, disp=False) except ValueError: pitch[i] = minimize_scalar( lambda x: np.abs( rated_power_dist(x, Uhub[i], Omega_rpm[i])), bounds=[pitch0 - 5., pitch0 + 15.], method='bounded', options={ 'disp': False, 'xatol': 1e-3, 'maxiter': 40 })['x'] myout, _ = self.ccblade.evaluate([Uhub[i]], [Omega_rpm[i]], [pitch[i]], coefficients=True) P_aero[i], T[i], Q[i], M[i], Cp_aero[i], Ct_aero[ i], Cq_aero[i], Cm_aero[i] = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] P[i], eff = compute_P_and_eff(P_aero[i], P_rated, driveType, driveEta) Cp[i] = Cp_aero[i] * eff #P[i] = P_rated else: P[i_3:] = P_rated T[i_3:] = 0 Q[i_3:] = P[i_3:] / Omega[i_3:] M[i_3:] = 0 pitch[i_3:] = 0 Cp[i_3:] = P[i_3:] / (0.5 * inputs['rho'] * np.pi * R_tip**2 * Uhub[i_3:]**3) Ct_aero[i_3:] = 0 Cq_aero[i_3:] = 0 Cm_aero[i_3:] = 0 outputs['T'] = T outputs['Q'] = Q outputs['Omega'] = Omega_rpm outputs['P'] = P outputs['Cp'] = Cp outputs['Cp_aero'] = Cp_aero outputs['Ct_aero'] = Ct_aero outputs['Cq_aero'] = Cq_aero outputs['Cm_aero'] = Cm_aero outputs['V'] = Uhub outputs['M'] = M outputs['pitch'] = pitch self.ccblade.induction_inflow = True tsr_vec = Omega_rpm / 30. * np.pi * R_tip / Uhub id_regII = np.argmin(abs(tsr_vec - inputs['tsr_operational'])) loads, derivs = self.ccblade.distributedAeroLoads( Uhub[id_regII], Omega_rpm[id_regII], pitch[id_regII], 0.0) # outputs outputs['ax_induct_regII'] = loads['a'] outputs['tang_induct_regII'] = loads['ap'] outputs['aoa_regII'] = loads['alpha'] outputs['cl_regII'] = loads['Cl'] outputs['cd_regII'] = loads['Cd'] outputs['Cp_regII'] = Cp_aero[id_regII]
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): self.r = inputs['r'] self.chord = inputs['chord'] self.theta = inputs['theta'] self.Rhub = inputs['Rhub'] self.Rtip = inputs['Rtip'] self.hub_height = inputs['hub_height'] self.precone = inputs['precone'] self.tilt = inputs['tilt'] self.yaw = inputs['yaw'] self.precurve = inputs['precurve'] self.precurveTip = inputs['precurveTip'] # self.airfoils = discrete_inputs['airfoils'] self.B = discrete_inputs['nBlades'] self.rho = inputs['rho'] self.mu = inputs['mu'] self.shearExp = inputs['shearExp'] self.nSector = discrete_inputs['nSector'] self.tiploss = discrete_inputs['tiploss'] self.hubloss = discrete_inputs['hubloss'] self.wakerotation = discrete_inputs['wakerotation'] self.usecd = discrete_inputs['usecd'] self.V_load = inputs['V_load'] self.Omega_load = inputs['Omega_load'] self.pitch_load = inputs['pitch_load'] self.azimuth_load = inputs['azimuth_load'] if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files # n = len(self.airfoils) af = [None] * self.naero for i in range(self.naero): af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][:, i, :], inputs['airfoils_cd'][:, i, :], inputs['airfoils_cm'][:, i, :]) # af = self.airfoils self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hub_height, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip outputs['loads_r'] = self.r # conform to blade-aligned coordinate system outputs['loads_Px'] = Np outputs['loads_Py'] = -Tp outputs['loads_Pz'] = 0 * Np # return other outputs needed outputs['loads_V'] = self.V_load outputs['loads_Omega'] = self.Omega_load outputs['loads_pitch'] = self.pitch_load outputs['loads_azimuth'] = self.azimuth_load '''
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): self.r = inputs['r'] self.chord = inputs['chord'] self.theta = inputs['theta'] self.Rhub = inputs['Rhub'] self.Rtip = inputs['Rtip'] self.hub_height = inputs['hub_height'] self.precone = inputs['precone'] self.tilt = inputs['tilt'] self.yaw = inputs['yaw'] self.precurve = inputs['precurve'] self.precurveTip = inputs['precurveTip'] # self.airfoils = discrete_inputs['airfoils'] self.B = discrete_inputs['nBlades'] self.rho = inputs['rho'] self.mu = inputs['mu'] self.shearExp = inputs['shearExp'] self.nSector = discrete_inputs['nSector'] self.tiploss = discrete_inputs['tiploss'] self.hubloss = discrete_inputs['hubloss'] self.wakerotation = discrete_inputs['wakerotation'] self.usecd = discrete_inputs['usecd'] self.Uhub = inputs['Uhub'] self.Omega = inputs['Omega'] self.pitch = inputs['pitch'] af = [None] * self.naero for i in range(self.naero): af[i] = CCAirfoil(inputs['airfoils_aoa'], inputs['airfoils_Re'], inputs['airfoils_cl'][:, i, :], inputs['airfoils_cd'][:, i, :], inputs['airfoils_cm'][:, i, :]) self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hub_height, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # power, thrust, torque self.P, self.T, self.Q, self.M, self.dP, self.dT, self.dQ \ = self.ccblade.evaluate(self.Uhub, self.Omega, self.pitch, coefficients=False) outputs['T'] = self.T outputs['Q'] = self.Q outputs['P'] = self.P '''