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

Fix reading of MoorDyn v2 input file #57

Merged
merged 2 commits into from
Jan 13, 2023
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/development-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8] #
python-version: [3.7, 3.8, 3.9, 3.11] #
steps:
- name: Checkout
uses: actions/checkout@main
Expand Down
73 changes: 62 additions & 11 deletions pyFAST/input_output/fast_input_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,12 +351,19 @@ def _read(self):

# --- Tables that can be detected based on the "Label" (second entry on line)
# NOTE: MJointID1, used by SubDyn and HydroDyn
NUMTAB_FROM_LAB_DETECT = ['NumAlf' , 'F_X' , 'MemberCd1' , 'MJointID1' , 'NOutLoc' , 'NOutCnt' , 'PropD' ,'Diam' ,'Type' ,'LineType' ]
NUMTAB_FROM_LAB_DIM_VAR = ['NumAlf' , 'NKInpSt' , 'NCoefMembers' , 'NMembers' , 'NMOutputs' , 'NMOutputs' , 'NPropSets' ,'NTypes' ,'NConnects' ,'NLines' ]
NUMTAB_FROM_LAB_VARNAME = ['AFCoeff' , 'TMDspProp' , 'MemberProp' , 'Members' , 'MemberOuts' , 'MemberOuts' , 'SectionProp' ,'LineTypes' ,'ConnectionProp' ,'LineProp' ]
NUMTAB_FROM_LAB_NHEADER = [2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ]
NUMTAB_FROM_LAB_NOFFSET = [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
NUMTAB_FROM_LAB_TYPE = ['num' , 'num' , 'num' , 'mix' , 'num' , 'sdout' , 'num' ,'mix' ,'mix' ,'mix' ]
NUMTAB_FROM_LAB_DETECT = ['NumAlf' , 'F_X' , 'MemberCd1' , 'MJointID1' , 'NOutLoc' , 'NOutCnt' , 'PropD' ]
NUMTAB_FROM_LAB_DIM_VAR = ['NumAlf' , 'NKInpSt' , 'NCoefMembers' , 'NMembers' , 'NMOutputs' , 'NMOutputs' , 'NPropSets' ]
NUMTAB_FROM_LAB_VARNAME = ['AFCoeff' , 'TMDspProp' , 'MemberProp' , 'Members' , 'MemberOuts' , 'MemberOuts' , 'SectionProp' ]
NUMTAB_FROM_LAB_NHEADER = [2 , 2 , 2 , 2 , 2 , 2 , 2 ]
NUMTAB_FROM_LAB_NOFFSET = [0 , 0 , 0 , 0 , 0 , 0 , 0 ]
NUMTAB_FROM_LAB_TYPE = ['num' , 'num' , 'num' , 'mix' , 'num' , 'sdout' , 'num' ]
# MoorDyn Version 1 and 2 (with AUTO for LAB_DIM_VAR)
NUMTAB_FROM_LAB_DETECT += ['Diam' ,'Type' ,'LineType' , 'Attachment']
NUMTAB_FROM_LAB_DIM_VAR += ['NTypes:AUTO','NConnects' ,'NLines:AUTO' , 'AUTO']
NUMTAB_FROM_LAB_VARNAME += ['LineTypes' ,'ConnectionProp' ,'LineProp' , 'Points']
NUMTAB_FROM_LAB_NHEADER += [ 2 , 2 , 2 , 2 ]
NUMTAB_FROM_LAB_NOFFSET += [ 0 , 0 , 0 , 0 ]
NUMTAB_FROM_LAB_TYPE += ['mix' ,'mix' ,'mix' , 'mix']
# SubDyn
NUMTAB_FROM_LAB_DETECT += ['GuyanDampSize' , 'YoungE' , 'YoungE' , 'EA' , 'MatDens' ]
NUMTAB_FROM_LAB_DIM_VAR += [6 , 'NPropSets', 'NXPropSets', 'NCablePropSets' , 'NRigidPropSets']
Expand Down Expand Up @@ -480,6 +487,19 @@ def _read(self):
i+=1;
self.readBeamDynProps(lines,i)
return
elif line.upper().find('OUTPUTS')>0:
if 'Points' in self.keys() and 'dtM' in self.keys():
OutList,i = parseFASTOutList(lines,i+1)
d = getDict()
d['label'] = 'Outlist'
d['descr'] = ''
d['tabType'] = TABTYPE_FIL # TODO
d['value'] = OutList
self.addComment('------------------------ OUTPUTS --------------------------------------------')
self.data.append(d)
self.addComment('END')
self.addComment('------------------------- need this line --------------------------------------')
return

# --- Parsing of standard lines: value(s) key comment
line = lines[i]
Expand Down Expand Up @@ -597,7 +617,6 @@ def _read(self):
self.data.append(dd)

d['label'] = NUMTAB_FROM_LAB_VARNAME[ii]
d['tabDimVar'] = NUMTAB_FROM_LAB_DIM_VAR[ii]
if d['label'].lower()=='afcoeff' :
d['tabType'] = TABTYPE_NUM_WITH_HEADERCOM
else:
Expand All @@ -607,10 +626,28 @@ def _read(self):
d['tabType'] = TABTYPE_NUM_SUBDYNOUT
else:
d['tabType'] = TABTYPE_MIX_WITH_HEADER
if isinstance(d['tabDimVar'],int):
# Finding table dimension (number of lines)
tabDimVar = NUMTAB_FROM_LAB_DIM_VAR[ii]
if isinstance(tabDimVar, int): # dimension hardcoded
d['tabDimVar'] = tabDimVar
nTabLines = d['tabDimVar']
else:
nTabLines = self[d['tabDimVar']+labOffset]
# We either use a variable name or "AUTO" to find the number of rows
tabDimVars = tabDimVar.split(':')
for tabDimVar in tabDimVars:
d['tabDimVar'] = tabDimVar
if tabDimVar=='AUTO':
# Determine table dimension automatically
nTabLines = findNumberOfTableLines(lines[i+nHeaders:], break_chars=['---','!','#'])
break
else:
try:
nTabLines = self[tabDimVar+labOffset]
break
except KeyError:
#print('Cannot determine table dimension using {}'.format(tabDimVar))
# Hopefully this table has AUTO as well
pass

d['label'] += labOffset
#print('Reading table {} Dimension {} (based on {})'.format(d['label'],nTabLines,d['tabDimVar']));
Expand Down Expand Up @@ -755,10 +792,13 @@ def mat_tostring(M,fmt='24.16e'):
s+='\n'.join('\t'.join('{:15.8e}'.format(x) for x in y) for y in d['value'])
elif d['tabType']==TABTYPE_FIL:
#f.write('{} {} {}\n'.format(d['value'][0],d['tabDetect'],d['descr']))
label = d['label']
if 'kbot' in self.keys(): # Moordyn has no 'OutList' label..
label=''
if len(d['value'])==1:
s+='{} {} {}'.format(d['value'][0],d['label'],d['descr']) # TODO?
s+='{} {} {}'.format(d['value'][0], label, d['descr']) # TODO?
else:
s+='{} {} {}\n'.format(d['value'][0],d['label'],d['descr']) # TODO?
s+='{} {} {}\n'.format(d['value'][0], label, d['descr']) # TODO?
s+='\n'.join(fil for fil in d['value'][1:])
elif d['tabType']==TABTYPE_NUM_BEAMDYN:
# TODO use dedicated sub-class
Expand Down Expand Up @@ -1183,6 +1223,17 @@ def detectUnits(s,nRef):
return Units


def findNumberOfTableLines(lines, break_chars):
""" Loop through lines until a one of the "break character is found"""
for i, l in enumerate(lines):
for bc in break_chars:
if l.startswith(bc):
return i
# Not found
print('[FAIL] end of table not found')
return len(lines)


def parseFASTNumTable(filename,lines,n,iStart,nHeaders=2,tableType='num',nOffset=0, varNumLines=''):
"""
First lines of data starts at: nHeaders+nOffset
Expand Down
39 changes: 39 additions & 0 deletions pyFAST/input_output/tests/example_files/FASTIn_MD-v2.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--------------------- MoorDyn Input File ------------------------------------
Mooring system for OC4-DeepCwind Semi
FALSE Echo - echo the input file data (flag)
----------------------- LINE TYPES ------------------------------------------
Name Diam MassDen EA BA/-zeta EI Cd Ca CdAx CaAx
(-) (m) (kg/m) (N) (N-s/-) (-) (-) (-) (-) (-)
main 0.0766 113.35 7.536E8 -1.0 0 2.0 0.8 0.4 0.25
---------------------- POINTS --------------------------------
ID Attachment X Y Z M V CdA CA
(-) (-) (m) (m) (m) (kg) (m^3) (m^2) (-)
1 Fixed 418.8 725.383 -200.0 0 0 0 0
2 Fixed -837.6 0.0 -200.0 0 0 0 0
3 Fixed 418.8 -725.383 -200.0 0 0 0 0
4 Vessel 20.434 35.393 -14.0 0 0 0 0
5 Vessel -40.868 0.0 -14.0 0 0 0 0
6 Vessel 20.434 -35.393 -14.0 0 0 0 0
---------------------- LINES --------------------------------------
ID LineType AttachA AttachB UnstrLen NumSegs Outputs
(-) (-) (-) (-) (m) (-) (-)
1 main 1 4 835.35 20 -
2 main 2 5 835.35 20 -
3 main 3 6 835.35 20 -
---------------------- SOLVER OPTIONS ---------------------------------------
0.001 dtM - time step to use in mooring integration (s)
3.0e6 kbot - bottom stiffness (Pa/m)
3.0e5 cbot - bottom damping (Pa-s/m)
2.0 dtIC - time interval for analyzing convergence during IC gen (s)
60.0 TmaxIC - max time for ic gen (s)
4.0 CdScaleIC - factor by which to scale drag coefficients during dynamic relaxation (-)
0.01 threshIC - threshold for IC convergence (-)
------------------------ OUTPUTS --------------------------------------------
FairTen1
FairTen2
FairTen3
AnchTen1
AnchTen2
AnchTen3
END
------------------------- need this line --------------------------------------
36 changes: 31 additions & 5 deletions pyFAST/input_output/tests/test_fast_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import numpy as np

from .helpers_for_test import MyDir, reading_test
from pyFAST.input_output.tests.helpers_for_test import MyDir, reading_test

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

F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD.dat'))
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
self.assertEqual(float(F['LineTypes'][0,1]),0.02)

def test_FASTWnd(self):
F=FASTWndFile(os.path.join(MyDir,'FASTWnd.wnd'))
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
Expand All @@ -87,7 +83,37 @@ def test_FASTInGraph(self):
#graph = F.toGraph()
# self.assertEqual(len(graph.Nodes), 2)
# self.assertEqual(len(graph.Elements), 1)
def test_FASTInMoorDyn(self):
# MoorDyn version 1
F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD-v1.dat'))
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
self.assertEqual(float(F['LineTypes'][0,1]),0.02)

# MoorDyn version 2
F=FASTInputFile(os.path.join(MyDir,'FASTIn_MD-v2.dat'))
#F.write(os.path.join(MyDir,'FASTIn_MD-v2.dat---OUT'))
self.assertTrue('Points' in F.keys())
self.assertTrue('LineTypes' in F.keys())
self.assertTrue('LineProp' in F.keys())
self.assertEqual(F['LineProp'].shape , (3,7))
self.assertEqual(F['LineTypes'].shape , (1,10))
self.assertEqual(F['Points'].shape , (6,9))
self.assertEqual(len(F['Outlist']) , 6)
self.assertEqual(F['Outlist'][0] , 'FairTen1')
self.assertEqual(F['LineProp'][0,0] , '1')
self.assertEqual(F['LineProp'][0,1] , 'main')
self.assertEqual(F['LineProp'][0,6] , '-')

def test_FASTInAirfoil(self):
F=FASTInputFile(os.path.join(MyDir,'FASTIn_AD15_arfl.dat'))
F.test_ascii(bCompareWritesOnly=True,bDelete=True)
self.assertTrue('InterpOrd' in F.keys())
self.assertTrue('AFCoeff' in F.keys())
self.assertEqual(F['AFCoeff'].shape, (30,4))

if __name__ == '__main__':
from welib.tools.clean_exceptions import *
#Test().test_FASTIn()
#Test().test_FASTInAirfoil()
#Test().test_FASTInMoorDyn()
unittest.main()