Skip to content

Commit 54fbd3a

Browse files
committed
FF: changes in turbsim-based boxes generation. See extended message
Some overhaul on the api for the turbsim case creation. This commit fixes some hard-coded parameters from before. It is recommended that the user sets the ds_{high,low} and dt_{high,low} him/herself.
1 parent 7019be0 commit 54fbd3a

2 files changed

Lines changed: 165 additions & 196 deletions

File tree

openfast_toolbox/fastfarm/FASTFarmCaseCreation.py

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class FFCaseCreation:
8888
def __init__(self,
8989
path,
9090
wts,
91-
tmax_desired,
91+
tmax,
9292
zbot,
9393
vhub,
9494
shear,
@@ -123,8 +123,9 @@ def __init__(self,
123123
Full path for the target case directory
124124
wts: dictionary
125125
Wind farm layout and turbine parameters in dictionary form
126-
tmax_desired: scalar
127-
Max desired simulation time given in seconds. OF toolbox makes small adjustments to ensure compatibility to dT
126+
tmax: scalar
127+
Max FAST.Farm simulation time given in seconds. Small adjustments for TurbSim
128+
simulations are made to ensure compatibility with both high- and low-res boxes
128129
vhub: list of scalars or single scalar
129130
Wind speeds at hub height to sweep on. Accepts a list or single value
130131
shear: list of scalars or single scalar
@@ -190,7 +191,7 @@ def __init__(self,
190191

191192
self.path = path
192193
self.wts = wts
193-
self.tmax_desired = tmax_desired
194+
self.tmax = tmax
194195
self.zbot = zbot
195196
self.vhub = vhub
196197
self.shear = shear
@@ -325,9 +326,6 @@ def _checkInputs(self):
325326
self.fmax = self.wts[0]['fmax']
326327
self.Cmeander = self.wts[0]['Cmeander']
327328

328-
if self.inflowType == 'TS' and self.dt_high == None:
329-
self.dt_high = 1./(2.*self.fmax)
330-
331329
# Check the platform heading and initialize as zero if needed
332330
if 'phi_deg' not in self.wts[0]: # check key for first turbine
333331
for i in self.wts:
@@ -374,7 +372,7 @@ def _checkInputs(self):
374372
if self.cmax <= 0: raise ValueError('cmax cannot be negative')
375373
if self.fmax <= 0: raise ValueError('fmax cannot be negative')
376374
if self.Cmeander <= 0: raise ValueError('Cmeander cannot be negative')
377-
if self.tmax_desired <= 0: raise ValueError('A positive tmax_desired should be requested')
375+
if self.tmax <= 0: raise ValueError('A positive tmax should be requested')
378376
if self.zbot <= 0: raise ValueError('zbot should be greater than 0 (recommended 1)')
379377

380378
# Ensure quantities are list
@@ -414,7 +412,6 @@ def _checkInputs(self):
414412
raise ValueError(f'The extent of high boxes is not enough to cover the rotor diameter. '\
415413
'The extent high is given as the total extent, and it needs to be greater than 1.')
416414

417-
418415
# Check the FAST.Farm binary
419416
if self.ffbin is None:
420417
self.ffbin = shutil.which('FAST.Farm')
@@ -436,7 +433,6 @@ def _checkInputs(self):
436433
elif not os.path.isfile(self.tsbin):
437434
raise ValueError (f'The TurbSim binary given does not exist.')
438435

439-
440436
# Check turbine conditions arrays for consistency
441437
if len(self.inflow_deg) != len(self.yaw_init):
442438
raise ValueError(f'One row for each inflow angle should be given in yaw_init. '\
@@ -499,30 +495,27 @@ def _checkInputs(self):
499495
else:
500496
raise ValueError (f"Inflow type `inflowType` should be 'TS' or 'LES'. Received {self.inflowType}.")
501497

502-
503498
# Check the wake model (1:Polar; 2:Curl; 3:Cartesian)
504499
if self.mod_wake not in [1,2,3]:
505500
raise ValueError(f'Wake model `mod_wake` should be 1 (Polar), 2 (Curl), or 3 (Cartesian). Received {self.mod_wake}.')
506501

507-
508502
# Check the ds and dt for the high- and low-res boxes. If not given, call the
509503
# AMR-Wind auxiliary function with dummy domain limits.
510-
if None in (self.dt_high, self.ds_high, self.dt_low, self.ds_low) and self.inflowType == 'LES':
504+
if None in (self.dt_high, self.ds_high, self.dt_low, self.ds_low):
511505
mod_wake_str = ['','polar', 'curled', 'cartesian']
512506
print(f'WARNING: One or more temporal or spatial resolution for low- and high-res domains were not given.')
513507
print(f' Estimated values for {mod_wake_str[self.mod_wake]} wake model shown below.')
514508
self._determine_resolutions_from_dummy_amrwind_grid()
515509

516-
# Check the domain extents when values are provided by the user:
510+
# Check the temporal and spatial resolutions if provided
517511
if self.dt_low != None and self.dt_high!= None:
518512
if self.dt_low%(self.dt_high-1e-15) > 1e-12:
519-
raise ValueError(f'The temporal resolution dT_Low should be a multiple of dT_High')
513+
raise ValueError(f'The temporal resolution dT_Low should be a multiple of dT_High.')
520514
if self.dt_low < self.dt_high:
521-
raise ValueError(f'The temporal resolution dT_High should not be greater than dT_Low on the LES side')
515+
raise ValueError(f'The temporal resolution dT_High should not be greater than dT_Low.')
522516
if self.ds_low != None and self.ds_high!= None:
523517
if self.ds_low < self.ds_high:
524-
raise ValueError(f'The grid resolution dS_High should not be greater than dS_Low on the LES side')
525-
518+
raise ValueError(f'The grid resolution dS_High should not be greater than dS_Low.')
526519

527520
# Check the reference turbine for rotation
528521
if self.refTurb_rot >= self.nTurbines:
@@ -1663,19 +1656,19 @@ def TS_low_setup(self, writeFiles=True, runOnce=False):
16631656
# Create and write new Low.inp files creating the proper box with proper resolution
16641657
# By passing low_ext, manual mode for the domain size is activated, and by passing ds_low,
16651658
# manual mode for discretization (and further domain size) is also activated
1666-
currentTS = TSCaseCreation(D_, HubHt_, Vhub_, tivalue_, shear_, x=xlocs_, y=ylocs_, zbot=self.zbot, cmax=self.cmax,
1667-
fmax=self.fmax, Cmeander=self.Cmeander, boxType=boxType, low_ext=self.extent_low, ds_low=self.ds_low)
1668-
self.TSlowbox = currentTS
1669-
1670-
# Ensure that dt low is a multiple of dt high and that tmax is a multiple of both dt low and dt high, and that tmax is larger than tmax_desired
1671-
currentTS.dt = getMultipleOf(currentTS.dt, multipleof=self.dt_high)
1672-
self.tmax = getMultipleOf(self.tmax_desired, multipleof=currentTS.dt)
1673-
if self.tmax < self.tmax_desired:
1674-
self.tmax += currentTS.dt
1675-
1676-
if runOnce:
1677-
return
1678-
currentTS.writeTSFile(self.turbsimLowfilepath, currentTSLowFile, tmax=self.tmax, verbose=self.verbose)
1659+
self.TSlowbox = TSCaseCreation(D_, HubHt_, Vhub_, tivalue_, shear_, x=xlocs_, y=ylocs_, zbot=self.zbot,
1660+
cmax=self.cmax, fmax=self.fmax, Cmeander=self.Cmeander, boxType='lowres', extent=self.extent_low,
1661+
ds_low=self.ds_low, dt_low=self.dt_low, ds_high=self.ds_high, dt_high=self.dt_high)
1662+
1663+
if runOnce: return
1664+
1665+
# Write the actual TurbSim input file. Here we set the total simulation time to one time-step
1666+
# longer than the requested value. When executing the high-res boxes, sometimes the resulting
1667+
# flowfield is shorter than the requested total simulation time. So if we ask for the low-res
1668+
# with the exact length we want, the high-res boxes might be shorter than tmax. Note that the
1669+
# total FAST.Farm simulation time remains unmodified from what the user requested.
1670+
self.TSlowbox.writeTSFile(self.turbsimLowfilepath, currentTSLowFile, tmax=self.tmax+self.dt_low, verbose=self.verbose)
1671+
16791672
# Modify some values and save file (some have already been set in the call above)
16801673
Lowinp = FASTInputFile(currentTSLowFile)
16811674
Lowinp['RandSeed1'] = self.seedValues[seed]
@@ -1853,7 +1846,6 @@ def getDomainParameters(self):
18531846
def TS_high_get_time_series(self):
18541847

18551848
# Loop on all conditions/seeds extracting time series from the Low box at turbines location
1856-
boxType='highres'
18571849
for cond in range(self.nConditions):
18581850
for seed in range(self.nSeeds):
18591851
condSeedPath = os.path.join(self.path, self.condDirList[cond], f'Seed_{seed}')
@@ -1887,9 +1879,11 @@ def TS_high_get_time_series(self):
18871879
jMid, kMid = bts.iMid
18881880

18891881
# Get time series at the box center to get mean vhub and create time array.
1890-
#Vhub = bts['u'][0,:,jTurb,kTurb]
18911882
Vmid = bts['u'][0,:,jMid,kMid]
18921883
time = bts.t
1884+
1885+
# Given the nature of how TS decides on the total time, let's get the actual tmax from the low-res
1886+
self.tmax_low = time[-1]
18931887

18941888
# The time-series need to be shifted depending on the turbine location, so we need to find how many
18951889
# grid points (time steps) the data have convected. We use the mean streamwise component for that
@@ -1901,7 +1895,7 @@ def TS_high_get_time_series(self):
19011895
wvel = np.roll(bts['u'][2, :, jTurb, kTurb], start_time_step)
19021896

19031897
# Map it to high-res time and dt (both)
1904-
time_hr = np.arange(bts.t[0], self.tmax + self.dt_high, self.dt_high)
1898+
time_hr = np.arange(time[0], time[-1]+self.dt_high, self.dt_high)
19051899
uvel_hr = np.interp(time_hr, time, uvel)
19061900
vvel_hr = np.interp(time_hr, time, vvel)
19071901
wvel_hr = np.interp(time_hr, time, wvel)
@@ -1946,7 +1940,6 @@ def TS_high_setup(self, writeFiles=True):
19461940
self.TS_high_get_time_series()
19471941

19481942
# Loop on all conditions/cases/seeds setting up the High boxes
1949-
boxType='highres'
19501943
highFilesName = []
19511944
for cond in range(self.nConditions):
19521945
for case in range(self.nHighBoxCases):
@@ -1977,13 +1970,15 @@ def TS_high_setup(self, writeFiles=True):
19771970

19781971
# Create and write new Low.inp files creating the proper box with proper resolution
19791972
currentTS = TSCaseCreation(D_, HubHt_, Vhub_, tivalue_, shear_, x=xloc_, y=yloc_, zbot=self.zbot,
1980-
cmax=self.cmax, fmax=self.fmax, Cmeander=self.Cmeander, boxType=boxType, high_ext=self.extent_high)
1981-
currentTS.writeTSFile(self.turbsimHighfilepath, currentTSHighFile, tmax=self.tmax, turb=t, verbose=self.verbose)
1973+
cmax=self.cmax, fmax=self.fmax, Cmeander=self.Cmeander, boxType='highres', extent=self.extent_high,
1974+
ds_low=self.ds_low, dt_low=self.dt_low, ds_high=self.ds_high, dt_high=self.dt_high)
1975+
1976+
currentTS.writeTSFile(self.turbsimHighfilepath, currentTSHighFile, tmax=self.tmax_low, turb=t, verbose=self.verbose)
19821977

19831978
# Modify some values and save file (some have already been set in the call above)
19841979
Highinp = FASTInputFile(currentTSHighFile)
19851980
Highinp['RandSeed1'] = self.seedValues[seed]
1986-
Highinp['TimeStep'] = 1./(2.*self.fmax)
1981+
#Highinp['TimeStep'] = 1/(2*self.fmax)
19871982
Highinp['TurbModel'] = f'"TIMESR"'
19881983
Highinp['UserFile'] = f'"USRTimeSeries_T{t+1}.txt"'
19891984
Highinp['RefHt'] = HubHt_

0 commit comments

Comments
 (0)