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

Commit 7fa23ef

Browse files
committed
IO: adding graph and SubDyn Summary file mode example
1 parent 4e6ea5f commit 7fa23ef

File tree

6 files changed

+1488
-1
lines changed

6 files changed

+1488
-1
lines changed

data/example_files/FASTSum_5MW_OC3Mnpl.SD.sum.yaml

Lines changed: 387 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
- Read a SubDyn summary file and extract the Guyan and Craig Bampton Modes
3+
- Convert the SubDyn file to JSON for 3D visualization
4+
"""
5+
import numpy as np
6+
import pandas as pd
7+
import matplotlib.pyplot as plt
8+
import os
9+
# Local
10+
from pyFAST.input_output.fast_summary_file import FASTSummaryFile
11+
12+
# Get current directory so this script can be called from any location
13+
scriptDir = os.path.dirname(__file__)
14+
15+
16+
# Read SubDyn summary file
17+
filename = os.path.join(scriptDir, '../../../data/example_files/FASTSum_5MW_OC3Mnpl.SD.sum.yaml')
18+
sds = FASTSummaryFile(filename)
19+
20+
# Optional: Extract Guyan and Craig-Bampton modes
21+
#dispGY, posGY, _, dispCB, posCB, _ = sds.getModes()
22+
23+
# Extract Guyan and Craig-Bampton modes and store them in a DataFrame
24+
df = sds.toDataFrame() #sortDim=2, removeZero=True)
25+
print(df.keys())
26+
plt.plot(df['z_[m]'], df['GuyanMode1x_[m]'], 'o')
27+
plt.plot(df['z_[m]'], df['GuyanMode5x_[m]'], 'o')
28+
29+
30+
# Store as JSON for 3d visualization with viz3danim
31+
sds.toJSON('_OUT.json')
32+
sds.toGraph().toJSON('_OUT2.json')
33+
34+
35+
if __name__ == '__main__':
36+
plt.show()
37+
if __name__=='__test__':
38+
pass
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
import numpy as np
2+
import pandas as pd
3+
import matplotlib.pyplot as plt
4+
5+
# Local
6+
# try:
7+
from pyFAST.input_output.tools.graph import *
8+
# except ImportError:
9+
#from welib.FEM.graph import *
10+
11+
12+
# --------------------------------------------------------------------------------}
13+
# --- Wrapper to convert a "fast" input file dictionary into a graph
14+
# --------------------------------------------------------------------------------{
15+
def fastToGraph(data, **kwargs):
16+
if 'BeamProp' in data.keys():
17+
return subdynToGraph(data, **kwargs)
18+
19+
if 'SmplProp' in data.keys():
20+
return hydrodynToGraph(data, **kwargs)
21+
22+
if 'DOF2Nodes' in data.keys():
23+
return subdynSumToGraph(data, **kwargs)
24+
25+
raise NotImplementedError('Graph for object with keys: {}'.format(data.keys()))
26+
27+
# --------------------------------------------------------------------------------}
28+
# --- SubDyn
29+
# --------------------------------------------------------------------------------{
30+
def subdynToGraph(sd, propToNodes=False, propToElem=False):
31+
"""
32+
sd: dict-like object as returned by weio
33+
34+
-propToNodes: if True, the element properties are also transferred to the nodes for convenience.
35+
NOTE: this is not the default because a same node can have two different diameters in SubDyn (it's by element)
36+
"""
37+
type2Color=[
38+
(0.1,0.1,0.1), # Watchout based on background
39+
(0.753,0.561,0.05), # 1 Beam
40+
(0.541,0.753,0.05), # 2 Cable
41+
(0.753,0.05,0.204), # 3 Rigid
42+
(0.918,0.702,0.125), # 3 Rigid
43+
]
44+
45+
Graph = GraphModel()
46+
# --- Properties
47+
if 'BeamProp' in sd.keys():
48+
BProps = sd['BeamProp']
49+
Graph.addNodePropertySet('Beam')
50+
for ip,P in enumerate(BProps):
51+
prop= NodeProperty(ID=P[0], E=P[1], G=P[2], rho=P[3], D=P[4], t=P[5] )
52+
Graph.addNodeProperty('Beam',prop)
53+
54+
if 'CableProp' in sd.keys():
55+
CProps = sd['CableProp']
56+
Graph.addNodePropertySet('Cable')
57+
for ip,P in enumerate(CProps):
58+
Chan = -1 if len(P)<5 else P[4]
59+
prop= NodeProperty(ID=P[0], EA=P[1], rho=P[2], T0=P[3], Chan=Chan)
60+
Graph.addNodeProperty('Cable',prop)
61+
62+
if 'RigidProp' in sd.keys():
63+
RProps = sd['RigidProp']
64+
Graph.addNodePropertySet('Rigid')
65+
for ip,P in enumerate(RProps):
66+
prop= NodeProperty(ID=P[0], rho=P[1])
67+
Graph.addNodeProperty('Rigid',prop)
68+
69+
# --- Nodes and DOFs
70+
Nodes = sd['Joints']
71+
for iNode,N in enumerate(Nodes):
72+
Type= 1 if len(N)<=4 else N[4]
73+
node = Node(ID=N[0], x=N[1], y=N[2], z=N[3], Type=Type)
74+
Graph.addNode(node)
75+
76+
# --- Elements
77+
Members = sd['Members'].astype(int)
78+
PropSets = ['Beam','Cable','Rigid']
79+
for ie,E in enumerate(Members):
80+
Type=1 if len(E)==5 else E[5]
81+
#elem= Element(E[0], E[1:3], propset=PropSets[Type-1], propIDs=E[3:5])
82+
elem= Element(E[0], E[1:3], Type=PropSets[Type-1], propIDs=E[3:5], propset=PropSets[Type-1])
83+
elem.data['object']='cylinder'
84+
elem.data['color'] = type2Color[Type]
85+
Graph.addElement(elem)
86+
# Nodal prop data
87+
if propToNodes:
88+
# NOTE: this is disallowed by default because a same node can have two different diameters in SubDyn (it's by element)
89+
Graph.setElementNodalProp(elem, propset=PropSets[Type-1], propIDs=E[3:5])
90+
if propToElem:
91+
Graph.setElementNodalPropToElem(elem) # TODO, this shouldn't be needed
92+
93+
# --- Concentrated Masses (in global coordinates), node data
94+
for iC, CM in enumerate(sd['ConcentratedMasses']):
95+
#CMJointID, JMass, JMXX, JMYY, JMZZ, JMXY, JMXZ, JMYZ, MCGX, MCGY, MCGZ
96+
nodeID = CM[0]
97+
n = Graph.getNode(nodeID)
98+
M66 = np.zeros((6,6))
99+
if len(CM)==11:
100+
m = CM[1]
101+
x, y ,z = (CM[8], CM[9], CM[10])
102+
Jxx = CM[2]; Jyy = CM[3]; Jzz = CM[4]
103+
Jxy = CM[5]; Jxz = CM[6]; Jyz = CM[7];
104+
else:
105+
raise NotImplementedError('TODO legacy')
106+
m = CM[1]
107+
Jxx = CM[2]; Jyy = CM[3]; Jzz = CM[4]
108+
Jxy = 0; Jyz =0; Jzz = 0; x,y,z=0,0,0
109+
M66[0, :] =[ m , 0 , 0 , 0 , z*m , -y*m ]
110+
M66[1, :] =[ 0 , m , 0 , -z*m , 0 , x*m ]
111+
M66[2, :] =[ 0 , 0 , m , y*m , -x*m , 0 ]
112+
M66[3, :] =[ 0 , -z*m , y*m , Jxx + m*(y**2+z**2) , Jxy - m*x*y , Jxz - m*x*z ]
113+
M66[4, :] =[ z*m , 0 , -x*m , Jxy - m*x*y , Jyy + m*(x**2+z**2) , Jyz - m*y*z ]
114+
M66[5, :] =[ -y*m , x*m , 0 , Jxz - m*x*z , Jyz - m*y*z , Jzz + m*(x**2+y**2) ]
115+
n.setData({'addedMassMatrix':M66})
116+
117+
# Nodal data
118+
for iN,N in enumerate(sd['InterfaceJoints']):
119+
nodeID = int(N[0])
120+
Graph.setNodalData(nodeID,IBC=N[1:])
121+
for iN,N in enumerate(sd['BaseJoints']):
122+
NN=[int(n) if i<7 else n for i,n in enumerate(N)]
123+
nodeID = NN[0]
124+
Graph.setNodalData(nodeID,RBC=NN[1:])
125+
# print('CMass')
126+
# print(sd['ConcentratedMasses'])
127+
128+
return Graph
129+
130+
131+
132+
133+
134+
# --------------------------------------------------------------------------------}
135+
# --- HydroDyn
136+
# --------------------------------------------------------------------------------{
137+
def hydrodynToGraph(hd, propToNodes=False, propToElem=False):
138+
"""
139+
hd: dict-like object as returned by weio
140+
141+
-propToNodes: if True, the element properties are also transferred to the nodes for convenience.
142+
NOTE: this is not the default because a same node can have two different diameters in SubDyn (it's by element)
143+
144+
- propToElem: This might be due to misunderstanding of graph..
145+
"""
146+
def type2Color(Pot):
147+
if Pot:
148+
return (0.753,0.05,0.204), # Pot flow
149+
else:
150+
return (0.753,0.561,0.05), # Morison
151+
152+
153+
Graph = GraphModel()
154+
155+
# --- Properties
156+
if 'SectionProp' in hd.keys():
157+
# NOTE: setting it as element property since two memebrs may connect on the same node with different diameters/thicknesses
158+
Graph.addNodePropertySet('Section')
159+
for ip,P in enumerate(hd['SectionProp']):
160+
# PropSetID PropD PropThck
161+
prop= NodeProperty(ID=P[0], D=P[1], t=P[2])
162+
Graph.addNodeProperty('Section',prop)
163+
164+
# --- Hydro Coefs - will be stored in AxCoefs, SimpleCoefs, DepthCoefs, MemberCoefs
165+
if 'AxCoefs' in hd.keys():
166+
Graph.addNodePropertySet('AxCoefs')
167+
for ip,P in enumerate(hd['AxCoefs']):
168+
prop= NodeProperty(ID=P[0], JAxCd=P[1], JAxCa=P[2], JAxCp=P[3])
169+
Graph.addNodeProperty('AxCoefs',prop)
170+
if 'SmplProp' in hd.keys():
171+
Graph.addNodePropertySet('SimpleCoefs')
172+
for ip,P in enumerate(hd['SmplProp']):
173+
# SimplCd SimplCdMG SimplCa SimplCaMG SimplCp SimplCpMG SimplAxCd SimplAxCdMG SimplAxCa SimplAxCaMG SimplAxCp SimplAxCpMG
174+
if len(P)==12:
175+
prop= NodeProperty(ID=ip+1, Cd=P[0], CdMG=P[1], Ca=P[2], CaMG=P[3], Cp=P[4], CpMG=P[5], AxCd=P[6], AxCdMG=P[7], AxCa=P[8], AxCaMG=P[9], AxCp=P[10], AxCpMG=P[11])
176+
elif len(P)==10:
177+
prop= NodeProperty(ID=ip+1, Cd=P[0], CdMG=P[1], Ca=P[2], CaMG=P[3], Cp=P[4], CpMG=P[5], AxCa=P[6], AxCaMG=P[7], AxCp=P[8], AxCpMG=P[9])
178+
else:
179+
raise NotImplementedError()
180+
Graph.addNodeProperty('SimpleCoefs',prop)
181+
if 'DpthProp' in hd.keys():
182+
Graph.addMiscPropertySet('DepthCoefs')
183+
for ip,P in enumerate(hd['DpthProp']):
184+
# Dpth DpthCd DpthCdMG DpthCa DpthCaMG DpthCp DpthCpMG DpthAxCd DpthAxCdMG DpthAxCa DpthAxCaMG DpthAxCp DpthAxCpMG
185+
prop= Property(ID=ip+1, Dpth=P[0], Cd=P[1], CdMG=P[2], Ca=P[3], CaMG=P[4], Cp=P[5], CpMG=P[6], AxCd=P[7], AxCdMG=P[8], AxCa=P[9], AxCaMG=P[10], AxCp=P[11], AxCpMG=P[12])
186+
Graph.addMiscProperty('DepthCoefs',prop)
187+
188+
if 'MemberProp' in hd.keys():
189+
# Member-based hydro coefficinet
190+
Graph.addMiscPropertySet('MemberCoefs')
191+
for ip,P in enumerate(hd['MemberProp']):
192+
# MemberID MemberCd1 MemberCd2 MemberCdMG1 MemberCdMG2 MemberCa1 MemberCa2 MemberCaMG1 MemberCaMG2 MemberCp1 MemberCp2 MemberCpMG1 MemberCpMG2 MemberAxCd1 MemberAxCd2 MemberAxCdMG1 MemberAxCdMG2 MemberAxCa1 MemberAxCa2 MemberAxCaMG1 MemberAxCaMG2 MemberAxCp1 MemberAxCp2 MemberAxCpMG1 MemberAxCpMG2
193+
prop = ElemProperty(ID=ip+1, MemberID=P[0], Cd1=P[1], Cd2=P[2], CdMG1=P[3], CdMG2=P[4], Ca1=P[5], Ca2=P[6], CaMG1=P[7], CaMG2=P[8], Cp1=P[9], Cp2=P[10], CpMG1=P[11], CpMG2=P[12], AxCd1=P[14], AxCd2=P[15], axCdMG1=P[16], axCdMG2=P[17], AxCa1=P[18], AxCa2=P[19], AxCaMG1=P[20], AxCaMG2=P[21], AxCp1=P[22], AxCp2=P[23])
194+
Graph.addMiscProperty('MemberCoefs',prop)
195+
# ---
196+
if 'FillGroups' in hd.keys():
197+
# Filled members
198+
Graph.addMiscPropertySet('FillGroups')
199+
print('>>> TODO Filled Groups')
200+
#for ip,P in enumerate(hd['FillGroups']):
201+
# # FillNumM FillMList FillFSLoc FillDens
202+
# raise NotImplementedError('hydroDynToGraph, Fill List might not be properly set, verify below')
203+
# prop = MiscProperty(ID=ip+1, FillNumM=P[0], FillMList=P[1], FillFSLoc=P[2], FillDens=P[3])
204+
# Graph.addMiscProperty('FillGroups',prop)
205+
206+
if 'MGProp' in hd.keys():
207+
# Marine Growth
208+
Graph.addMiscPropertySet('MG')
209+
for ip,P in enumerate(hd['MGProp']):
210+
# MGDpth MGThck MGDens
211+
# (m) (m) (kg/m^3)
212+
prop = Property(ID=ip+1, MGDpth=P[0], MGThck=P[1], MGDens=P[2])
213+
Graph.addMiscProperty('FillGroups',prop)
214+
215+
# --- Nodes
216+
Nodes = hd['Joints']
217+
for iNode,N in enumerate(Nodes):
218+
node = Node(ID=N[0], x=N[1], y=N[2], z=N[3])
219+
Graph.addNode(node)
220+
Graph.setNodeNodalProp(node, 'AxCoefs', N[4])
221+
222+
# --- Elements
223+
PropSets=['SimpleCoefs','DepthCoefs','MemberCoefs']
224+
Members = hd['Members']
225+
for ie,E in enumerate(Members):
226+
# MemberID MJointID1 MJointID2 MPropSetID1 MPropSetID2 MDivSize MCoefMod PropPot
227+
EE = E[:5].astype(int)
228+
Type = int(E[6]) # MCoefMod
229+
Pot = E[7].lower()[0]=='t'
230+
elem= Element(ID=EE[0], nodeIDs=EE[1:3], propIDs=EE[3:5], propset='Section', CoefMod=PropSets[Type-1], DivSize=float(E[5]), Pot=Pot)
231+
elem.data['object']='cylinder'
232+
elem.data['color'] = type2Color(Pot)
233+
Graph.addElement(elem)
234+
# Nodal prop data NOTE: can't do that anymore for memebrs with different diameters at the same node
235+
if propToNodes:
236+
# NOTE: not by default because of feature with members with different diameters at the same node
237+
Graph.setElementNodalProp(elem, propset='Section', propIDs=EE[3:5])
238+
if propToElem:
239+
Graph.setElementNodalPropToElem(elem) # TODO, this shouldn't be needed
240+
241+
if Type==1:
242+
# Simple
243+
Graph.setElementNodalProp(elem, propset='SimpleCoefs', propIDs=[1,1])
244+
else:
245+
print('>>> TODO type DepthCoefs and MemberCoefs')
246+
# NOTE: this is disallowed by default because a same node can have two different diameters in SubDyn (it's by element)
247+
#Graph.setElementNodalProp(elem, propset=PropSets[Type-1], propIDs=E[3:5])
248+
249+
return Graph
250+
251+
252+
# --------------------------------------------------------------------------------}
253+
# --- SubDyn Summary file
254+
# --------------------------------------------------------------------------------{
255+
def subdynSumToGraph(data, Graph=None):
256+
"""
257+
data: dict-like object as returned by weio
258+
"""
259+
type2Color=[
260+
(0.1,0.1,0.1), # Watchout based on background
261+
(0.753,0.561,0.05), # 1 Beam
262+
(0.541,0.753,0.05), # 2 Cable
263+
(0.753,0.05,0.204), # 3 Rigid
264+
(0.918,0.702,0.125), # 3 Rigid
265+
]
266+
267+
#print(data.keys())
268+
DOF2Nodes = data['DOF2Nodes']
269+
nDOF = data['nDOF_red']
270+
271+
if Graph is None:
272+
Graph = GraphModel()
273+
274+
# --- Nodes and DOFs
275+
Nodes = data['Nodes']
276+
for iNode,N in enumerate(Nodes):
277+
if len(N)==9: # Temporary fix
278+
#N[4]=np.float(N[4].split()[0])
279+
N=N.astype(np.float32)
280+
ID = int(N[0])
281+
nodeDOFs=DOF2Nodes[(DOF2Nodes[:,1]==ID),0] # NOTE: these were reindex to start at 0
282+
node = Node(ID=ID, x=N[1], y=N[2], z=N[3], Type=int(N[4]), DOFs=nodeDOFs)
283+
Graph.addNode(node)
284+
285+
# --- Elements
286+
Elements = data['Elements']
287+
for ie,E in enumerate(Elements):
288+
nodeIDs=[int(E[1]),int(E[2])]
289+
# shear_[-] Ixx_[m^4] Iyy_[m^4] Jzz_[m^4] T0_[N]
290+
D = np.sqrt(E[7]/np.pi)*4 # <<< Approximation basedon area TODO use I as well
291+
elem= Element(int(E[0]), nodeIDs, Type=int(E[5]), Area=E[7], rho=E[8], E=E[7], G=E[8], D=D)
292+
elem.data['object']='cylinder'
293+
elem.data['color'] = type2Color[int(E[5])]
294+
Graph.addElement(elem)
295+
296+
#print(self.extent)
297+
#print(self.maxDimension)
298+
299+
# --- Graph Modes
300+
# Very important sortDims should be None to respect order of nodes
301+
dispGy, posGy, InodesGy, dispCB, posCB, InodesCB = data.getModes(sortDim=None)
302+
for iMode in range(dispGy.shape[2]):
303+
Graph.addMode(displ=dispGy[:,:,iMode],name='GY{:d}'.format(iMode+1), freq=1/(2*np.pi))
304+
305+
for iMode in range(dispCB.shape[2]):
306+
Graph.addMode(displ=dispCB[:,:,iMode],name='CB{:d}'.format(iMode+1), freq=data['CB_frequencies'][iMode])
307+
308+
#print(Graph.toJSON())
309+
310+
311+
return Graph
312+
313+
314+
315+
if __name__ == '__main__':
316+
from .fast_input_file import FASTInputFile
317+
318+
filename='../../_data/Monopile/MT100_SD.dat'
319+
# filename='../../_data/Monopile/TetraSpar_SubDyn_v3.dat'
320+
321+
sd = FASTInputFile(filename)
322+
# sd.write('OutMT.dat')
323+
Graph = sd.toGraph()
324+
Graph.divideElements(2)
325+
print(Graph)
326+
print(Graph.sortNodesBy('z'))
327+
# print(Graph.nodalDataFrame(sortBy='z'))
328+
print(Graph.points)
329+
print(Graph.connectivity)
330+
print(Graph)
331+
332+
# import numpy as np
333+
# import matplotlib.pyplot as plt
334+
# from matplotlib import collections as mc
335+
# from mpl_toolkits.mplot3d import Axes3D
336+
# fig = plt.figure()
337+
# ax = fig.add_subplot(1,2,1,projection='3d')
338+
#
339+
# lines=Graph.toLines(output='coord')
340+
# for l in lines:
341+
# # ax.add_line(l)
342+
# ax.plot(l[:,0],l[:,1],l[:,2])
343+
#
344+
# ax.autoscale()
345+
# ax.set_xlim([-40,40])
346+
# ax.set_ylim([-40,40])
347+
# ax.set_zlim([-40,40])
348+
# # ax.margins(0.1)
349+
#
350+
# plt.show()
351+
352+

0 commit comments

Comments
 (0)