Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions openfast_toolbox/case_generation/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ def writeBatch(batchfile, fastfiles, fastExe=None, nBatches=1, pause=False, flag
- nBatches: split into nBatches files.
- pause: insert a pause statement at the end so that batch file is not closed after execution
- flags: flags (string) to be placed between the executable and the filename
- flags_after: flags (string) to be placed after the filename
- flags_after: flags to be placed after the filename (single string if the same for every file,
or a list of strings if different for each file)
- run_if_ext_missing: add a line in the batch file so that the command is only run if
the file `f.EXT` is missing, where .EXT is specified in run_if_ext_missing
If None, the command is always run
Expand All @@ -394,8 +395,11 @@ def writeBatch(batchfile, fastfiles, fastExe=None, nBatches=1, pause=False, flag
fastExe_rel = os.path.relpath(fastExe_abs, batchdir)
if len(flags)>0:
flags=' '+flags
if len(flags_after)>0:
flags_after=' '+flags_after
if isinstance(flags_after, str):
if len(flags_after)>0:
flags_after=' '+flags_after
elif isinstance(flags_after, list):
flags_after = [' '+f if len(f)>0 else f for f in flags_after]

# Remove commandlines if outputs are already present
if discard_if_ext_present:
Expand All @@ -414,10 +418,11 @@ def writeb(batchfile, fastfiles):
f.write('@echo off\n')
if preCommands is not None:
f.write(preCommands+'\n')
for ff in fastfiles:
for i, ff in enumerate(fastfiles):
ff_abs = os.path.abspath(ff)
ff_rel = os.path.relpath(ff_abs, batchdir)
cmd = fastExe_rel + flags + ' '+ ff_rel + flags_after
cmd = fastExe_rel + flags + ' '+ ff_rel
cmd += flags_after[i] if isinstance(flags_after, list) else flags_after
if stdOutToFile:
stdout = os.path.splitext(ff_rel)[0]+'.stdout'
cmd += ' > ' +stdout
Expand Down
117 changes: 74 additions & 43 deletions openfast_toolbox/fastfarm/FASTFarmCaseCreation.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def checkIfExists(f):
return False

def shutilcopy2_untilSuccessful(src, dst):
# Fail-safe for filesystem issues
shutil.copy2(src, dst)
if not checkIfExists(dst):
print(f'File {dst} not created. Trying again.\n')
Expand Down Expand Up @@ -268,6 +269,8 @@ def __init__(self,
Not fully tested.
ptfm_rot: bool
Whether or not platforms have headings or not (False in case of fixed farms or floating with all platforms facing 0deg)
flat: bool
Whether or not to create a flat directory structure (all cases in the same folder)
verbose: int
Verbosity level, given as integers <5

Expand Down Expand Up @@ -321,7 +324,7 @@ def __init__(self,
self.condDirList = []
self.caseDirList = []
self.DLLfilepath = None
self.DLLext = None
self.DLLext = None
self.batchfile_high = ''
self.batchfile_low = ''
self.batchfile_ff = ''
Expand All @@ -344,7 +347,7 @@ def __init__(self,

#
# TODO TODO TODO
# Creating Cases and Conditions should have it's own function interface for the user can call for a given
# Creating Cases and Conditions should have its own function interface so the user can call
if self.verbose>0: print(f'Creating auxiliary arrays for all conditions and cases...', end='\r')
self.createAuxArrays()
if self.verbose>0: print(f'Creating auxiliary arrays for all conditions and cases... Done.')
Expand All @@ -358,7 +361,7 @@ def __init__(self,


def __repr__(self):
s='<{} object> with the following content:\n'.format(type(self).__name__)
s = f'<{type(self).__name__} object> with the following content:\n'
s += f'Requested parameters:\n'
s += f' - Case path: {self.path}\n'
s += f' - Wake model: {self.mod_wake} (1:Polar; 2:Curl; 3:Cartesian)\n'
Expand Down Expand Up @@ -404,14 +407,14 @@ def __repr__(self):
s += f' - dt low: {self.dt_low} s\n'
s += f' - Extent of low-res box (in D): xmin = {self.extent_low[0]}, xmax = {self.extent_low[1]}, '
s += f'ymin = {self.extent_low[2]}, ymax = {self.extent_low[3]}, zmax = {self.extent_low[4]}\n'
if self.inflowType !='LES':
if self.inflowType == 'TS':
s += f' Low-res boxes created: {self.TSlowBoxFilesCreatedBool} .\n'

s += f' High-resolution domain: \n'
s += f' - ds high: {self.ds_high} m\n'
s += f' - dt high: {self.dt_high} s\n'
s += f' - Extent of high-res boxes: {self.extent_high} D total\n'
if self.inflowType !='LES':
if self.inflowType == 'TS':
s += f' High-res boxes created: {self.TShighBoxFilesCreatedBool}.\n'
s += f"\n"

Expand Down Expand Up @@ -534,19 +537,38 @@ def files(self, module='AD'):

@property
def high_res_bts(self):

# Not all individual high-res boxes are unique. If there are cases where the same high-res
# boxes are warranted (e.g. different nacelle yaw values), then copies/symlinks are made
highBoxesCaseDirList = [self.caseDirList[c] for c in self.allHighBoxCases.case.values]
highBoxesCaseIndex = [self.caseDirList.index(c) for c in highBoxesCaseDirList]

files = []
#highBoxesCaseDirList = [self.caseDirList[c] for c in self.allHighBoxCases.case.values]
#for condDir in self.condDirList:
# for case in highBoxesCaseDirList:
for cond in range(self.nConditions):
for case in range(self.nCases):
for case in highBoxesCaseIndex:
for seed in range(self.nSeeds):
dirpath = self.getHRTurbSimPath(cond, case, seed)
for t in range(self.nTurbines):
#dirpath = os.path.join(self.path, condDir, case, f"Seed_{seed}/TurbSim")
files.append(f'{dirpath}/HighT{t+1}.bts')

return files

@property
def high_res_log(self):

highBoxesCaseDirList = [self.caseDirList[c] for c in self.allHighBoxCases.case.values]
highBoxesCaseIndex = [self.caseDirList.index(c) for c in highBoxesCaseDirList]

files = []
for cond in range(self.nConditions):
for case in highBoxesCaseIndex:
for seed in range(self.nSeeds):
dirpath = self.getHRTurbSimPath(cond, case, seed)
for t in range(self.nTurbines):
files.append(f'{dirpath}/log.high{t+1}.seed{seed}.txt')
return files

# TODO create the same properties for the low res

def _checkInputs(self):
#### check if the turbine in the template FF input exists.
Expand Down Expand Up @@ -656,8 +678,9 @@ def _checkInputs(self):

# Check turbine conditions arrays for consistency
if len(self.inflow_deg) != len(self.yaw_init):
raise ValueError(f'One row for each inflow angle should be given in yaw_init. '\
f'Currently {len(self.inflow_deg)} inflow angle(s) and {len(self.yaw_init)} yaw entrie(s)')
raise FFException(f"Asked for {len(self.yaw_init)} yaw condition(s) but provided {len(self.inflow_deg)} inflow angle(s). "\
f"Each yaw_init condition should have a corresponding inflow_deg. Duplicate inflow_deg as needed. Note "\
f"this is irrespective to the the vhub/shear/TIvalue sweeps.")

# Check reduced-order models
if self.ADmodel is None or self.ADmodel == 'ADyn':
Expand Down Expand Up @@ -727,7 +750,7 @@ def _checkInputs(self):
if None in (self.dt_high, self.ds_high, self.dt_low, self.ds_low):
WARN(f'One or more temporal or spatial resolution for low- and high-res domains were not given.\n'+
f'Estimated values for {_MOD_WAKE_STR[self.mod_wake]} wake model shown below.')
self._determine_resolutions_from_dummy_amrwind_grid()
self._determine_resolutions_from_dummy_les_grid()

# Check the temporal and spatial resolutions if provided
if self.dt_low != None and self.dt_high!= None:
Expand All @@ -746,7 +769,7 @@ def _checkInputs(self):



def _determine_resolutions_from_dummy_amrwind_grid(self):
def _determine_resolutions_from_dummy_les_grid(self):

from openfast_toolbox.fastfarm.AMRWindSimulation import AMRWindSimulation

Expand Down Expand Up @@ -781,12 +804,11 @@ def _determine_resolutions_from_dummy_amrwind_grid(self):
print(f'`ds_high = {2*amr.ds_high_les}`; ', end='')
print(f'`dt_low = {2*amr.dt_low_les}`; ', end='')
print(f'`ds_low = {2*amr.ds_low_les}`; ')
#print(f' If the values above are okay, you can safely ignore this warning.\n')

self.dt_high = amr.dt_high_les
self.ds_high = amr.dt_high_les
self.ds_high = amr.ds_high_les
self.dt_low = amr.dt_low_les
self.ds_low = amr.dt_low_les
self.ds_low = amr.ds_low_les



Expand All @@ -813,7 +835,7 @@ def _create_dir_structure(self):
self.condDirList = condDirList

# --- Creating Case List
caseDirList_ = []
caseDirList = []
for case in range(self.nCases):
# Recover information about current case for directory naming purposes
inflow_deg_ = self.allCases['inflow_deg' ].sel(case=case).values
Expand All @@ -822,27 +844,23 @@ def _create_dir_structure(self):
nFED_ = self.allCases['nFulllElastoDyn'].sel(case=case).values
yawCase_ = self.allCases['yawCase' ].sel(case=case).values

# Set current path name string. The case is of the following form: Case00_wdirp10_WSfalse_YMfalse_12fED_12ADyn
# Set current path name string. The case is of the following form: Case00_wdirp10_12fED_12ADyn
ndigits = len(str(self.nCases))
caseStr = f"Case{case:0{ndigits}d}_wdir{f'{int(inflow_deg_):+03d}'.replace('+','p').replace('-','m')}"
# Add standard sweeps to the case name
if self.sweepYM:
caseStr += f"_YM{str(misalignment_).lower()}"
#if self.sweepYM:
# caseStr += f"_YM{str(misalignment_).lower()}"
if self.sweepEDmodel:
caseStr += f"_{nFED_}fED"
if self.sweepADmodel:
caseStr += f"_{nADyn_}ADyn"

#caseStr = f"Case{case:0{ndigits}d}_wdir{f'{int(inflow_deg_):+03d}'.replace('+','p').replace('-','m')}"\
# f"_WS{str(wakeSteering_).lower()}_YM{str(misalignment_).lower()}"\
# f"_{nFED_}fED_{nADyn_}ADyn"
# If sweeping on yaw, then add yaw case to dir name
if len(np.unique(self.allCases.yawCase)) > 1:
caseStr += f"_yawCase{yawCase_}"

caseDirList_.append(caseStr)
caseDirList.append(caseStr)

self.caseDirList = caseDirList_
self.caseDirList = caseDirList

# --- Creating directories including seed directories
for cond in range(self.nConditions):
Expand Down Expand Up @@ -882,26 +900,35 @@ def _copy(self, src, dst, debug=False):


def _symlink(self, src, dst, debug=False):
# If src is a relative path, reconstruct the absolute path based on dst directory
if not os.path.isabs(src):
src_abs = os.path.normpath(os.path.join(os.path.dirname(dst), src))
else:
src_abs = src

if debug:
print('SRC:', src, os.path.exists(src))
print('DST:', dst, os.path.exists(dst))
error = f"Src file not found: {src}"
if not os.path.exists(src):
print('SRC ABS:', src_abs, os.path.exists(src_abs))
print('SRC REL:', src, os.path.exists(src))
print('DST :', dst, os.path.exists(dst))
error = f"Src file not found: {src_abs}"

if not os.path.exists(src_abs):
raise Exception(error)
#return error
if not os.path.exists(dst):
if self._can_create_symlinks:
# Unix-based
try:
os.symlink(src, dst)
except FileExistsError:
error = dst
error = f"Dst file already exists: {dst}. Skipping symlink."
else:
# Windows
try:
shutil.copy2(src, dst)
except FileExistsError:
if debug:
raise Exception(error)
error = dst
error = f"Dst file already exists: {dst}. Skipping copy."
return error


Expand Down Expand Up @@ -2267,7 +2294,7 @@ def TS_low_slurm_submit(self, qos='normal', A=None, t=None, p=None, inplace=True


def TS_low_createSymlinks(self):
# Create symbolic links for all of the time-series and the Low.bts files too
# Create symbolic links for all of the time-series and the Low.bts files
for cond in range(self.nConditions):
for case in range(self.nCases):
for seed in range(self.nSeeds):
Expand Down Expand Up @@ -2493,8 +2520,9 @@ def TS_high_batch_prepare(self, run=False, **kwargs):
ext = ".bat" if os.name == "nt" else ".sh"
batchfile = os.path.join(self.path, f'runAllHighBox{ext}')

TS_files = [f.replace('.bts', '.inp') for f in self.high_res_bts]
writeBatch(batchfile, TS_files, fastExe=self.tsbin, **kwargs)
TS_high_files = [f.replace('.bts', '.inp') for f in self.high_res_bts]
TS_high_logs = self.high_res_log
writeBatch(batchfile, TS_high_files, fastExe=self.tsbin, flags_after=[f"2>&1 | tee {log}" for log in TS_high_logs], **kwargs)
self.batchfile_high = batchfile

OK(f"Batch file written to {batchfile}")
Expand Down Expand Up @@ -2621,12 +2649,12 @@ def TS_high_create_symlink(self):
# In order to do the symlink let's check if the current case is source (has bts). If so, skip if. If not, find its equivalent source
casematch = self.allHighBoxCases['case'] == case
if len(np.where(casematch)) != 1:
raise ValueError (f'Something is wrong with the allHighBoxCases array. Found repeated case number. Stopping')
raise FFException('Something is wrong with the allHighBoxCases array. Found repeated case number. Stopping.')

src_id = np.where(casematch)[0]

if len(src_id) == 1:
# Current case is source (contains bts). Skipping
# Current case is source (contains bts). Skipping it.
continue

# If we are here, the case is destination. Let's find the first case with the same wdir for source
Expand All @@ -2638,18 +2666,21 @@ def TS_high_create_symlink(self):
src_case = src_xr['case'].values[0]
src_xr = src_xr.sel(case=src_case, drop=True)

# Let's make sure the src and destination are the same case, except yaw misalignment and ROM bools, and yaw angles
# The xarrays we are comparing here contains all self.nTurbines turbines and no info about seed
# Let's make sure the src and destination are the same case, except ROM bool and yaw angles
# The xarrays we are comparing here contain all self.nTurbines turbines and no info about seed
xr.testing.assert_equal(src_xr, dst_xr)

# Now that we have the correct arrays, we perform the loop on the turbines and seeds
for t in range(self.nTurbines):
for seed in range(self.nSeeds):
src = os.path.join(self.getHRTurbSimPath(cond, src_case, seed), f'HighT{t+1}.bts')
dst = os.path.join(self.getHRTurbSimPath(cond, case , seed), f'HighT{t+1}.bts')
#print(f'src is {src}')
#print(f'dst is {dst}')
#src = os.path.join('..', '..', '..', '..', self.condDirList[cond], self.caseDirList[src_case], f'Seed_{seed}', 'TurbSim', f'HighT{t+1}.bts')
print('Emmanuel Says: TODO Check the line below')
src = os.path.relpath(src, dst)
#print('Emmanuel Says: TODO Check the line below')
src = os.path.relpath(src, os.path.dirname(dst))
#print(f'rel src{src}\n')
self._symlink(src, dst)


Expand Down
9 changes: 6 additions & 3 deletions openfast_toolbox/postpro/postpro.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import numpy as np
import re
try:
from scipy.integrate import cumulative_trapezoid
from numpy import trapezoid
except:
from scipy.integrate import cumulative_trapezoid
except ImportError:
from scipy.integrate import cumtrapz as cumulative_trapezoid

try:
from numpy import trapezoid
except ImportError:
from numpy import trapz as trapezoid

import openfast_toolbox.io as weio
Expand Down
Loading