Skip to content

Commit fa0d603

Browse files
authored
Merge pull request #24 from ebranlard/misc_updates
Misc updates of IO/Tools/Postpro/Lin and Polar
2 parents ce9a6d2 + c9264de commit fa0d603

32 files changed

+2159
-941
lines changed

.github/workflows/development-pipeline.yml

+3-11
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,6 @@ jobs:
2121
strategy:
2222
matrix:
2323
include:
24-
- os: ubuntu-latest
25-
python-version: 3.7
26-
python: xvfb-run python3
27-
pip_arg: ""
28-
- os: ubuntu-latest
29-
python-version: 3.8
30-
python: xvfb-run python3
31-
pip_arg: ""
3224
- os: ubuntu-latest
3325
python-version: 3.9
3426
python: xvfb-run python3
@@ -41,11 +33,11 @@ jobs:
4133
python-version: 3.12
4234
python: xvfb-run python3
4335
pip_arg: ""
44-
- os: macos-11
45-
python-version: 3.11
36+
- os: macos-13
37+
python-version: 3.12
4638
python: python3
4739
pip_arg: ""
48-
- os: windows-2019
40+
- os: windows-2022
4941
python-version: 3.11
5042
python: python
5143
pip_arg: --user

openfast_toolbox/airfoils/Polar.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,18 @@ def __init__(self, filename=None, alpha=None, cl=None, cd=None, cm=None, Re=None
8484
cd = df['Cd'].values
8585
cm = df['Cm'].values
8686
if 'fs' in df.keys():
87-
print('[INFO] Using separating function from input file.')
87+
if verbose:
88+
print('[INFO] Using separating function from input file.')
8889
self.fs = df['fs'].values
8990
self._fs_lock = True
9091
if 'Cl_fs' in df.keys():
91-
print('[INFO] Using Cl fully separated from input file.')
92+
if verbose:
93+
print('[INFO] Using Cl fully separated from input file.')
9294
self.cl_fs =df['Cl_fs'].values
9395
self._cl_fs_lock = True
9496
if 'Cl_inv' in df.keys():
95-
print('[INFO] Using Cl inviscid from input file.')
97+
if verbose:
98+
print('[INFO] Using Cl inviscid from input file.')
9699
self.cl_inv = df['Cl_inv'].values
97100
self._cl_inv_lock = True
98101
# TODO we need a trigger if cl_inv provided, we should get alpha0 and slope from it
@@ -667,7 +670,7 @@ def __getCM(self, i, cmCoef, alpha, cl_ext, cd_ext, alpha_low_deg, alpha_high_de
667670
print("Angle encountered for which there is no CM table value " "(near +/-180 deg). Program will stop.")
668671
return cm_new
669672

670-
def unsteadyParams(self, window_offset=None, nMin=720):
673+
def unsteadyParams(self, window_offset=None, nMin=720, verbose=False):
671674
"""compute unsteady aero parameters used in AeroDyn input file
672675
673676
TODO Questions to solve:
@@ -737,7 +740,8 @@ def unsteadyParams(self, window_offset=None, nMin=720):
737740
try:
738741
alpha0cn = _find_alpha0(alpha, cn, window, direction='up', value_if_constant = 0.)
739742
except NoCrossingException:
740-
print("[WARN] Polar: Cn unsteady, cannot find zero crossing with up direction, trying down direction")
743+
if verbose:
744+
print("[WARN] Polar: Cn unsteady, cannot find zero crossing with up direction, trying down direction")
741745
alpha0cn = _find_alpha0(alpha, cn, window, direction='down')
742746

743747
# checks for inppropriate data (like cylinders)
@@ -751,7 +755,8 @@ def unsteadyParams(self, window_offset=None, nMin=720):
751755
try:
752756
a_MaxUpp, cn_MaxUpp, a_MaxLow, cn_MaxLow = _find_max_points(alpha, cn, alpha0, method="inflections")
753757
except NoStallDetectedException:
754-
print('[WARN] Polar: Cn unsteady, cannot find stall based on inflections, using min and max')
758+
if verbose:
759+
print('[WARN] Polar: Cn unsteady, cannot find stall based on inflections, using min and max')
755760
a_MaxUpp, cn_MaxUpp, a_MaxLow, cn_MaxLow = _find_max_points(alpha, cn, alpha0, method="minmax")
756761

757762
# --- cn slope
@@ -790,7 +795,8 @@ def unsteadyParams(self, window_offset=None, nMin=720):
790795
a_f07_Upp = xInter[2]
791796
a_f07_Low = xInter[0]
792797
else:
793-
print('[WARN] Polar: Cn unsteady, cn_f does not intersect cn 3 times. Intersections:{}.'.format(xInter))
798+
if verbose:
799+
print('[WARN] Polar: Cn unsteady, cn_f does not intersect cn 3 times. Intersections:{}.'.format(xInter))
794800
a_f07_Upp = abs(xInter[0])
795801
a_f07_Low = -abs(xInter[0])
796802

@@ -1745,6 +1751,8 @@ def _find_linear_region(x, y, nMin, x0=None):
17451751
iEnd = j + nMin
17461752
if x0 is not None:
17471753
sl = np.linalg.lstsq(x[iStart:iEnd], y[iStart:iEnd], rcond=None)[0][0]
1754+
if not np.isscalar(sl):
1755+
sl = sl[0]
17481756
slp[iStart, j] = sl
17491757
off[iStart, j] = x0
17501758
y_lin = x[iStart:iEnd] * sl

openfast_toolbox/airfoils/tests/test_run_Examples.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ def test_run_examples(self):
1919
# Add tests to class
2020
MyDir=os.path.dirname(__file__)
2121
files = glob.glob(os.path.join(MyDir,'../examples/[a-zA-Z]*.py'))
22-
import matplotlib.pyplot as plt
2322
print('\n--------------------------------------------------------------')
23+
import matplotlib.pyplot as plt
2424
for f in files:
25-
print('Running example script: {}'.format(f))
25+
print('Running: {}'.format(os.path.relpath(f, MyDir)))
2626
if hasattr(self,'subTest'):
2727
with self.subTest(filename=os.path.basename(f)):
2828
execfile(f, {'__name__': '__test__', 'print': lambda *_:None})
29-
plt.close('all')
29+
try:
30+
plt.close('all')
31+
except:
32+
pass
3033

3134

3235
if __name__ == '__main__':

openfast_toolbox/io/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def fileFormats(userpath=None, ignoreErrors=False, verbose=False):
8585
from .hawcstab2_pwr_file import HAWCStab2PwrFile
8686
from .hawcstab2_ind_file import HAWCStab2IndFile
8787
from .hawcstab2_cmb_file import HAWCStab2CmbFile
88+
from .gnuplot_file import GNUPlotFile
8889
from .mannbox_file import MannBoxFile
8990
from .flex_blade_file import FLEXBladeFile
9091
from .flex_profile_file import FLEXProfileFile
@@ -141,6 +142,7 @@ def addFormat(priority, fmt):
141142
addFormat(60, FileFormat(NetCDFFile))
142143
addFormat(60, FileFormat(VTKFile))
143144
addFormat(60, FileFormat(TDMSFile))
145+
addFormat(60, FileFormat(GNUPlotFile))
144146
addFormat(60, FileFormat(ParquetFile))
145147
addFormat(60, FileFormat(PickleFile))
146148
addFormat(70, FileFormat(CactusFile))

openfast_toolbox/io/bladed_out_file.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,11 @@ def read_bladed_sensor_file(sensorfile):
7272
try:
7373
# Combine the strings into one string
7474
combined_string = ''.join(sensorLines)
75-
76-
# Search everything betwee AXITICK and AXISLAB with a regex pattern
77-
t_line = re.search(r'(?<=AXITICK).+?(?=AXISLAB)', combined_string, flags=re.DOTALL)
78-
t_line=t_line.group(0)
75+
# Search for a regex pattern that spans across multiple strings
76+
line = re.search(r'(?<=AXITICK).+?(?=(AXISLAB|NVARS))', combined_string, flags=re.DOTALL)
77+
line=line.group(0)
7978
# Replace consecutive whitespace characters with a single space
80-
t_line = re.sub(r'\s+', ' ', t_line)
79+
t_line = re.sub(r'\s+', ' ', line)
8180
except:
8281
pass
8382

@@ -107,6 +106,7 @@ def read_bladed_sensor_file(sensorfile):
107106
except:
108107
pass
109108
def repUnits(s):
109+
s = s.replace('[[','[').replace(']]',']')
110110
s = s.replace('TT','s^2').replace('T','s').replace('A','rad')
111111
s = s.replace('P','W').replace('L','m').replace('F','N').replace('M','kg')
112112
return s
@@ -184,6 +184,10 @@ def read_bladed_output(sensorFilename, readTimeFilesOnly=False):
184184
data = np.fromfile(fid_2, sensorInfo['Precision'])
185185

186186
try:
187+
if nMajor==0:
188+
nMajor=int(np.floor(len(data)/nSections/nSensors))
189+
data=data[0:nMajor*nSections*nSensors]
190+
sensorInfo['nMajor']=nMajor
187191
if sensorInfo['NDIMENS'] == 3:
188192
data = np.reshape(data,(nMajor, nSections, nSensors), order='C')
189193

openfast_toolbox/io/csv_file.py

+67-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import os
2-
3-
from .file import File, WrongFormatError
42
import pandas as pd
53

4+
try:
5+
from .file import File, WrongFormatError
6+
except:
7+
File=dict
8+
WrongFormatError = type('WrongFormatError', (Exception,),{})
9+
610
class CSVFile(File):
711
"""
812
Read/write a CSV file.
@@ -51,7 +55,23 @@ def __init__(self, filename=None, sep=None, colNames=None, commentChar=None, com
5155
raise Exception('Provide either `commentChar` or `commentLines` for CSV file types')
5256
if (len(self.colNames)>0) and (self.colNamesLine is not None):
5357
raise Exception('Provide either `colNames` or `colNamesLine` for CSV file types')
54-
super(CSVFile, self).__init__(filename=filename,**kwargs)
58+
if filename:
59+
self.read(filename, **kwargs)
60+
else:
61+
self.filename = None
62+
63+
def read(self, filename=None, **kwargs):
64+
if filename:
65+
self.filename = filename
66+
if not self.filename:
67+
raise Exception('No filename provided')
68+
if not os.path.isfile(self.filename):
69+
raise OSError(2,'File not found:',self.filename)
70+
if os.stat(self.filename).st_size == 0:
71+
raise EmptyFileError('File is empty:',self.filename)
72+
# Calling children function
73+
self._read(**kwargs)
74+
5575

5676
def _read(self):
5777
COMMENT_CHAR=['#','!',';']
@@ -283,3 +303,47 @@ def __repr__(self):
283303
def _toDataFrame(self):
284304
return self.data
285305

306+
def to2DFields(self, **kwargs):
307+
import xarray as xr
308+
if len(kwargs.keys())>0:
309+
print('[WARN] CSVFile: to2DFields: ignored keys: ',kwargs.keys())
310+
if len(self.data)==0:
311+
return None
312+
M = self.data.values
313+
if self.data.columns[0].lower()=='index':
314+
M = M[:,1:]
315+
s1 = 'rows'
316+
s2 = 'columns'
317+
ds = xr.Dataset(coords={s1: range(M.shape[0]), s2: range(M.shape[1])})
318+
ds['data'] = ([s1, s2], M)
319+
return ds
320+
321+
# --------------------------------------------------------------------------------
322+
# --- Properties NOTE: copy pasted from file.py to make this file standalone..
323+
# --------------------------------------------------------------------------------
324+
@property
325+
def size(self):
326+
return os.path.getsize(self.filename)
327+
@property
328+
def encoding(self):
329+
import codecs
330+
import chardet
331+
""" Detects encoding"""
332+
try:
333+
byts = min(32, self.size)
334+
except TypeError:
335+
return None
336+
with open(self.filename, 'rb') as f:
337+
raw = f.read(byts)
338+
if raw.startswith(codecs.BOM_UTF8):
339+
return 'utf-8-sig'
340+
else:
341+
result = chardet.detect(raw)
342+
return result['encoding']
343+
344+
if __name__ == '__main__':
345+
f = CSVFile('C:/Work/Courses/440/project_solution/data/CFD_u.dat')
346+
print(f)
347+
ds = f.to2DFields()
348+
print(ds)
349+

openfast_toolbox/io/fast_input_file.py

+19
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,21 @@ def _read(self, IComment=None):
551551
i+=1;
552552
self.readBeamDynProps(lines,i)
553553
return
554+
elif line.upper().find('GLBDCM')>0:
555+
# BeamDyn DCM has no label.....
556+
self.addComment(lines[i]); i+=1
557+
self.addComment(lines[i]); i+=1
558+
d = getDict()
559+
d['label'] = 'GlbDCM'
560+
d['tabType'] = TABTYPE_NUM_NO_HEADER
561+
nTabLines = 3
562+
nHeaders = 0
563+
d['value'], d['tabColumnNames'], d['tabUnits'] = parseFASTNumTable(self.filename, lines[i:i+nTabLines],nTabLines, i, nHeaders, tableType='num', nOffset=0)
564+
i=i+3
565+
self.data.append(d)
566+
continue
567+
568+
#---The following 3 by 3 matrix is the direction cosine matirx ,GlbDCM(3,3),
554569
elif line.upper().find('OUTPUTS')>0:
555570
if 'Points' in self.keys() and 'dtM' in self.keys():
556571
OutList,i = parseFASTOutList(lines,i+1)
@@ -902,6 +917,9 @@ def mat_tostring(M,fmt='24.16e'):
902917
if np.size(d['value'],0) > 0 :
903918
s+='\n'
904919
s+='\n'.join('\t'.join('{:15.0f}'.format(x) for x in y) for y in data)
920+
elif d['tabType']==TABTYPE_NUM_NO_HEADER:
921+
data = d['value']
922+
s+='\n'.join('\t'.join( ('{:15.0f}'.format(x) if int(x)==x else '{:15.8e}'.format(x) ) for x in y) for y in d['value'])
905923
else:
906924
raise Exception('Unknown table type for variable {}'.format(d))
907925
if i<len(self.data)-1:
@@ -1325,6 +1343,7 @@ def parseFASTNumTable(filename,lines,n,iStart,nHeaders=2,tableType='num',nOffset
13251343
Units = None
13261344

13271345

1346+
i = 0
13281347
if len(lines)!=n+nHeaders+nOffset:
13291348
raise BrokenFormatError('Not enough lines in table: {} lines instead of {}\nFile:{}'.format(len(lines)-nHeaders,n,filename))
13301349
try:

openfast_toolbox/io/fast_input_file_graph.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def subdynToGraph(sd, propToNodes=False, propToElem=False):
134134
# --------------------------------------------------------------------------------}
135135
# --- HydroDyn
136136
# --------------------------------------------------------------------------------{
137-
def hydrodynToGraph(hd, propToNodes=False, propToElem=False):
137+
def hydrodynToGraph(hd, propToNodes=False, propToElem=False, verbose=False):
138138
"""
139139
hd: dict-like object as returned by weio
140140
@@ -196,7 +196,8 @@ def type2Color(Pot):
196196
if 'FillGroups' in hd.keys():
197197
# Filled members
198198
Graph.addMiscPropertySet('FillGroups')
199-
print('>>> TODO Filled Groups')
199+
if verbose:
200+
print('>>> TODO Filled Groups')
200201
#for ip,P in enumerate(hd['FillGroups']):
201202
# # FillNumM FillMList FillFSLoc FillDens
202203
# raise NotImplementedError('hydroDynToGraph, Fill List might not be properly set, verify below')

0 commit comments

Comments
 (0)