Skip to content

Commit

Permalink
Move load_from_txt to utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhar-abbas committed Feb 4, 2020
1 parent 92060a5 commit 1e95510
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 55 deletions.
118 changes: 68 additions & 50 deletions ROSCO_toolbox/turbine.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import numpy as np
import datetime
from wisdem.ccblade import CCAirfoil, CCBlade
from wisdem.aeroelasticse.FAST_reader import InputReader_OpenFAST
from scipy import interpolate
from numpy import gradient
import pickle
import matplotlib.pyplot as plt

from ROSCO_toolbox import utilities as ROSCO_utilities

# Some useful constants
now = datetime.datetime.now()
pi = np.pi
Expand Down Expand Up @@ -158,6 +159,7 @@ def load_from_fast(self, FAST_InputFile,FAST_directory, FAST_ver='OpenFAST',dev_
txt_filename: str, optional
filename for *.txt, only used if rot_source='txt'
"""
from wisdem.aeroelasticse.FAST_reader import InputReader_OpenFAST

print('Loading FAST model: %s ' % FAST_InputFile)
self.TurbineName = FAST_InputFile.strip('.fst')
Expand Down Expand Up @@ -199,15 +201,15 @@ def load_from_fast(self, FAST_InputFile,FAST_directory, FAST_ver='OpenFAST',dev_

# Load Cp, Ct, Cq tables
if rot_source == 'txt':
self.load_from_txt(txt_filename)
self.pitch_initial_rad, self.TSR_initial, self.Cp_table, self.Ct_table, self.Cq_table = ROSCO_utilities.FileProcessing.load_from_txt(txt_filename)
elif rot_source == 'cc-blade':
self.load_from_ccblade(fast)
else: # default load from cc-blade
if txt_filename is None:
print('No rotor performance data source available, running CC-Blade.')
self.load_from_ccblade(fast)
elif os.path.exists(txt_filename):
self.load_from_txt(txt_filename)
self.pitch_initial_rad, self.TSR_initial, self.Cp_table, self.Ct_table, self.Cq_table = ROSCO_utilities.FileProcessing.load_from_txt(txt_filename)
else:
print('No rotor performance data source available, running CC-Blade.')
self.load_from_ccblade(fast)
Expand Down Expand Up @@ -305,57 +307,73 @@ def load_from_ccblade(self,fast):
self.Cp_table = Cp
self.Ct_table = Ct
self.Cq_table = Cq
def load_from_txt(self,txt_filename):

def load_blade_info(self, FAST_InputFile,FAST_directory, FAST_ver='OpenFAST',dev_branch=True):
'''
Load rotor performance data from a *.txt file.
Loads wind turbine blade data from an OpenFAST model
Parameters:
-----------
txt_filename: str
Filename of the text containing the Cp, Ct, and Cq data. This should be in the format printed by the write_rotorperformance function
Fast_InputFile: str
Primary fast model input file (*.fst)
FAST_directory: str
Directory for primary fast model input file
FAST_ver: string, optional
fast version, usually OpenFAST
dev_branch: bool, optional
dev_branch input to InputReader_OpenFAST, probably True
'''
print('Loading rotor performace data from text file:', txt_filename)

with open(txt_filename) as pfile:
for line in pfile:
# Read Blade Pitch Angles (degrees)
if 'Pitch angle' in line:
pitch_initial = np.array([float(x) for x in pfile.readline().strip().split()])
pitch_initial_rad = pitch_initial * deg2rad # degrees to rad -- should this be conditional?

# Read Tip Speed Ratios (rad)
if 'TSR' in line:
TSR_initial = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Power Coefficients
if 'Power' in line:
pfile.readline()
Cp = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Cp[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Thrust Coefficients
if 'Thrust' in line:
pfile.readline()
Ct = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Ct[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Torque Coefficients
if 'Torque' in line:
pfile.readline()
Cq = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Cq[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# Store necessary metrics for analysis and tuning
self.pitch_initial_rad = pitch_initial_rad
self.TSR_initial = TSR_initial
self.Cp_table = Cp
self.Ct_table = Ct
self.Cq_table = Cq

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)
fast.FAST_InputFile = FAST_InputFile
fast.FAST_directory = FAST_directory
fast.execute()

# Make sure cc_rotor exists for DAC analysis
try:
exists(self.cc_rotor)
except NameError:
# Create CC-Blade Rotor
r0 = np.array(fast.fst_vt['AeroDynBlade']['BlSpn'])
chord0 = np.array(fast.fst_vt['AeroDynBlade']['BlChord'])
theta0 = np.array(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(fast.fst_vt['AeroDynBlade']['BlAFID']).astype(int) - 1 #Reset to 0 index
AFNames = 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, _ in enumerate(fast.fst_vt['AeroDyn15']['af_data']):
Re = [fast.fst_vt['AeroDyn15']['af_data'][i][0]['Re']]
Alpha = fast.fst_vt['AeroDyn15']['af_data'][i][0]['Alpha']
Cl = fast.fst_vt['AeroDyn15']['af_data'][i][0]['Cl']
Cd = fast.fst_vt['AeroDyn15']['af_data'][i][0]['Cd']
Cm = fast.fst_vt['AeroDyn15']['af_data'][i][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 = fast.fst_vt['AeroDyn15']['af_data']
self.span = r
self.chord = chord
self.twist = theta
self.bld_flapwise_damp = fast.fst_vt['ElastoDynBlade']['BldFlDmp1']

class RotorPerformance():
'''
Expand Down
63 changes: 58 additions & 5 deletions ROSCO_toolbox/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ class FileProcessing():

def __init__(self):
pass

def write_param_file(self, turbine, controller, param_file='DISCON.IN', txt_filename='Cp_Ct_Cq.txt'):
"""
Print the controller parameters to the DISCON.IN input file for the generic controller
Expand Down Expand Up @@ -437,7 +438,7 @@ def write_param_file(self, turbine, controller, param_file='DISCON.IN', txt_file
file.write('{0:<12d} ! PS_Mode - Pitch saturation mode {{0: no pitch saturation, 1: implement pitch saturation}}\n'.format(controller.PS_Mode > 0))
file.write('{0:<12d} ! SD_Mode - Shutdown mode {{0: no shutdown procedure, 1: pitch to max pitch at shutdown}}\n'.format(controller.SD_Mode))
file.write('{0:<12d} ! Fl_Mode - Floating specific feedback mode {{0: no nacelle velocity feedback, 1: nacelle velocity feedback}}\n'.format(controller.Fl_Mode))
file.write('{0:<12d} ! Flp_Mode - Flap control mode {{0: no flap control, 1: steady state flap angle, 2: Proportional flap control}}\n'.format(controller.Fl_Mode))
file.write('{0:<12d} ! Flp_Mode - Flap control mode {{0: no flap control, 1: steady state flap angle, 2: Proportional flap control}}\n'.format(controller.Flp_Mode))
file.write('\n')
file.write('!------- FILTERS ----------------------------------------------------------\n')
file.write('{:<13.5f} ! F_LPFCornerFreq - Corner frequency (-3dB point) in the low-pass filters, [rad/s]\n'.format(turbine.bld_edgewise_freq * 1/4))
Expand All @@ -446,7 +447,7 @@ def write_param_file(self, turbine, controller, param_file='DISCON.IN', txt_file
file.write('{:<10.5f}{:<9.5f} ! F_NotchBetaNumDen - Two notch damping values (numerator and denominator, resp) - determines the width and depth of the notch, [-]\n'.format(0.0,0.25))
file.write('{:<014.5f} ! F_SSCornerFreq - Corner frequency (-3dB point) in the first order low pass filter for the setpoint smoother, [rad/s].\n'.format(controller.ss_cornerfreq))
file.write('{:<10.5f}{:<9.5f} ! F_FlCornerFreq - Corner frequency and damping in the second order low pass filter of the tower-top fore-aft motion for floating feedback control [rad/s, -].\n'.format(turbine.ptfm_freq, 1.0))
file.write('{:<10.5f}{:<9.5f} ! F_FlpCornerFreq - Corner frequency and damping in the second order low pass filter of the blade root bending moment for flap control [rad/s, -].\n'.format(turbine.bld_flapwise_freq, 0.7))
file.write('{:<10.5f}{:<9.5f} ! F_FlpCornerFreq - Corner frequency and damping in the second order low pass filter of the blade root bending moment for flap control [rad/s, -].\n'.format(turbine.bld_flapwise_freq*1/2, 0.7))

file.write('\n')
file.write('!------- BLADE PITCH CONTROL ----------------------------------------------\n')
Expand Down Expand Up @@ -539,8 +540,8 @@ def write_param_file(self, turbine, controller, param_file='DISCON.IN', txt_file
file.write('\n')
file.write('!------- FLAP ACTUATION -----------------------------------------------------\n')
file.write('{:<014.5f} ! Flp_Angle - Initial or steady state flap angle [rad]\n'.format(controller.flp_angle))
file.write('{:<014.5f} ! Flp_Kp - Blade root bending moment proportional gain for flap control [s]\n'.format(controller.Kp_flap))
file.write('{:<014.5f} ! Flp_Ki - Flap displacement integral gain for flap control [s]'.format(controller.Ki_flap))
file.write('{:<014.8e} ! Flp_Kp - Blade root bending moment proportional gain for flap control [s]\n'.format(controller.Kp_flap[-1]))
file.write('{:<014.8e} ! Flp_Ki - Flap displacement integral gain for flap control [s]'.format(controller.Ki_flap[-1]))
file.close()

def write_rotor_performance(self,turbine,txt_filename='Cp_Ct_Cq.txt'):
Expand Down Expand Up @@ -592,4 +593,56 @@ def write_rotor_performance(self,turbine,txt_filename='Cp_Ct_Cq.txt'):
file.write('{0:.6f} '.format(turbine.Cq_table[i,j]))
file.write('\n')
file.write('\n')
file.close()
file.close()

def load_from_txt(txt_filename):
'''
Load rotor performance data from a *.txt file.
Parameters:
-----------
txt_filename: str
Filename of the text containing the Cp, Ct, and Cq data. This should be in the format printed by the write_rotorperformance function
'''
print('Loading rotor performace data from text file:', txt_filename)

with open(txt_filename) as pfile:
for line in pfile:
# Read Blade Pitch Angles (degrees)
if 'Pitch angle' in line:
pitch_initial = np.array([float(x) for x in pfile.readline().strip().split()])
pitch_initial_rad = pitch_initial * deg2rad # degrees to rad -- should this be conditional?

# Read Tip Speed Ratios (rad)
if 'TSR' in line:
TSR_initial = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Power Coefficients
if 'Power' in line:
pfile.readline()
Cp = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Cp[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Thrust Coefficients
if 'Thrust' in line:
pfile.readline()
Ct = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Ct[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# Read Torque Coefficients
if 'Torque' in line:
pfile.readline()
Cq = np.empty((len(TSR_initial),len(pitch_initial)))
for tsr_i in range(len(TSR_initial)):
Cq[tsr_i] = np.array([float(x) for x in pfile.readline().strip().split()])

# return pitch_initial_rad TSR_initial Cp Ct Cq
# Store necessary metrics for analysis and tuning
# self.pitch_initial_rad = pitch_initial_rad
# self.TSR_initial = TSR_initial
# self.Cp_table = Cp
# self.Ct_table = Ct
# self.Cq_table = Cq
return pitch_initial_rad, TSR_initial, Cp, Ct, Cq

0 comments on commit 1e95510

Please sign in to comment.