wawi 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- wawi/__init__.py +8 -4
- wawi/abq.py +1128 -0
- wawi/fe.py +134 -0
- wawi/general.py +473 -0
- wawi/identification.py +66 -0
- wawi/io.py +696 -0
- wawi/modal.py +608 -0
- wawi/plot.py +569 -0
- wawi/prob.py +9 -0
- wawi/random.py +38 -0
- wawi/signal.py +45 -0
- wawi/structural.py +278 -0
- wawi/time_domain.py +126 -0
- wawi/tools.py +7 -0
- wawi/wave.py +491 -0
- wawi/wind.py +1108 -0
- wawi/wind_code.py +14 -0
- {wawi-0.0.1.dist-info → wawi-0.0.3.dist-info}/METADATA +7 -6
- wawi-0.0.3.dist-info/RECORD +22 -0
- wawi-0.0.1.dist-info/RECORD +0 -6
- {wawi-0.0.1.dist-info → wawi-0.0.3.dist-info}/LICENSE +0 -0
- {wawi-0.0.1.dist-info → wawi-0.0.3.dist-info}/WHEEL +0 -0
- {wawi-0.0.1.dist-info → wawi-0.0.3.dist-info}/top_level.txt +0 -0
wawi/io.py
ADDED
@@ -0,0 +1,696 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import numpy as np
|
3
|
+
import re
|
4
|
+
import json
|
5
|
+
import csv
|
6
|
+
import dill
|
7
|
+
from pathlib import Path
|
8
|
+
|
9
|
+
from wawi.model import ModalDry, PontoonType, Node, Model, Aero, AeroSection
|
10
|
+
|
11
|
+
def import_folder(model_folder, pontoon_stl='pontoon.stl', aero_sections='aero_sections.json',
|
12
|
+
modal='modal.json', pontoon='pontoon.json', eldef='element.json', orientations='orientations.json',
|
13
|
+
drag_elements='drag_elements.json', pontoon_types='pontoon_types.json',
|
14
|
+
sort_nodes_by_x=True, interpolation_kind='quadratic'):
|
15
|
+
|
16
|
+
'''
|
17
|
+
Import folder containing files defining a WAWI model.
|
18
|
+
|
19
|
+
Parameters
|
20
|
+
---------------
|
21
|
+
model_folder : str
|
22
|
+
string defining path of root
|
23
|
+
pontoon_stl : 'pontoon.stl', optional
|
24
|
+
string to relative path of stl-file with pontoon (3d file)
|
25
|
+
aero_sections : 'aero_sections.json', optional
|
26
|
+
string to relative path of json file with aerodynamic sections
|
27
|
+
modal : 'modal.json', optional
|
28
|
+
string to relative path of dry modal definitions
|
29
|
+
pontoon : 'pontoon.json', optional
|
30
|
+
string to relative path of pontoon definitions
|
31
|
+
eldef : 'element.json', optional
|
32
|
+
string to relative path of element definitions (node and element matrices)
|
33
|
+
orientations : 'orientations.json', optional
|
34
|
+
string to relative path of orientations of aero elements
|
35
|
+
drag_elements : 'drag_elements.json', optional
|
36
|
+
string to relative path of file defining drag elements
|
37
|
+
pontoon_types : 'pontoon_types.json', optional
|
38
|
+
string to relative path of pontoon types
|
39
|
+
sort_nodes_by_x : True, optional
|
40
|
+
whether or not to sort the nodes of eldef by their x-coordinate
|
41
|
+
interpolation_kind : {'quadratic', 'linear', ...}
|
42
|
+
interpolation kind used for hydrodynamic transfer function
|
43
|
+
|
44
|
+
Returns
|
45
|
+
---------------
|
46
|
+
model : `wawi.model.Model`
|
47
|
+
WAWI model object
|
48
|
+
|
49
|
+
Notes
|
50
|
+
---------------
|
51
|
+
Add info here describing the structure and contents of all files.
|
52
|
+
|
53
|
+
See `importing-folder.ipynb` for a full example.
|
54
|
+
'''
|
55
|
+
|
56
|
+
|
57
|
+
from beef.fe import Part, Section
|
58
|
+
from beef.general import n2d_ix
|
59
|
+
|
60
|
+
model_folder = Path(model_folder)
|
61
|
+
|
62
|
+
# Load pontoon data
|
63
|
+
pontoon_data = {}
|
64
|
+
if pontoon is not None:
|
65
|
+
try:
|
66
|
+
with open(model_folder / pontoon, 'r') as f:
|
67
|
+
pontoon_data = json.load(f)
|
68
|
+
except:
|
69
|
+
print('Valid pontoon data not found. No pontoons created.')
|
70
|
+
|
71
|
+
|
72
|
+
# Load modal data
|
73
|
+
with open(model_folder / modal, 'r') as f:
|
74
|
+
modal = json.load(f)
|
75
|
+
|
76
|
+
# Load element definitions
|
77
|
+
if eldef is not None:
|
78
|
+
try:
|
79
|
+
with open(model_folder / eldef, 'r') as f:
|
80
|
+
element_data = json.load(f)
|
81
|
+
except:
|
82
|
+
print('Valid element data not found. No eldef created.')
|
83
|
+
element_data = {}
|
84
|
+
else:
|
85
|
+
element_data = {}
|
86
|
+
|
87
|
+
if orientations is not None:
|
88
|
+
try:
|
89
|
+
with open(model_folder / orientations, 'r') as f:
|
90
|
+
orientations = json.load(f)
|
91
|
+
except:
|
92
|
+
print('Specified orientations file found. No orientation definitions applied.')
|
93
|
+
orientations = {}
|
94
|
+
else:
|
95
|
+
orientations = {}
|
96
|
+
|
97
|
+
|
98
|
+
common_ptype_settings = None
|
99
|
+
if pontoon_types is not None:
|
100
|
+
try:
|
101
|
+
with open(model_folder / pontoon_types, 'r') as f:
|
102
|
+
pontoon_type_settings = json.load(f)
|
103
|
+
|
104
|
+
if '*' in pontoon_type_settings:
|
105
|
+
common_ptype_settings = pontoon_type_settings.pop('*')
|
106
|
+
|
107
|
+
except:
|
108
|
+
print('Valid pontoon types file not found. No pontoon_types definitions applied.')
|
109
|
+
pontoon_type_settings = {}
|
110
|
+
|
111
|
+
else:
|
112
|
+
pontoon_type_settings = {}
|
113
|
+
|
114
|
+
# Extract specific pontoon data
|
115
|
+
pontoon_names = [key for key in pontoon_data]
|
116
|
+
pontoon_nodes = [Node(pontoon_data[key]['node'], *pontoon_data[key]['coordinates']) for key in pontoon_data]
|
117
|
+
pontoon_rotation = [pontoon_data[key]['rotation']*np.pi/180 for key in pontoon_data]
|
118
|
+
pontoon_types = [pontoon_data[key]['pontoon_type'] for key in pontoon_data]
|
119
|
+
|
120
|
+
# Pontoon types
|
121
|
+
unique_pontoon_types = list(set(pontoon_types))
|
122
|
+
files = [f'{model_folder}/{pt}.npz' for pt in unique_pontoon_types]
|
123
|
+
ptypes = dict()
|
124
|
+
|
125
|
+
if len(pontoon_type_settings)==0:
|
126
|
+
pontoon_type_settings = {key: {} for key in unique_pontoon_types}
|
127
|
+
|
128
|
+
if common_ptype_settings: #universal setting, overwriting others
|
129
|
+
for name in pontoon_type_settings:
|
130
|
+
for par in common_ptype_settings:
|
131
|
+
pontoon_type_settings[name][par] = common_ptype_settings[par]
|
132
|
+
|
133
|
+
for file in files:
|
134
|
+
name = Path(file).stem
|
135
|
+
P = np.load(file)
|
136
|
+
|
137
|
+
if 'Cd' in P:
|
138
|
+
Cd = P['Cd']
|
139
|
+
else:
|
140
|
+
Cd = np.array([0,0,0,0,0,0])
|
141
|
+
|
142
|
+
if 'A' in P:
|
143
|
+
A = P['A']
|
144
|
+
else:
|
145
|
+
A = np.array([0,0,0,0,0,0])
|
146
|
+
|
147
|
+
ACd = {'area': A, 'Cd':Cd}
|
148
|
+
ptypes[name] = PontoonType.from_numeric(interpolation_kind=interpolation_kind, A=P['M'], B=P['C'],
|
149
|
+
omega=P['omega'], Q=P['Q'], theta=P['theta_Q'], omegaQ=P['omega_Q'], label=name,
|
150
|
+
stl_path=f'{model_folder}/{pontoon_stl}', **{**ACd, **pontoon_type_settings[name]})
|
151
|
+
|
152
|
+
|
153
|
+
# Modal dry object
|
154
|
+
if 'xi0' in modal:
|
155
|
+
xi0 = np.array(modal['xi0'])
|
156
|
+
else:
|
157
|
+
xi0 = 0.0
|
158
|
+
|
159
|
+
if 'm_min' in modal:
|
160
|
+
m_min = modal['m_min']
|
161
|
+
else:
|
162
|
+
m_min = 0.0
|
163
|
+
|
164
|
+
if 'phi_x' in modal:
|
165
|
+
phi_x = modal['phi_x']
|
166
|
+
else:
|
167
|
+
phi_x = None
|
168
|
+
|
169
|
+
modal_dry = ModalDry(modal['phi'], m=np.array(modal['m']), k=np.array(modal['k']),
|
170
|
+
local_phi=modal['local'], xi0=xi0, phi_x=phi_x, m_min=m_min)
|
171
|
+
|
172
|
+
# Element definition
|
173
|
+
if element_data != {}:
|
174
|
+
if 'sections' in element_data:
|
175
|
+
sd = element_data['sections']
|
176
|
+
section_dict = {key: Section(E=sd[key]['E'], poisson=sd[key]['poisson'],
|
177
|
+
A=sd[key]['A'], I_y=sd[key]['Iy'], I_z=sd[key]['Iz'],
|
178
|
+
J=sd[key]['J'], m=sd[key]['m'], name=key) for key in sd}
|
179
|
+
|
180
|
+
sections = [section_dict[key] for key in element_data['section_assignment']]
|
181
|
+
else:
|
182
|
+
sections = None
|
183
|
+
|
184
|
+
node_matrix = np.array(element_data['node_matrix'])
|
185
|
+
node_matrix[:,0] = node_matrix[:,0].astype(int)
|
186
|
+
element_matrix = np.array(element_data['element_matrix'])
|
187
|
+
|
188
|
+
eldef = Part(node_matrix, element_matrix, sections=sections,
|
189
|
+
assemble=False, forced_ndofs=6)
|
190
|
+
|
191
|
+
if sort_nodes_by_x:
|
192
|
+
ix = np.argsort([n.coordinates[0] for n in eldef.nodes])
|
193
|
+
node_labels_sorted = eldef.get_node_labels()[ix]
|
194
|
+
eldef.arrange_nodes(node_labels_sorted, arrange_dof_ixs=True)
|
195
|
+
|
196
|
+
if 'full' in modal_dry.phi_full: #adjust phi_full
|
197
|
+
modal_dry.phi_full['full'] = modal_dry.phi_full['full'][n2d_ix(ix, n_dofs=6), :]
|
198
|
+
|
199
|
+
# Assign orientation
|
200
|
+
for key in orientations:
|
201
|
+
if 'e2' in orientations[key]:
|
202
|
+
e2 = orientations[key]['e2']
|
203
|
+
e3 = None
|
204
|
+
elif 'e3' in orientations[key]:
|
205
|
+
e3 = orientations[key]['e3']
|
206
|
+
e2 = None
|
207
|
+
else:
|
208
|
+
raise ValueError('Orientations should contain either e2 or e3')
|
209
|
+
|
210
|
+
elements = orientations[key]['elements']
|
211
|
+
for el_label in elements:
|
212
|
+
el = eldef.get_element(int(el_label))
|
213
|
+
|
214
|
+
|
215
|
+
el.assign_e2(e2)
|
216
|
+
el.assign_e3(e3)
|
217
|
+
|
218
|
+
el.initiate_geometry()
|
219
|
+
else:
|
220
|
+
eldef = None
|
221
|
+
|
222
|
+
# Create model object
|
223
|
+
model = Model.from_nodes_and_types(pontoon_nodes, [ptypes[pt] for pt in pontoon_types], modal_dry=modal_dry,
|
224
|
+
rotation=pontoon_rotation, eldef=eldef, labels=pontoon_names)
|
225
|
+
|
226
|
+
# Aero mode
|
227
|
+
if aero_sections is not None:
|
228
|
+
# Load aero sections
|
229
|
+
try:
|
230
|
+
with open(model_folder / aero_sections, 'r') as f:
|
231
|
+
data = json.load(f)
|
232
|
+
|
233
|
+
sections = dict()
|
234
|
+
element_assignments = dict()
|
235
|
+
for key in data:
|
236
|
+
element_assignments[key] = data[key].pop('elements')
|
237
|
+
sections[key] = AeroSection(**data[key])
|
238
|
+
|
239
|
+
model.aero = Aero(sections=sections, element_assignments=element_assignments)
|
240
|
+
except:
|
241
|
+
print('Specified aero_sections file found. No aerodynamics definitions applied.')
|
242
|
+
|
243
|
+
# Drag elements model
|
244
|
+
if drag_elements is not None:
|
245
|
+
try:
|
246
|
+
with open(model_folder / drag_elements, 'r') as f:
|
247
|
+
data = json.load(f)
|
248
|
+
|
249
|
+
model.assign_drag_elements(data)
|
250
|
+
except:
|
251
|
+
print('Specified drag_elements file found or invalid. No drag elements defined.')
|
252
|
+
|
253
|
+
model.connect_eldef()
|
254
|
+
model.assign_dry_modes()
|
255
|
+
|
256
|
+
return model
|
257
|
+
|
258
|
+
|
259
|
+
def save_wwi(model, model_path):
|
260
|
+
'''
|
261
|
+
Save WAWI model object to specified model path.
|
262
|
+
'''
|
263
|
+
with open(model_path, 'wb') as f:
|
264
|
+
dill.dump(model, f, -1)
|
265
|
+
|
266
|
+
|
267
|
+
def convert_to_wwi(model_path, output=None):
|
268
|
+
'''
|
269
|
+
Convert folder with WAWI object definition files to wwi (model) file
|
270
|
+
'''
|
271
|
+
model_path = Path(model_path)
|
272
|
+
if output is None:
|
273
|
+
output = model_path / 'model.wwi'
|
274
|
+
|
275
|
+
model = load_model(model_path)
|
276
|
+
|
277
|
+
if model_path.is_dir():
|
278
|
+
with open(output, 'wb') as f:
|
279
|
+
dill.dump(model, f, -1)
|
280
|
+
|
281
|
+
|
282
|
+
def load_model(model_path, save_if_nonexistent=False, sort_nodes_by_x=True):
|
283
|
+
'''
|
284
|
+
Load wwi-file with WAWI model object or folder with relevant definitions.
|
285
|
+
|
286
|
+
Parameters
|
287
|
+
-----------
|
288
|
+
model_path : str
|
289
|
+
path of wwi-file
|
290
|
+
save_if_nonexistent : False, optional
|
291
|
+
whether or not to save model as wwi if not existing
|
292
|
+
sort_nodes_by_x : True, optional
|
293
|
+
whether or not to sort the nodes of eldef by their x-coordinate
|
294
|
+
|
295
|
+
Returns
|
296
|
+
-----------
|
297
|
+
model : `wawi.model.Model`
|
298
|
+
WAWI model
|
299
|
+
'''
|
300
|
+
|
301
|
+
model_path = Path(model_path)
|
302
|
+
|
303
|
+
if model_path.is_dir() and not (model_path/'model.wwi').is_file():
|
304
|
+
model = import_folder(model_path, sort_nodes_by_x=sort_nodes_by_x)
|
305
|
+
if save_if_nonexistent:
|
306
|
+
convert_to_wwi(model_path)
|
307
|
+
else:
|
308
|
+
with open(model_path, 'rb') as f:
|
309
|
+
model = dill.load(f)
|
310
|
+
|
311
|
+
return model
|
312
|
+
|
313
|
+
|
314
|
+
def import_wadam_hydro_transfer(wadam_file):
|
315
|
+
'''
|
316
|
+
Import WADAM output file.
|
317
|
+
|
318
|
+
Parameters
|
319
|
+
-----------
|
320
|
+
wadam_file : str
|
321
|
+
path to file to import
|
322
|
+
|
323
|
+
Returns
|
324
|
+
----------
|
325
|
+
omega : float
|
326
|
+
numpy array describing numerical frequency axis
|
327
|
+
theta : float
|
328
|
+
numpy array describing numerical directional axis
|
329
|
+
fhyd : float
|
330
|
+
complex numpy 3d-array describing the transfer functions relating
|
331
|
+
regular waves with unit height to the 6 relevant forces and moments
|
332
|
+
6-by-len(theta)-by-len(omega)
|
333
|
+
'''
|
334
|
+
|
335
|
+
string = ('.+W A V E P E R I O D.+=\s+(?P<period>.+):.+\n.+'
|
336
|
+
'H E A D I N G A N G L E.+=\s+(?P<theta>.+):(?:.*\n){5,10}'
|
337
|
+
|
338
|
+
'.+EXCITING FORCES AND MOMENTS FROM THE HASKIN RELATIONS(?:.*\n){4,6}\s+'
|
339
|
+
|
340
|
+
'-F1-\s+(?P<F1_real>[-?.E\d+]+)\s+(?P<F1_imag>[-?.E\d+]+)\s.+\n\s+'
|
341
|
+
'-F2-\s+(?P<F2_real>[-?.E\d+]+)\s+(?P<F2_imag>[-?.E\d+]+)\s.+\n\s+'
|
342
|
+
'-F3-\s+(?P<F3_real>[-?.E\d+]+)\s+(?P<F3_imag>[-?.E\d+]+)\s.+\n\s+'
|
343
|
+
'-F4-\s+(?P<F4_real>[-?.E\d+]+)\s+(?P<F4_imag>[-?.E\d+]+)\s.+\n\s+'
|
344
|
+
'-F5-\s+(?P<F5_real>[-?.E\d+]+)\s+(?P<F5_imag>[-?.E\d+]+)\s.+\n\s+'
|
345
|
+
'-F6-\s+(?P<F6_real>[-?.E\d+]+)\s+(?P<F6_imag>[-?.E\d+]+)\s.+\n')
|
346
|
+
regex = re.compile(string)
|
347
|
+
|
348
|
+
nondim_string = ('\s+NON-DIMENSIONALIZING FACTORS:\n(?:.*\n)+'
|
349
|
+
'\s+RO\s+=\s+(?P<rho>[-?.E\d+]+)\n'
|
350
|
+
'\s+G\s+=\s+(?P<g>[-?.E\d+]+)\n'
|
351
|
+
'\s+VOL\s+=\s+(?P<vol>[-?.E\d+]+)\n'
|
352
|
+
'\s+L\s+=\s+(?P<l>[-?.E\d+]+)\n'
|
353
|
+
'\s+WA\s+=\s+(?P<wa>[-?.E\d+]+)')
|
354
|
+
|
355
|
+
regex_nondim = re.compile(nondim_string)
|
356
|
+
|
357
|
+
with open(wadam_file, encoding='utf-8') as file:
|
358
|
+
data = file.read()
|
359
|
+
|
360
|
+
periods = []
|
361
|
+
thetas = []
|
362
|
+
Q = []
|
363
|
+
|
364
|
+
for match in regex.finditer(data):
|
365
|
+
data_dict = dict(zip(match.groupdict().keys(), [float(val) for val in match.groupdict().values()]))
|
366
|
+
F1 = data_dict['F1_real'] + data_dict['F1_imag']*1j
|
367
|
+
F2 = data_dict['F2_real'] + data_dict['F2_imag']*1j
|
368
|
+
F3 = data_dict['F3_real'] + data_dict['F3_imag']*1j
|
369
|
+
F4 = data_dict['F4_real'] + data_dict['F4_imag']*1j
|
370
|
+
F5 = data_dict['F5_real'] + data_dict['F5_imag']*1j
|
371
|
+
F6 = data_dict['F6_real'] + data_dict['F6_imag']*1j
|
372
|
+
Q.append(np.array([F1,F2,F3,F4,F5,F6]))
|
373
|
+
|
374
|
+
thetas.append(data_dict['theta'])
|
375
|
+
periods.append(data_dict['period'])
|
376
|
+
|
377
|
+
theta = np.unique(np.array(thetas))*np.pi/180.0
|
378
|
+
period = np.unique(np.array(periods))
|
379
|
+
omega = np.flip(2*np.pi/period, axis=0)
|
380
|
+
Q = np.vstack(Q).T
|
381
|
+
fhyd = np.zeros([6, len(theta), len(omega)]).astype('complex')
|
382
|
+
for n in range(len(theta)):
|
383
|
+
for k in range(len(omega)):
|
384
|
+
fhyd[:,n,k] = Q[:, k*len(theta)+n]
|
385
|
+
|
386
|
+
# Re-dimensionalize and flip
|
387
|
+
nondim_parameters = regex_nondim.search(data).groupdict()
|
388
|
+
nd = dict(zip(nondim_parameters.keys(), [float(val) for val in nondim_parameters.values()]))
|
389
|
+
|
390
|
+
rho, g, vol, wa, l = nd['rho'], nd['g'], nd['vol'], nd['wa'], nd['l']
|
391
|
+
dim = 0*fhyd
|
392
|
+
dim[0::6,:,:] = rho*vol*g*wa/l
|
393
|
+
dim[1::6,:,:] = rho*vol*g*wa/l
|
394
|
+
dim[2::6,:,:] = rho*vol*g*wa/l
|
395
|
+
|
396
|
+
dim[3::6,:,:] = rho*vol*g*wa
|
397
|
+
dim[4::6,:,:] = rho*vol*g*wa
|
398
|
+
dim[5::6,:,:] = rho*vol*g*wa
|
399
|
+
|
400
|
+
fhyd = np.flip(fhyd, axis=2) * dim
|
401
|
+
|
402
|
+
return omega, theta, fhyd
|
403
|
+
|
404
|
+
|
405
|
+
def import_wadam_mat(wadam_file):
|
406
|
+
'''
|
407
|
+
Import system matrices given by input WADAM results (frequency dependent and constant).
|
408
|
+
|
409
|
+
Parameters
|
410
|
+
-------------
|
411
|
+
wadam_file : str
|
412
|
+
path to WADAM file to open
|
413
|
+
|
414
|
+
Returns
|
415
|
+
-------------
|
416
|
+
mass : float
|
417
|
+
6-by-6 added mass matrix of body
|
418
|
+
damping : float
|
419
|
+
6-by-6 radiation damping matrix of body
|
420
|
+
stiffness : float
|
421
|
+
6-by-6 restoring stiffness matrix of body
|
422
|
+
static_mass : float
|
423
|
+
6-by-6 static (constant) mass matrix of body
|
424
|
+
this represents inertia of pontoon object itself
|
425
|
+
omega : float
|
426
|
+
numpy array describing numerical frequency axis (corresponding to mass and damping
|
427
|
+
output)
|
428
|
+
'''
|
429
|
+
|
430
|
+
static_mass_target = 'MASS INERTIA COEFFICIENT MATRIX'
|
431
|
+
stiffness_target = 'HYDROSTATIC RESTORING COEFFICIENT MATRIX'
|
432
|
+
mass_target = 'ADDED MASS MATRIX '
|
433
|
+
damping_target = 'DAMPING MATRIX '
|
434
|
+
period_target = 'WAVE PERIOD = '
|
435
|
+
non_dim_target = ' THE OUTPUT IS NON-DIMENSIONALIZED USING -'
|
436
|
+
|
437
|
+
f = open(wadam_file)
|
438
|
+
active_search = False
|
439
|
+
current_elements = []
|
440
|
+
current_range = [0]
|
441
|
+
|
442
|
+
stiffness = np.empty([0,6,6])
|
443
|
+
static_mass = np.empty([0,6,6])
|
444
|
+
mass = np.empty([0,6,6])
|
445
|
+
damping = np.empty([0,6,6])
|
446
|
+
period = np.empty([0,1,1])
|
447
|
+
non_dim = np.empty([0])
|
448
|
+
|
449
|
+
temp_data=[]
|
450
|
+
data_storage = {'static_mass': static_mass, 'stiffness': stiffness, 'period': period, 'mass':mass,'damping':damping,'non_dim':non_dim}
|
451
|
+
append_axis = {'static_mass': 0, 'stiffness': 0, 'period': 0, 'mass':0,'damping':0,'non_dim':None}
|
452
|
+
|
453
|
+
for lineno, line in enumerate(f):
|
454
|
+
static_mass_switch = line.find(static_mass_target)!=-1
|
455
|
+
stiffness_switch = line.find(stiffness_target)!=-1
|
456
|
+
period_switch = line.find(period_target)!=-1
|
457
|
+
mass_switch = line.find(mass_target)!=-1
|
458
|
+
damping_switch = line.find(damping_target)!=-1
|
459
|
+
non_dim_switch = line.find(non_dim_target)!=-1
|
460
|
+
|
461
|
+
if static_mass_switch == True:
|
462
|
+
active_search=True
|
463
|
+
current_range = [el+lineno-1 for el in range(5,11)]
|
464
|
+
current_elements = range(1,6+1)
|
465
|
+
current_data_string='static_mass'
|
466
|
+
current_data = data_storage[current_data_string]
|
467
|
+
elif stiffness_switch:
|
468
|
+
active_search=True
|
469
|
+
current_range = [el+lineno-1 for el in range(5,11)]
|
470
|
+
current_elements = range(1,6+1)
|
471
|
+
current_data_string='stiffness'
|
472
|
+
current_data = data_storage[current_data_string]
|
473
|
+
elif period_switch:
|
474
|
+
active_search=True
|
475
|
+
current_range = [lineno]
|
476
|
+
current_elements = [3]
|
477
|
+
current_data_string='period'
|
478
|
+
current_data = data_storage[current_data_string]
|
479
|
+
elif mass_switch:
|
480
|
+
active_search=True
|
481
|
+
current_range = [el+lineno-1 for el in range(5,11)]
|
482
|
+
current_elements = range(1,6+1)
|
483
|
+
current_data_string='mass'
|
484
|
+
current_data = data_storage[current_data_string]
|
485
|
+
elif damping_switch:
|
486
|
+
active_search=True
|
487
|
+
current_range = [el+lineno-1 for el in range(5,11)]
|
488
|
+
current_elements = range(1,6+1)
|
489
|
+
current_data_string='damping'
|
490
|
+
current_data = data_storage[current_data_string]
|
491
|
+
elif non_dim_switch:
|
492
|
+
active_search=True
|
493
|
+
current_range = [el+lineno-1 for el in range(9,14)]
|
494
|
+
current_elements = [2]
|
495
|
+
current_data_string='non_dim'
|
496
|
+
current_data = data_storage[current_data_string]
|
497
|
+
|
498
|
+
if (active_search==True) and (lineno in current_range):
|
499
|
+
data_line = [float(i) for i in [line.split()[el] for el in current_elements]]
|
500
|
+
temp_data.append(data_line)
|
501
|
+
elif (active_search==True) and lineno>=max(current_range):
|
502
|
+
current_data=np.append(current_data,[temp_data],axis=append_axis[current_data_string])
|
503
|
+
temp_data=[]
|
504
|
+
current_range = [0]
|
505
|
+
current_elements = []
|
506
|
+
active_search=False
|
507
|
+
data_storage[current_data_string] = current_data
|
508
|
+
|
509
|
+
# Non-dimensionalizing factors
|
510
|
+
non_dim = data_storage['non_dim']
|
511
|
+
ro = non_dim[0]
|
512
|
+
g = non_dim[1]
|
513
|
+
vol = non_dim[2]
|
514
|
+
l = non_dim[3]
|
515
|
+
|
516
|
+
non_dim_defs = {'static_mass': np.array([[ro*vol, ro*vol*l],
|
517
|
+
[ro*vol*l, ro*vol*l*l]]),
|
518
|
+
'mass': np.array([[ro*vol, ro*vol*l],
|
519
|
+
[ro*vol*l, ro*vol*l*l]]),
|
520
|
+
'damping': np.array([[ro*vol*np.sqrt(g/l), ro*vol*np.sqrt(g*l)],
|
521
|
+
[ro*vol*np.sqrt(g*l), ro*vol*l*np.sqrt(g*l)]]),
|
522
|
+
'stiffness':np.array([[ro*vol*g/l, ro*vol*g],
|
523
|
+
[ro*vol*g, ro*vol*g*l]])}
|
524
|
+
|
525
|
+
# Re-dimensionalize data
|
526
|
+
for current_data_string in non_dim_defs.keys():
|
527
|
+
dim_matrix = np.repeat(np.repeat(non_dim_defs[current_data_string],3,axis=0),3,axis=1)
|
528
|
+
data_storage[current_data_string] = np.multiply(data_storage[current_data_string], dim_matrix)
|
529
|
+
|
530
|
+
static_mass = data_storage['static_mass'][0]
|
531
|
+
stiffness = data_storage['stiffness'][0]
|
532
|
+
mass = np.moveaxis(data_storage['mass'],0,2)
|
533
|
+
damping = np.moveaxis(data_storage['damping'],0,2)
|
534
|
+
omega = 2.0*np.pi/data_storage['period'].flatten()
|
535
|
+
sortix = np.argsort(omega)
|
536
|
+
omega = omega[sortix]
|
537
|
+
|
538
|
+
mass = mass[:,:, sortix]
|
539
|
+
damping = damping[:,:, sortix]
|
540
|
+
stiffness = (stiffness + stiffness.T)/2
|
541
|
+
|
542
|
+
return mass, damping, stiffness, static_mass, omega
|
543
|
+
|
544
|
+
|
545
|
+
|
546
|
+
def import_wamit_mat(wamit_path, suffix=['hst', 'mmx', '1'], rho=1020, L=1.0, g=9.80665):
|
547
|
+
'''
|
548
|
+
Import system matrices from WAMIT results (frequency dependent and constant).
|
549
|
+
|
550
|
+
Parameters
|
551
|
+
-------------
|
552
|
+
wamit_path : str
|
553
|
+
path to WAMIT file to open
|
554
|
+
suffix : ['hst', 'mmx', '1']
|
555
|
+
all suffixes used - all default values required for full output
|
556
|
+
rho : 1020, optional
|
557
|
+
water mass density used to redimensionalize results
|
558
|
+
L : 1.0, optional
|
559
|
+
redimensionaling length - 1.0 is fine for most use cases
|
560
|
+
g : 9.80665, optional
|
561
|
+
gravitational constant for redimensionalization of data
|
562
|
+
|
563
|
+
Returns
|
564
|
+
-------------
|
565
|
+
A : float
|
566
|
+
6-by-6 added mass matrix of body
|
567
|
+
B : float
|
568
|
+
6-by-6 radiation damping matrix of body
|
569
|
+
K0 : float
|
570
|
+
6-by-6 restoring stiffness matrix of body
|
571
|
+
M0 : float
|
572
|
+
6-by-6 static (constant) mass matrix of body
|
573
|
+
this represents inertia of pontoon object itself
|
574
|
+
omega : float
|
575
|
+
numpy array describing numerical frequency axis (corresponding to mass and damping
|
576
|
+
output)
|
577
|
+
'''
|
578
|
+
if suffix.count('hst')>=1:
|
579
|
+
# Hydrostatic stiffness
|
580
|
+
k_list = np.loadtxt(wamit_path+'.hst')
|
581
|
+
K0 = np.reshape(k_list[:,2], [6,6]) #assumes the order of elements in k_list
|
582
|
+
dimK = 0*K0
|
583
|
+
dimK[2, 2] = rho*g*L**2
|
584
|
+
dimK[2, 3] = dimK[3, 2] = rho*g*L**3
|
585
|
+
dimK[2, 4] = dimK[4, 2] = rho*g*L**3
|
586
|
+
dimK[3::, :] = rho*g*L**4
|
587
|
+
dimK[:, 3::] = rho*g*L**4
|
588
|
+
|
589
|
+
K0 = K0*dimK
|
590
|
+
|
591
|
+
else:
|
592
|
+
K0 = 0
|
593
|
+
|
594
|
+
if suffix.count('mmx')>=1:
|
595
|
+
# Inertia matrix
|
596
|
+
m_list = np.loadtxt(wamit_path+'.mmx', skiprows=12)
|
597
|
+
M0 = np.reshape(m_list[:,2], [6,6])
|
598
|
+
else:
|
599
|
+
M0 = 0
|
600
|
+
|
601
|
+
if suffix.count('1')>=1:
|
602
|
+
# Added mass and added damping
|
603
|
+
mc_list = np.loadtxt(wamit_path+'.1')
|
604
|
+
period = mc_list[0::10,0]
|
605
|
+
Aper = np.zeros([6,6,len(period)])
|
606
|
+
Bper = np.zeros([6,6,len(period)])
|
607
|
+
|
608
|
+
for k in range(0,int(mc_list.shape[0]/10)):
|
609
|
+
mc_this = mc_list[k*10:k*10+10,:]
|
610
|
+
dof1, dof2 = mc_this[:,1].astype('int')-1, mc_this[:, 2].astype('int')-1
|
611
|
+
|
612
|
+
for ix, i in enumerate(dof1):
|
613
|
+
j = dof2[ix]
|
614
|
+
Aper[i, j, k] = mc_this[ix, 3]
|
615
|
+
Bper[i, j, k] = mc_this[ix, 4]
|
616
|
+
|
617
|
+
# Re-dimensionalize and flip
|
618
|
+
omega = np.flip(2*np.pi/period, axis=0)
|
619
|
+
|
620
|
+
dimA = 0*Aper
|
621
|
+
dimA[0:3, 0:3, :] = rho*L**3
|
622
|
+
dimA[0:3, 3:-1, :] = rho*L**4
|
623
|
+
dimA[3:-1, 0:3, :] = rho*L**4
|
624
|
+
dimA[3:-1, 3:-1, :] = rho*L**5
|
625
|
+
|
626
|
+
dimB = 0*Bper
|
627
|
+
dimB[0:3, 0:3, :] = rho*L**3*omega
|
628
|
+
dimB[0:3, 3:-1, :] = rho*L**4*omega
|
629
|
+
dimB[3:-1, 0:3, :] = rho*L**4*omega
|
630
|
+
dimB[3:-1, 3:-1, :] = rho*L**5*omega
|
631
|
+
|
632
|
+
A = np.flip(Aper, axis=2)*dimA
|
633
|
+
B = np.flip(Bper, axis=2)*dimB
|
634
|
+
else:
|
635
|
+
A = 0
|
636
|
+
B = 0
|
637
|
+
|
638
|
+
return A, B, K0, M0, omega
|
639
|
+
|
640
|
+
|
641
|
+
def import_wamit_force(wamit_path, A=1.0, rho=1020, L=1.0, g=9.80665):
|
642
|
+
'''
|
643
|
+
Import hydrodynamic transfer function from WAMIT '.2'-file.
|
644
|
+
|
645
|
+
Parameters
|
646
|
+
-----------
|
647
|
+
wamit_path :
|
648
|
+
A : 1.0, optional
|
649
|
+
area used to redimensionalize results (1.0 is fine for most use cases)
|
650
|
+
rho : 1020, optional
|
651
|
+
water mass density used to redimensionalize results
|
652
|
+
L : 1.0, optional
|
653
|
+
redimensionaling length (1.0 is fine for most use cases)
|
654
|
+
g : 9.80665, optional
|
655
|
+
gravitational constant for redimensionalization of data
|
656
|
+
|
657
|
+
Returns
|
658
|
+
----------
|
659
|
+
omega : float
|
660
|
+
numpy array describing numerical frequency axis
|
661
|
+
theta : float
|
662
|
+
numpy array describing numerical directional axis
|
663
|
+
fhyd : float
|
664
|
+
complex numpy 3d-array describing the transfer functions relating
|
665
|
+
regular waves with unit height to the 6 relevant forces and moments
|
666
|
+
6-by-len(theta)-by-len(omega)
|
667
|
+
'''
|
668
|
+
|
669
|
+
# Transfer function
|
670
|
+
q_list = np.loadtxt(wamit_path+'.2')
|
671
|
+
period = np.unique(q_list[:,0])
|
672
|
+
n_omega = len(period)
|
673
|
+
n_theta = int(len(q_list[:,0])/len(period)/6)
|
674
|
+
theta = q_list[0:6*n_theta:6,1]
|
675
|
+
f_hyd_per = np.zeros([6, n_theta, n_omega]).astype('complex')
|
676
|
+
|
677
|
+
for k in range(0, n_omega):
|
678
|
+
q_this = q_list[k*n_theta*6:k*n_theta*6+n_theta*6]
|
679
|
+
for dof in range(0,6):
|
680
|
+
f_hyd_per[dof, :, k] = q_this[dof::6, 5] + q_this[dof::6, 6]*1j
|
681
|
+
|
682
|
+
omega = np.flip(2*np.pi/period, axis=0)
|
683
|
+
|
684
|
+
# Re-dimensionalize and flip
|
685
|
+
dim = 0*f_hyd_per
|
686
|
+
dim[0::6,:,:] = rho*g*A*L**2
|
687
|
+
dim[1::6,:,:] = rho*g*A*L**2
|
688
|
+
dim[2::6,:,:] = rho*g*A*L**2
|
689
|
+
dim[3::6,:,:] = rho*g*A*L**3
|
690
|
+
dim[4::6,:,:] = rho*g*A*L**3
|
691
|
+
dim[5::6,:,:] = rho*g*A*L**3
|
692
|
+
|
693
|
+
f_hyd = np.flip(f_hyd_per, axis=2) * dim
|
694
|
+
|
695
|
+
return omega, theta, f_hyd
|
696
|
+
|