77 https://openfast.readthedocs.io/en/dev/source/user/fast.farm/ModelGuidance.html
88
99"""
10-
10+ import os
1111from openfast_toolbox .fastfarm .FASTFarmCaseCreation import FFCaseCreation
1212from openfast_toolbox .fastfarm .AMRWindSimulation import AMRWindSimulation
1313
14- def main ():
14+
15+ scriptDir = os .path .dirname (__file__ )
16+
17+ def main (test = False ):
1518
1619 # -----------------------------------------------------------------------------
1720 # USER INPUT: Modify these
@@ -22,26 +25,25 @@ def main():
2225 # -----------------------------------------------------------------------------
2326
2427 # ----------- Case absolute path
25- path = '/complete/ path/of/your/case'
28+ path = os . path . join ( scriptDir , '_ex3' ) # folder (preferably new) where all the simulation files will be written
2629
2730 # ----------- Execution parameters
2831 # If you are sure the correct binary is first on your $PATH and associated
2932 # libraries on $LD_LIBRARY_PATH, you can set the variable below to None or
3033 # remove it from the `FFCaseCreation` call
31- ffbin = '/full/ path/to/your/binary/.../bin/ FAST.Farm'
34+ ffbin = os . path . join ( scriptDir , './SampleFiles/dummy_fastfarm.exe.txt' ) # relative or absolute path of FAST.Farm executable
3235
36+ libdiscon = os .path .join (scriptDir , './SampleFiles/dummy_discon.dll.dummy' ) # relative or absolute path to discon shared library
3337
3438 # -----------------------------------------------------------------------------
3539 # --------------------------- Farm parameters ---------------------------------
3640 # -----------------------------------------------------------------------------
37-
3841 # ----------- General turbine parameters
39- cmax = 5 # Maximum blade chord (m)
40- fmax = 10 / 6 # Maximum excitation frequency (Hz)
42+ cmax = 5 # Maximum blade chord (m), affects dr
43+ fmax = 10 / 6 # Maximum excitation frequency (Hz), affects dt_high
4144 Cmeander = 1.9 # Meandering constant (-)
4245 D = 240 # Rotor diameter (m)
4346 zhub = 150 # Hub height (m)
44-
4547 # ----------- Wind farm
4648 # The wts dictionary holds information of each wind turbine. The allowed entries
4749 # are: x, y, z, D, zhub, cmax, fmax, Cmeander, and phi_deg. The phi_deg is the
@@ -65,12 +67,10 @@ def main():
6567 # -----------------------------------------------------------------------------
6668 # ------------------- Inflow conditions and input files -----------------------
6769 # -----------------------------------------------------------------------------
68-
6970 # ----------- Additional variables
7071 tmax = 60 # Total simulation time
7172 zbot = 1 # Bottom of your domain
7273 mod_wake = 2 # Wake model. 1: Polar, 2: Curled, 3: Cartesian
73-
7474 # ----------- Inflow parameters
7575 inflowType = 'LES'
7676 inflowPath = '/full/path/to/LES/case/.../LESboxes'
@@ -81,36 +81,40 @@ def main():
8181 # executions with multiple different LES solutions, you can give inflowPath
8282 # as a n-sized array (related to the different LES), and give the variables
8383 # below as n-sized arrays as well.
84- vhub = [8 ]
85- shear = [1 ]
86- TIvalue = [1 ]
87- inflow_deg = [0 ]
84+ vhub = [8 ] # Hub velocity [m/s]
85+ shear = [0.1 ] # Power law exponent [- ]
86+ TIvalue = [10 ] # Turbulence intensity [% ]
87+ inflow_deg = [0 ] # Wind direction [deg]
8888
8989 # ----------- Template files
90- templatePath = '/full/path/where/template/files/are'
91- # Files should be in templatePath. Put None on any input that is not applicable.
92- templateFiles = {
93- "EDfilename" : 'ElastoDyn.T' ,
94- 'SEDfilename' : None , # 'SimplifiedElastoDyn.T',
95- 'HDfilename' : None , # 'HydroDyn.dat', # ending with .T for per-turbine HD, .dat for holisitc
96- 'MDfilename' : None , # 'MoorDyn.T', # ending with .T for per-turbine MD, .dat for holistic
97- 'SSfilename' : None , # 'SeaState.dat',
98- 'SrvDfilename' : 'ServoDyn.T' ,
99- 'ADfilename' : 'AeroDyn.dat' ,
100- 'ADskfilename' : None ,
101- 'SubDfilename' : 'SubDyn.dat' ,
102- 'IWfilename' : 'InflowWind.dat' ,
103- 'BDfilename' : None ,
104- 'EDbladefilename' : 'ElastoDyn_Blade.dat' ,
105- 'EDtowerfilename' : 'ElastoDyn_Tower.dat' ,
106- 'ADbladefilename' : 'AeroDyn_Blade.dat' ,
107- 'turbfilename' : 'Model.T' ,
108- 'libdisconfilepath' : '/full/path/to/controller/libdiscon.so' ,
109- 'controllerInputfilename' : 'DISCON.IN' ,
110- 'coeffTablefilename' : None ,
111- 'hydroDatapath' : None , # '/full/path/to/hydroData',
112- 'FFfilename' : 'Model_FFarm.fstf' ,
113- }
90+ # --- Option 1
91+ templateFSTF = os .path .join (scriptDir , '../../../data/IEA15MW/FF.fstf' )
92+ templateFiles = {'libdisconfilepath' : libdiscon }
93+ # --- Option 2
94+ #templatePath = '/full/path/where/template/files/are'
95+ ## Files should be in templatePath. Put None on any input that is not applicable.
96+ #templateFiles = {
97+ # "EDfilename" : 'ElastoDyn.T',
98+ # 'SEDfilename' : None, # 'SimplifiedElastoDyn.T',
99+ # 'HDfilename' : None, # 'HydroDyn.dat', # ending with .T for per-turbine HD, .dat for holisitc
100+ # 'MDfilename' : None, # 'MoorDyn.T', # ending with .T for per-turbine MD, .dat for holistic
101+ # 'SSfilename' : None, # 'SeaState.dat',
102+ # 'SrvDfilename' : 'ServoDyn.T',
103+ # 'ADfilename' : 'AeroDyn.dat',
104+ # 'ADskfilename' : None,
105+ # 'SubDfilename' : 'SubDyn.dat',
106+ # 'IWfilename' : 'InflowWind.dat',
107+ # 'BDfilename' : None,
108+ # 'EDbladefilename' : 'ElastoDyn_Blade.dat',
109+ # 'EDtowerfilename' : 'ElastoDyn_Tower.dat',
110+ # 'ADbladefilename' : 'AeroDyn_Blade.dat',
111+ # 'turbfilename' : 'Model.T',
112+ # 'libdisconfilepath' : '/full/path/to/controller/libdiscon.so',
113+ # 'controllerInputfilename' : 'DISCON.IN',
114+ # 'coeffTablefilename' : None,
115+ # 'hydroDatapath' : None, # '/full/path/to/hydroData',
116+ # 'FFfilename' : 'Model_FFarm.fstf',
117+ #}
114118 # SLURM scripts
115119 slurm_FF_single = './SampleFiles/runFASTFarm_cond0_case0_seed0.sh'
116120
@@ -152,7 +156,7 @@ def main():
152156 level_lr = level_lr , level_hr = level_hr , verbose = 0 )
153157
154158 # -----------Save AMR-Wind sampling input
155- amr .write_sampling_params (os .path .join (path ,'FF_boxes.i' ), overwrite = True )
159+ amr .write_sampling_params (os .path .join (path ,'FF_boxes.i' ), overwrite = True , terrain = False )
156160
157161
158162 # -----------------------------------------------------------------------------
@@ -166,12 +170,12 @@ def main():
166170
167171 # ----------- Low- and high-res boxes parameters
168172 # High-res boxes settings
169- dt_high = amr .dt_high_les # sampling frequency of high-res files
170- ds_high = amr .ds_high_les # dx, dy, dz of high-res files
171- extent_high = amr .extent_high # extent in y and x for each turbine, in D.
173+ dt_high = amr .dt_high_les # sampling time of high-res files [s]
174+ ds_high = amr .ds_high_les # dx, dy, dz of high-res files [m]
175+ extent_high = amr .extent_high # extent in y and x for each turbine, in D
172176 # Low-res boxes settings
173- dt_low = amr .dt_low_les # sampling frequency of low-res files
174- ds_low = amr .ds_low_les # dx, dy, dz of low-res files
177+ dt_low = amr .dt_low_les # sampling time of low-res files [s]
178+ ds_low = amr .ds_low_les # dx, dy, dz of low-res files [m]
175179 extent_low = amr .extent_low # extent in [xmin,xmax,ymin,ymax,zmax], in D
176180
177181
@@ -193,27 +197,60 @@ def main():
193197 refTurb_rot = refTurb_rot , verbose = 0 )
194198
195199 # ----------- Perform auxiliary steps in preparing the case
196- ffcase .setTemplateFilename (templatePath , templateFiles )
200+ ffcase .setTemplateFilename (templateFiles = templateFiles , templateFSTF = templateFSTF ) # Option 1
201+ #ffcase.setTemplateFilename(templatePath, templateFiles) # Option 2
197202 ffcase .getDomainParameters ()
198203 ffcase .copyTurbineFilesForEachCase ()
199204
200205
201206 # -----------------------------------------------------------------------------
202207 # ------------------ Finish FAST.Farm setup and execution ---------------------
203208 # -----------------------------------------------------------------------------
209+ if test :
210+ return amr , ffcase
204211 # ----------- FAST.Farm setup
205- ffcase .FF_setup ()
212+ ffcase .FF_setup () # Write FAST.Farm input files
206213 # Update wake model constants (adjust as needed for your turbine model)
207214 ffcase .set_wake_model_params (k_VortexDecay = 0 , k_vCurl = 2.8 )
208215
209216 # ----------- Prepare script for submission
210- ffcase .FF_slurm_prepare (slurm_FF_single )
217+ ffcase .FF_batch_prepare () # Write batch files with all commands to be run
218+ #ffcase.FF_slurm_prepare(slurm_FF_single) # Alternative, prepare a slurm batch file
211219
212220 # ----------- Submit the FAST.Farm script (can be done from the command line)
213- ffcase .FF_slurm_submit (p = 'debug' , t = '1:00:00' )
214-
221+ ffcase .FF_batch_run (showOutputs = True , showCommand = True , nBuffer = 10 , shell_cmd = 'bash' )
222+ #ffcase.FF_slurm_submit(p='debug', t='1:00:00') # Alternative, submit a slurm batch file
223+ return amr , ffcase
215224
216225
217226if __name__ == '__main__' :
227+ amr , ffcase = main (test = True )
218228 # This example cannot be fully run.
229+ #print(amr)
230+ #print(ffcase)
219231 pass
232+ if __name__ == '__test__' :
233+ amr , ffcase = main (test = True )
234+ # This example cannot be fully run.
235+ import numpy as np
236+ import shutil
237+ np .testing .assert_equal (amr .ds_low_les , 20.0 )
238+ np .testing .assert_equal (amr .dt_low_les , 0.9 )
239+ np .testing .assert_array_equal (amr .extent_low , [1.5 , 2.5 , 1.5 , 1.5 , 2 ] )
240+ np .testing .assert_equal (amr .ds_high_les , 5.0 )
241+ np .testing .assert_equal (amr .dt_high_les , 0.3 )
242+ np .testing .assert_equal (ffcase .ds_low , 20.0 )
243+ np .testing .assert_equal (ffcase .dt_low , 0.9 )
244+ np .testing .assert_equal (ffcase .ds_high , 5.0 )
245+ np .testing .assert_equal (ffcase .dt_high , 0.3 )
246+ np .testing .assert_ (os .path .exists (ffcase .path ), f"path does not exist { ffcase .path } " )
247+ for cond , _ in enumerate (ffcase .condDirList ):
248+ np .testing .assert_ (os .path .exists (ffcase .getCondPath (cond )), f"cond path { cond } does not exist " )
249+ for case in range (ffcase .nCases ):
250+ np .testing .assert_ (os .path .exists (ffcase .getCasePath (cond , case )), f"case path { cond } { case } does not exist " )
251+ if os .path .exists (ffcase .path ):
252+ try :
253+ shutil .rmtree (ffcase .path )
254+ except OSError as e :
255+ print ('Fail to remove FAST.Farm ex2 folder' )
256+ pass
0 commit comments