Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 78ab491

Browse files
committed
Adding examples for case generation and postpro, running examples as unittest
1 parent 3d98b96 commit 78ab491

13 files changed

+457
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
NREL5MW/
2+
NREL5MW_*
3+
*.csv
4+
_*
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import numpy as np
2+
import os
3+
import matplotlib.pyplot as plt
4+
5+
import pyFAST.case_generation.case_gen as case_gen
6+
import pyFAST.input_output.postpro as postpro
7+
8+
# Get current directory so this script can be called from any location
9+
MyDir=os.path.dirname(__file__)
10+
11+
def CPLambdaExample():
12+
""" Example to determine the CP-CT Lambda Pitch matrices of a turbine.
13+
This script uses the function CPCT_LambdaPitch which basically does the same as Parametric Examples given in this folder
14+
"""
15+
FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
16+
ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied)
17+
main_file = 'Main_Onshore_OF2.fst' # Main file in ref_dir, used as a template
18+
19+
# --- Computing CP and CT matrices for range of lambda and pitches
20+
Lambda = np.linspace(0.1,10,3)
21+
Pitch = np.linspace(-10,10,4)
22+
23+
CP,CT,Lambda,Pitch,MaxVal,result = case_gen.CPCT_LambdaPitch(ref_dir,main_file,Lambda,Pitch,fastExe=FAST_EXE,showOutputs=False,nCores=4,TMax=10)
24+
25+
print('CP max',MaxVal)
26+
27+
# --- Plotting matrix of CP values
28+
from mpl_toolkits.mplot3d import Axes3D
29+
from matplotlib import cm
30+
fig = plt.figure()
31+
ax = fig.gca(projection='3d')
32+
LAMBDA, PITCH = np.meshgrid(Lambda, Pitch)
33+
CP[CP<0]=0
34+
surf = ax.plot_surface(LAMBDA, PITCH, np.transpose(CP), cmap=cm.coolwarm, linewidth=0, antialiased=True,alpha=0.8)
35+
ax.scatter(MaxVal['lambda_opt'],MaxVal['pitch_opt'],MaxVal['CP_max'],c='k',marker='o',s=20)
36+
ax.set_xlabel('lambda')
37+
ax.set_ylabel('pitch')
38+
ax.set_zlabel('CP')
39+
fig.colorbar(surf, shrink=0.5, aspect=5)
40+
41+
42+
43+
if __name__=='__main__':
44+
CPLambdaExample()
45+
plt.show()
46+
if __name__=='__test__':
47+
# Need openfast.exe, doing nothing
48+
pass
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Generate, run and postprocess openfast cases using an Excelfile which defines the parameters to change for each simulation
3+
"""
4+
import os
5+
import pandas as pd
6+
7+
import pyFAST.case_generation.case_gen as case_gen
8+
import pyFAST.case_generation.runner as case_gen
9+
import pyFAST.input_output.postpro as postpro
10+
import pyFAST.input_output as io
11+
12+
# Get current directory so this script can be called from any location
13+
MyDir=os.path.dirname(__file__)
14+
15+
16+
def main():
17+
# --- Main Parameters
18+
ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied)
19+
FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
20+
main_file = 'Main_Onshore_OF2.fst' # Main file in ref_dir, used as a template
21+
work_dir = '_NREL5MW_ParametricExcel/' # Output folder (will be created)
22+
parametricFile = 'ParametricExcel.xlsx' # Excel file containing set of parameters
23+
24+
# --- Reading Excel file, converting it to a list of dictionaries, and generate input files
25+
dfs = io.excel_file.ExcelFile(parametricFile).toDataFrame()
26+
df = dfs[list(dfs.keys())[0]]
27+
PARAMS = df.to_dict('records')
28+
print(df)
29+
fastFiles=case_gen.templateReplace(PARAMS, ref_dir, outputDir=work_dir, removeRefSubFiles=True, removeAllowed=False, main_file=main_file)
30+
31+
# --- Running fast simulations
32+
print('>>> Running {} simulations in {} ...'.format(len(fastFiles), work_dir))
33+
runner.writeBatch(os.path.join(work_dir,'_RUN_ALL.bat'),fastFiles,fastExe=FAST_EXE)
34+
runner.run_fastfiles(fastFiles, showOutputs=False, fastExe=FAST_EXE, nCores=4)
35+
36+
# --- Postpro - Computing averages at the end of the simluation
37+
print('>>> Postprocessing...')
38+
outFiles = [os.path.splitext(f)[0]+'.outb' for f in fastFiles]
39+
ColKeepStats = ['RotSpeed_[rpm]','BldPitch1_[deg]','RtAeroCp_[-]','RtAeroCt_[-]','Wind1VelX_[m/s]']
40+
result = postpro.averagePostPro(outFiles,avgMethod='constantwindow',avgParam=5,ColKeep=ColKeepStats,ColSort='RotSpeed_[rpm]')
41+
result.to_csv('ParametricExcel_Summary.csv',sep='\t',index=False)
42+
print('Average values saved to _ParametricExcel_Summary.csv')
43+
44+
if __name__=='__main__':
45+
main()
46+
47+
if __name__=='__test__':
48+
# Need openfast.exe, doing nothing
49+
pass
50+
51+
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import numpy as np
2+
import os
3+
4+
import pyFAST.case_generation.case_gen as case_gen
5+
import pyFAST.case_generation.runner as case_gen
6+
import pyFAST.input_output.postpro as postpro
7+
8+
# Get current directory so this script can be called from any location
9+
MyDir=os.path.dirname(__file__)
10+
11+
def PowerCurveParametricExample1():
12+
""" Example to run a set of FAST simulations to determine a power curve.
13+
In this example, the WS, RPM and Pitch are set within a for loop.
14+
If the controller and generator are active, these are just "initial conditions".
15+
Additional parameters may be set by adjusting the BaseDict.
16+
17+
This script is based on a reference directory which contains a reference main input file (.fst)
18+
Everything is copied to a working directory.
19+
The different fast inputs are generated based on a list of dictionaries, named `PARAMS`.
20+
For each dictionary:
21+
- they keys are "path" to a input parameter, e.g. `EDFile|RotSpeed` or `TMax`.
22+
These should correspond to whater name of the variable is used in the FAST inputs files.
23+
- they values are the values corresponding to this parameter
24+
"""
25+
# --- Parameters for this script
26+
FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
27+
ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied)
28+
main_file = 'Main_Onshore_OF2.fst' # Main file in ref_dir, used as a template
29+
work_dir = '_NREL5MW_PowerCurveParametric/' # Output folder (will be created)
30+
31+
# --- Defining the parametric study (list of dictionnaries with keys as FAST parameters)
32+
WS = [3,5,7,9 ,11,13,15]
33+
RPM = [5,6,7,10,10,10,10] # initial conditions
34+
PITCH = [0,0,0,0 ,5 ,10,15] # initial conditions
35+
BaseDict = {'TMax': 100, 'DT': 0.01, 'DT_Out': 0.1}
36+
#BaseDict = case_gen.paramsNoController(BaseDict)
37+
#BaseDict = case_gen.paramsStiff(BaseDict)
38+
#BaseDict = case_gen.paramsNoGen(BaseDict)
39+
PARAMS=[]
40+
for wsp,rpm,pitch in zip(WS,RPM,PITCH): # NOTE: same length of WS and RPM otherwise do multiple for loops
41+
p=BaseDict.copy()
42+
p['EDFile|RotSpeed'] = rpm
43+
p['EDFile|BlPitch(1)'] = pitch
44+
p['EDFile|BlPitch(2)'] = pitch
45+
p['EDFile|BlPitch(3)'] = pitch
46+
p['InflowFile|HWindSpeed'] = wsp
47+
p['InflowFile|WindType'] = 1 # Setting steady wind
48+
p['__name__'] = 'ws{:04.1f}'.format(p['InflowFile|HWindSpeed'])
49+
PARAMS.append(p)
50+
51+
# --- Generating all files in a workdir
52+
fastFiles=case_gen.templateReplace(PARAMS, ref_dir, work_dir, removeRefSubFiles=True, main_file=main_file)
53+
print(fastFiles)
54+
55+
# --- Creating a batch script just in case
56+
runner.writeBatch(os.path.join(work_dir,'_RUN_ALL.bat'), fastFiles,fastExe=FAST_EXE)
57+
# --- Running the simulations
58+
print('>>> Running {} simulations in {} ...'.format(len(fastFiles), work_dir))
59+
runner.run_fastfiles(fastFiles, fastExe=FAST_EXE, parallel=True, showOutputs=False, nCores=2)
60+
61+
# --- Simple Postprocessing
62+
outFiles = [os.path.splitext(f)[0]+'.outb' for f in fastFiles]
63+
64+
avg_results = postpro.averagePostPro(outFiles,avgMethod='constantwindow',avgParam=10, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
65+
print('>>> Average results:')
66+
print(avg_results)
67+
avg_results.to_csv('_PowerCurve1.csv',sep='\t',index=False)
68+
69+
70+
def PowerCurveParametricExample2():
71+
""" Example to run a set of FAST simulations to determine a power curve.
72+
In this example, the WS, RPM and Pitch are set within a for loop.
73+
If the controller and generator are active, these are just "initial conditions".
74+
Additional parameters may be set by adjusting the BaseDict.
75+
76+
This script is based on a reference directory which contains a reference main input file (.fst)
77+
Everything is copied to a working directory.
78+
The different fast inputs are generated based on a list of dictionaries, named `PARAMS`.
79+
For each dictionary:
80+
- they keys are "path" to a input parameter, e.g. `EDFile|RotSpeed` or `TMax`.
81+
These should correspond to whater name of the variable is used in the FAST inputs files.
82+
- they values are the values corresponding to this parameter
83+
"""
84+
# --- Parameters for this script
85+
FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
86+
ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied)
87+
main_file = 'Main_Onshore_OF2.fst' # Main file in ref_dir, used as a template
88+
work_dir = '_NREL5MW_PowerCurveParametric2/' # Output folder (will be created)
89+
out_Ext = '.outb' # Output extension
90+
91+
# --- Defining the parametric study (list of dictionnaries with keys as FAST parameters)
92+
WS = [3,5,7,9 ,11,13,15]
93+
RPM = [5,6,7,10,10,10,10]
94+
PITCH = [0,0,0,0 ,5 ,10,15]
95+
BaseDict = {'TMax': 10, 'DT': 0.01, 'DT_Out': 0.1}
96+
PARAMS = case_gen.paramsWS_RPM_Pitch(WS, RPM, PITCH, baseDict=BaseDict, flatInputs=True)
97+
98+
# --- Generating all files in a workdir
99+
fastFiles = case_gen.templateReplace(PARAMS, ref_dir, work_dir, removeRefSubFiles=True, removeAllowed=True, main_file=main_file)
100+
101+
# --- Creating a batch script just in case
102+
runner.writeBatch(os.path.join(work_dir,'_RUN_ALL.bat'), fastFiles,fastExe=FAST_EXE)
103+
104+
# --- Running the simulations
105+
runner.run_fastfiles(fastFiles, fastExe=FAST_EXE, parallel=True, showOutputs=False, nCores=2)
106+
107+
# --- Simple Postprocessing
108+
outFiles = [os.path.splitext(f)[0]+out_Ext for f in fastFiles]
109+
avg_results = postpro.averagePostPro(outFiles,avgMethod='constantwindow',avgParam=10, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
110+
print('>>> Average results:')
111+
print(avg_results)
112+
avg_results.to_csv('_PowerCurve2.csv',sep='\t',index=False)
113+
114+
115+
116+
if __name__=='__main__':
117+
PowerCurveParametricExample1()
118+
PowerCurveParametricExample2()
119+
120+
if __name__=='__test__':
121+
# Need openfast.exe, doing nothin
122+
pass
123+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This folder contains examples of OpenFAST postprocessing:
2+
3+
- CPLambdaPitch: determine the CP-CT Lambda Pitch matrices of a turbine.
4+
- ExcelFile: Generate, run and postprocess OpenFAST cases using an Excelfile which defines the parameters to change for each simulation
5+
- ParametricStudy: Generate, run and postprocess OpenFAST simulations where the parameters of each cases are defined within a python loop
6+
- PowerCurve: run a set of OpenFAST simulations to determine a power curve.
7+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import unittest
2+
import numpy as np
3+
import glob
4+
import os
5+
6+
def execfile(filepath, globals=None, locals=None):
7+
""" Execute a given python file """
8+
if globals is None:
9+
globals = {"__name__": "__main__"}
10+
globals.update({
11+
"__file__": filepath,
12+
})
13+
with open(filepath, 'rb') as file:
14+
exec(compile(file.read(), filepath, 'exec'), globals, locals)
15+
16+
class TestExamples(unittest.TestCase):
17+
def test_run_examples(self):
18+
exclude_list=[]
19+
# Add tests to class
20+
MyDir=os.path.dirname(__file__)
21+
files = glob.glob(os.path.join(MyDir,'../examples/[a-zA-Z]*.py'))
22+
for f in files:
23+
print('\n--------------------------------------------------------------')
24+
print('Running example script: {}'.format(f))
25+
if hasattr(self,'subTest'):
26+
with self.subTest(filename=os.path.basename(f)):
27+
execfile(f, {'__name__': '__test__', 'print': lambda *_:None})
28+
29+
30+
if __name__ == '__main__':
31+
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
NREL5MW/
2+
NREL5MW_*
3+
*.csv
4+
_*
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
This example opens a fast output file, and interpolate a timeseries to a given radial location.
3+
This is convenient when outputs are required at a station different from the ones used in the OpenFAST outputs.
4+
5+
"""
6+
import os
7+
import numpy as np
8+
import pandas as pd
9+
import matplotlib.pyplot as plt
10+
11+
import pyFAST.input_output as io
12+
import pyFAST.input_output.postpro as postpro
13+
14+
def main():
15+
# Get current directory so this script can be called from any location
16+
MyDir=os.path.dirname(__file__)
17+
18+
# --- Read an openfast output file
19+
outFile = os.path.join(MyDir,'../../../data/example_files/fastout_allnodes.outb')
20+
df = io.fast_output_file.FASTOutputFile(outFile).toDataFrame()
21+
22+
# --- Define output radial stations
23+
# Option 1 - Get all these locations automatically (recommended)
24+
fstFile = os.path.join(MyDir,'../../../data/NREL5MW/Main_Onshore_OF2.fst') # must correspond to the one used to generate outputs
25+
r_AD, r_ED, r_BD, IR_AD, IR_ED, IR_BD, R, r_hub, fst = postpro.FASTRadialOutputs(fstFile, df.columns.values)
26+
27+
# Option 2 - Get ouputs locations for each module
28+
#r_ED_gag, IR_ED = ED_BldGag(fstFile)
29+
#r_AD_gag, IR_AD = AD_BldGag(fstFile)
30+
31+
# Option 3 - Define them manually..
32+
#r_AD = [0.,30.,60.]
33+
#r_ED = [0.,30.,60.]
34+
35+
# --- Interpolate Cl and TDx at desired radial position
36+
# NOTE: format need to be adjusted if you use AllOuts, or outputs at few nodes
37+
r = 60 # Radial location where outputs are to be interpolated
38+
Cl_interp = postpro.radialInterpTS(df, r, 'Cl_[-]', r_AD, bldFmt='AB{:d}', ndFmt='N{:03d}')
39+
TDx_interp = postpro.radialInterpTS(df, r, 'TDx_[m]', r_ED, bldFmt='B{:d}' , ndFmt='N{:03d}')
40+
#TDx_interp = postpro.radialInterpTS(df, r, 'TDx_[m]', r_ED, bldFmt='B{:d}' , ndFmt='N{d}')
41+
42+
# --- Plot
43+
fig,ax = plt.subplots(1, 1, sharey=False, figsize=(6.4,4.8)) # (6.4,4.8)
44+
fig.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.11, hspace=0.20, wspace=0.20)
45+
ax.plot(df['Time_[s]'], df['AB1N017Cl_[-]'], label='Section before (r={}m)'.format(r_AD[16]))
46+
ax.plot(df['Time_[s]'], Cl_interp , label='Interpolated (r={}m)'.format(r))
47+
ax.plot(df['Time_[s]'], df['AB1N018Cl_[-]'], label='Section after (r={}m)'.format(r_AD[17]))
48+
ax.set_xlabel('Time [s]')
49+
ax.set_ylabel('Cl [-]')
50+
ax.set_xlim([7,10])
51+
ax.set_ylim([0.35,0.48])
52+
ax.legend()
53+
ax.tick_params(direction='in')
54+
ax.set_title('FAST - interpolate radial time series')
55+
56+
if __name__=='__main__':
57+
main()
58+
plt.show()
59+
60+
if __name__=='__test__':
61+
main()
62+
63+
if __name__=='__export__':
64+
main()
65+
from welib.tools.repo import export_figs_callback
66+
export_figs_callback(__file__)
67+

0 commit comments

Comments
 (0)