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/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
+