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

Commit 0fb4ab2

Browse files
authored
Merge pull request #57 from OpenFAST/f/fixmd
Fix reading of MoorDyn v2 input file
2 parents 3e4d84e + 0b912ed commit 0fb4ab2

File tree

4 files changed

+132
-16
lines changed

4 files changed

+132
-16
lines changed

pyFAST/input_output/fast_input_file.py

+62-11
Original file line numberDiff line numberDiff line change
@@ -355,12 +355,19 @@ def _read(self):
355355

356356
# --- Tables that can be detected based on the "Label" (second entry on line)
357357
# NOTE: MJointID1, used by SubDyn and HydroDyn
358-
NUMTAB_FROM_LAB_DETECT = ['NumAlf' , 'F_X' , 'MemberCd1' , 'MJointID1' , 'NOutLoc' , 'NOutCnt' , 'PropD' ,'Diam' ,'Type' ,'LineType' ]
359-
NUMTAB_FROM_LAB_DIM_VAR = ['NumAlf' , 'NKInpSt' , 'NCoefMembers' , 'NMembers' , 'NMOutputs' , 'NMOutputs' , 'NPropSets' ,'NTypes' ,'NConnects' ,'NLines' ]
360-
NUMTAB_FROM_LAB_VARNAME = ['AFCoeff' , 'TMDspProp' , 'MemberProp' , 'Members' , 'MemberOuts' , 'MemberOuts' , 'SectionProp' ,'LineTypes' ,'ConnectionProp' ,'LineProp' ]
361-
NUMTAB_FROM_LAB_NHEADER = [2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ]
362-
NUMTAB_FROM_LAB_NOFFSET = [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
363-
NUMTAB_FROM_LAB_TYPE = ['num' , 'num' , 'num' , 'mix' , 'num' , 'sdout' , 'num' ,'mix' ,'mix' ,'mix' ]
358+
NUMTAB_FROM_LAB_DETECT = ['NumAlf' , 'F_X' , 'MemberCd1' , 'MJointID1' , 'NOutLoc' , 'NOutCnt' , 'PropD' ]
359+
NUMTAB_FROM_LAB_DIM_VAR = ['NumAlf' , 'NKInpSt' , 'NCoefMembers' , 'NMembers' , 'NMOutputs' , 'NMOutputs' , 'NPropSets' ]
360+
NUMTAB_FROM_LAB_VARNAME = ['AFCoeff' , 'TMDspProp' , 'MemberProp' , 'Members' , 'MemberOuts' , 'MemberOuts' , 'SectionProp' ]
361+
NUMTAB_FROM_LAB_NHEADER = [2 , 2 , 2 , 2 , 2 , 2 , 2 ]
362+
NUMTAB_FROM_LAB_NOFFSET = [0 , 0 , 0 , 0 , 0 , 0 , 0 ]
363+
NUMTAB_FROM_LAB_TYPE = ['num' , 'num' , 'num' , 'mix' , 'num' , 'sdout' , 'num' ]
364+
# MoorDyn Version 1 and 2 (with AUTO for LAB_DIM_VAR)
365+
NUMTAB_FROM_LAB_DETECT += ['Diam' ,'Type' ,'LineType' , 'Attachment']
366+
NUMTAB_FROM_LAB_DIM_VAR += ['NTypes:AUTO','NConnects' ,'NLines:AUTO' , 'AUTO']
367+
NUMTAB_FROM_LAB_VARNAME += ['LineTypes' ,'ConnectionProp' ,'LineProp' , 'Points']
368+
NUMTAB_FROM_LAB_NHEADER += [ 2 , 2 , 2 , 2 ]
369+
NUMTAB_FROM_LAB_NOFFSET += [ 0 , 0 , 0 , 0 ]
370+
NUMTAB_FROM_LAB_TYPE += ['mix' ,'mix' ,'mix' , 'mix']
364371
# SubDyn
365372
NUMTAB_FROM_LAB_DETECT += ['GuyanDampSize' , 'YoungE' , 'YoungE' , 'EA' , 'MatDens' ]
366373
NUMTAB_FROM_LAB_DIM_VAR += [6 , 'NPropSets', 'NXPropSets', 'NCablePropSets' , 'NRigidPropSets']
@@ -484,6 +491,19 @@ def _read(self):
484491
i+=1;
485492
self.readBeamDynProps(lines,i)
486493
return
494+
elif line.upper().find('OUTPUTS')>0:
495+
if 'Points' in self.keys() and 'dtM' in self.keys():
496+
OutList,i = parseFASTOutList(lines,i+1)
497+
d = getDict()
498+
d['label'] = 'Outlist'
499+
d['descr'] = ''
500+
d['tabType'] = TABTYPE_FIL # TODO
501+
d['value'] = OutList
502+
self.addComment('------------------------ OUTPUTS --------------------------------------------')
503+
self.data.append(d)
504+
self.addComment('END')
505+
self.addComment('------------------------- need this line --------------------------------------')
506+
return
487507

488508
# --- Parsing of standard lines: value(s) key comment
489509
line = lines[i]
@@ -601,7 +621,6 @@ def _read(self):
601621
self.data.append(dd)
602622

603623
d['label'] = NUMTAB_FROM_LAB_VARNAME[ii]
604-
d['tabDimVar'] = NUMTAB_FROM_LAB_DIM_VAR[ii]
605624
if d['label'].lower()=='afcoeff' :
606625
d['tabType'] = TABTYPE_NUM_WITH_HEADERCOM
607626
else:
@@ -611,10 +630,28 @@ def _read(self):
611630
d['tabType'] = TABTYPE_NUM_SUBDYNOUT
612631
else:
613632
d['tabType'] = TABTYPE_MIX_WITH_HEADER
614-
if isinstance(d['tabDimVar'],int):
633+
# Finding table dimension (number of lines)
634+
tabDimVar = NUMTAB_FROM_LAB_DIM_VAR[ii]
635+
if isinstance(tabDimVar, int): # dimension hardcoded
636+
d['tabDimVar'] = tabDimVar
615637
nTabLines = d['tabDimVar']
616638
else:
617-
nTabLines = self[d['tabDimVar']+labOffset]
639+
# We either use a variable name or "AUTO" to find the number of rows
640+
tabDimVars = tabDimVar.split(':')
641+
for tabDimVar in tabDimVars:
642+
d['tabDimVar'] = tabDimVar
643+
if tabDimVar=='AUTO':
644+
# Determine table dimension automatically
645+
nTabLines = findNumberOfTableLines(lines[i+nHeaders:], break_chars=['---','!','#'])
646+
break
647+
else:
648+
try:
649+
nTabLines = self[tabDimVar+labOffset]
650+
break
651+
except KeyError:
652+
#print('Cannot determine table dimension using {}'.format(tabDimVar))
653+
# Hopefully this table has AUTO as well
654+
pass
618655

619656
d['label'] += labOffset
620657
#print('Reading table {} Dimension {} (based on {})'.format(d['label'],nTabLines,d['tabDimVar']));
@@ -758,10 +795,13 @@ def mat_tostring(M,fmt='24.16e'):
758795
s+='\n'.join('\t'.join('{:15.8e}'.format(x) for x in y) for y in d['value'])
759796
elif d['tabType']==TABTYPE_FIL:
760797
#f.write('{} {} {}\n'.format(d['value'][0],d['tabDetect'],d['descr']))
798+
label = d['label']
799+
if 'kbot' in self.keys(): # Moordyn has no 'OutList' label..
800+
label=''
761801
if len(d['value'])==1:
762-
s+='{} {} {}'.format(d['value'][0],d['label'],d['descr']) # TODO?
802+
s+='{} {} {}'.format(d['value'][0], label, d['descr']) # TODO?
763803
else:
764-
s+='{} {} {}\n'.format(d['value'][0],d['label'],d['descr']) # TODO?
804+
s+='{} {} {}\n'.format(d['value'][0], label, d['descr']) # TODO?
765805
s+='\n'.join(fil for fil in d['value'][1:])
766806
elif d['tabType']==TABTYPE_NUM_BEAMDYN:
767807
# TODO use dedicated sub-class
@@ -1186,6 +1226,17 @@ def detectUnits(s,nRef):
11861226
return Units
11871227

11881228

1229+
def findNumberOfTableLines(lines, break_chars):
1230+
""" Loop through lines until a one of the "break character is found"""
1231+
for i, l in enumerate(lines):
1232+
for bc in break_chars:
1233+
if l.startswith(bc):
1234+
return i
1235+
# Not found
1236+
print('[FAIL] end of table not found')
1237+
return len(lines)
1238+
1239+
11891240
def parseFASTNumTable(filename,lines,n,iStart,nHeaders=2,tableType='num',nOffset=0, varNumLines=''):
11901241
"""
11911242
First lines of data starts at: nHeaders+nOffset
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--------------------- MoorDyn Input File ------------------------------------
2+
Mooring system for OC4-DeepCwind Semi
3+
FALSE Echo - echo the input file data (flag)
4+
----------------------- LINE TYPES ------------------------------------------
5+
Name Diam MassDen EA BA/-zeta EI Cd Ca CdAx CaAx
6+
(-) (m) (kg/m) (N) (N-s/-) (-) (-) (-) (-) (-)
7+
main 0.0766 113.35 7.536E8 -1.0 0 2.0 0.8 0.4 0.25
8+
---------------------- POINTS --------------------------------
9+
ID Attachment X Y Z M V CdA CA
10+
(-) (-) (m) (m) (m) (kg) (m^3) (m^2) (-)
11+
1 Fixed 418.8 725.383 -200.0 0 0 0 0
12+
2 Fixed -837.6 0.0 -200.0 0 0 0 0
13+
3 Fixed 418.8 -725.383 -200.0 0 0 0 0
14+
4 Vessel 20.434 35.393 -14.0 0 0 0 0
15+
5 Vessel -40.868 0.0 -14.0 0 0 0 0
16+
6 Vessel 20.434 -35.393 -14.0 0 0 0 0
17+
---------------------- LINES --------------------------------------
18+
ID LineType AttachA AttachB UnstrLen NumSegs Outputs
19+
(-) (-) (-) (-) (m) (-) (-)
20+
1 main 1 4 835.35 20 -
21+
2 main 2 5 835.35 20 -
22+
3 main 3 6 835.35 20 -
23+
---------------------- SOLVER OPTIONS ---------------------------------------
24+
0.001 dtM - time step to use in mooring integration (s)
25+
3.0e6 kbot - bottom stiffness (Pa/m)
26+
3.0e5 cbot - bottom damping (Pa-s/m)
27+
2.0 dtIC - time interval for analyzing convergence during IC gen (s)
28+
60.0 TmaxIC - max time for ic gen (s)
29+
4.0 CdScaleIC - factor by which to scale drag coefficients during dynamic relaxation (-)
30+
0.01 threshIC - threshold for IC convergence (-)
31+
------------------------ OUTPUTS --------------------------------------------
32+
FairTen1
33+
FairTen2
34+
FairTen3
35+
AnchTen1
36+
AnchTen2
37+
AnchTen3
38+
END
39+
------------------------- need this line --------------------------------------

pyFAST/input_output/tests/test_fast_input.py

+31-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import numpy as np
44

5-
from .helpers_for_test import MyDir, reading_test
5+
from pyFAST.input_output.tests.helpers_for_test import MyDir, reading_test
66

77
import pyFAST
88
from pyFAST.input_output import FASTInputFile
@@ -67,10 +67,6 @@ def test_FASTIn(self):
6767
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
6868
self.assertEqual(F['PitManRat(1)'],2)
6969

70-
F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD.dat'))
71-
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
72-
self.assertEqual(float(F['LineTypes'][0,1]),0.02)
73-
7470
def test_FASTWnd(self):
7571
F=FASTWndFile(os.path.join(MyDir,'FASTWnd.wnd'))
7672
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
@@ -87,7 +83,37 @@ def test_FASTInGraph(self):
8783
#graph = F.toGraph()
8884
# self.assertEqual(len(graph.Nodes), 2)
8985
# self.assertEqual(len(graph.Elements), 1)
86+
def test_FASTInMoorDyn(self):
87+
# MoorDyn version 1
88+
F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD-v1.dat'))
89+
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
90+
self.assertEqual(float(F['LineTypes'][0,1]),0.02)
91+
92+
# MoorDyn version 2
93+
F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD-v2.dat'))
94+
#F.write(os.path.join(MyDir,'FASTIn_MD-v2.dat---OUT'))
95+
self.assertTrue('Points' in F.keys())
96+
self.assertTrue('LineTypes' in F.keys())
97+
self.assertTrue('LineProp' in F.keys())
98+
self.assertEqual(F['LineProp'].shape , (3,7))
99+
self.assertEqual(F['LineTypes'].shape , (1,10))
100+
self.assertEqual(F['Points'].shape , (6,9))
101+
self.assertEqual(len(F['Outlist']) , 6)
102+
self.assertEqual(F['Outlist'][0] , 'FairTen1')
103+
self.assertEqual(F['LineProp'][0,0] , '1')
104+
self.assertEqual(F['LineProp'][0,1] , 'main')
105+
self.assertEqual(F['LineProp'][0,6] , '-')
106+
107+
def test_FASTInAirfoil(self):
108+
F=FASTInputFile(os.path.join(MyDir,'FASTIn_AD15_arfl.dat'))
109+
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
110+
self.assertTrue('InterpOrd' in F.keys())
111+
self.assertTrue('AFCoeff' in F.keys())
112+
self.assertEqual(F['AFCoeff'].shape, (30,4))
90113

91114
if __name__ == '__main__':
115+
from welib.tools.clean_exceptions import *
92116
#Test().test_FASTIn()
117+
#Test().test_FASTInAirfoil()
118+
#Test().test_FASTInMoorDyn()
93119
unittest.main()

0 commit comments

Comments
 (0)