wawi 0.0.16__py3-none-any.whl → 0.0.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wawi/__init__.py +1 -1
- wawi/fe.py +259 -12
- wawi/general.py +538 -70
- wawi/identification.py +33 -11
- wawi/io.py +206 -3
- wawi/modal.py +384 -6
- wawi/model/_model.py +1 -1
- wawi/plot.py +403 -107
- wawi/prob.py +28 -0
- wawi/random.py +218 -2
- wawi/signal.py +111 -3
- wawi/structural.py +317 -59
- wawi/time_domain.py +122 -1
- wawi/tools.py +23 -0
- wawi/wave.py +668 -123
- wawi/wind.py +775 -32
- wawi/wind_code.py +26 -0
- {wawi-0.0.16.dist-info → wawi-0.0.18.dist-info}/METADATA +42 -4
- wawi-0.0.18.dist-info/RECORD +38 -0
- {wawi-0.0.16.dist-info → wawi-0.0.18.dist-info}/WHEEL +1 -1
- wawi-0.0.16.dist-info/RECORD +0 -38
- {wawi-0.0.16.dist-info → wawi-0.0.18.dist-info}/licenses/LICENSE +0 -0
- {wawi-0.0.16.dist-info → wawi-0.0.18.dist-info}/top_level.txt +0 -0
wawi/general.py
CHANGED
@@ -4,6 +4,27 @@ import numpy as np
|
|
4
4
|
from inspect import isfunction
|
5
5
|
|
6
6
|
def block_truncate(M, p, n_dofs=6):
|
7
|
+
"""
|
8
|
+
Truncate a block matrix to a specified size.
|
9
|
+
|
10
|
+
Parameters
|
11
|
+
----------
|
12
|
+
M : ndarray
|
13
|
+
Input matrix (2D or 3D).
|
14
|
+
p : int
|
15
|
+
Number of blocks to keep.
|
16
|
+
n_dofs : int, optional
|
17
|
+
Number of degrees of freedom per block (default is 6).
|
18
|
+
|
19
|
+
Returns
|
20
|
+
-------
|
21
|
+
Mtr : ndarray
|
22
|
+
Truncated matrix.
|
23
|
+
|
24
|
+
Notes
|
25
|
+
-----
|
26
|
+
Docstring generated using GitHub Copilot.
|
27
|
+
"""
|
7
28
|
Mtr = M*1
|
8
29
|
if np.ndim(M)==2:
|
9
30
|
is_2d = True
|
@@ -28,6 +49,31 @@ def block_truncate(M, p, n_dofs=6):
|
|
28
49
|
return Mtr
|
29
50
|
|
30
51
|
def eval_fun_or_scalar(par, x, y, x0=0, y0=0):
|
52
|
+
"""
|
53
|
+
Evaluate a function or return a scalar.
|
54
|
+
|
55
|
+
Parameters
|
56
|
+
----------
|
57
|
+
par : callable or scalar
|
58
|
+
Function or scalar to evaluate.
|
59
|
+
x : float
|
60
|
+
x value.
|
61
|
+
y : float
|
62
|
+
y value.
|
63
|
+
x0 : float, optional
|
64
|
+
x offset (default is 0).
|
65
|
+
y0 : float, optional
|
66
|
+
y offset (default is 0).
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
result : float
|
71
|
+
Evaluated result.
|
72
|
+
|
73
|
+
Notes
|
74
|
+
-----
|
75
|
+
Docstring generated using GitHub Copilot.
|
76
|
+
"""
|
31
77
|
if isfunction(par):
|
32
78
|
if 'x' in par.__code__.co_varnames and 'y' in par.__code__.co_varnames:
|
33
79
|
return par(x - x0, y - y0)
|
@@ -38,9 +84,24 @@ def eval_fun_or_scalar(par, x, y, x0=0, y0=0):
|
|
38
84
|
else:
|
39
85
|
return par
|
40
86
|
|
41
|
-
|
42
|
-
#%%
|
43
87
|
def zero_pad_upsample(B, omega, omega_max):
|
88
|
+
"""
|
89
|
+
Zero-pad and upsample a vector to a new maximum frequency.
|
90
|
+
|
91
|
+
Parameters
|
92
|
+
----------
|
93
|
+
B : ndarray
|
94
|
+
Input vector.
|
95
|
+
omega : ndarray
|
96
|
+
Frequency vector.
|
97
|
+
omega_max : float
|
98
|
+
Maximum frequency after upsampling.
|
99
|
+
|
100
|
+
Returns
|
101
|
+
-------
|
102
|
+
Bi : ndarray
|
103
|
+
Zero-padded and upsampled vector.
|
104
|
+
"""
|
44
105
|
dw = omega[1]-omega[0]
|
45
106
|
omega_max0 = np.max(omega)
|
46
107
|
n_add = int(np.floor((omega_max-omega_max0)/dw))
|
@@ -48,14 +109,51 @@ def zero_pad_upsample(B, omega, omega_max):
|
|
48
109
|
return Bi
|
49
110
|
|
50
111
|
def get_omega_upsampled(omega, omega_max):
|
112
|
+
"""
|
113
|
+
Get upsampled frequency vector.
|
114
|
+
|
115
|
+
Parameters
|
116
|
+
----------
|
117
|
+
omega : ndarray
|
118
|
+
Original frequency vector.
|
119
|
+
omega_max : float
|
120
|
+
Maximum frequency after upsampling.
|
121
|
+
|
122
|
+
Returns
|
123
|
+
-------
|
124
|
+
omegai : ndarray
|
125
|
+
Upsampled frequency vector.
|
126
|
+
"""
|
51
127
|
dw = omega[1]-omega[0]
|
52
128
|
omega_max0 = np.max(omega)
|
53
129
|
omega_add = np.arange(omega_max0+dw, omega_max, dw)
|
54
130
|
omegai = np.hstack([omega, omega_add])
|
55
131
|
return omegai
|
56
132
|
|
57
|
-
#%% Misc
|
58
133
|
def equal_energy_omega(S, omega_min, omega_max, n, dmax=np.inf, domega_ref=1e-4):
|
134
|
+
"""
|
135
|
+
Generate frequency vector with equal energy intervals.
|
136
|
+
|
137
|
+
Parameters
|
138
|
+
----------
|
139
|
+
S : callable
|
140
|
+
Spectral density function.
|
141
|
+
omega_min : float
|
142
|
+
Minimum frequency.
|
143
|
+
omega_max : float
|
144
|
+
Maximum frequency.
|
145
|
+
n : int
|
146
|
+
Number of intervals.
|
147
|
+
dmax : float, optional
|
148
|
+
Maximum interval width (default is np.inf).
|
149
|
+
domega_ref : float, optional
|
150
|
+
Reference frequency step (default is 1e-4).
|
151
|
+
|
152
|
+
Returns
|
153
|
+
-------
|
154
|
+
omegas : ndarray
|
155
|
+
Frequency vector with equal energy intervals.
|
156
|
+
"""
|
59
157
|
from scipy.integrate import cumtrapz
|
60
158
|
|
61
159
|
omega_ref = np.arange(omega_min, omega_max, domega_ref)
|
@@ -81,29 +179,100 @@ def equal_energy_omega(S, omega_min, omega_max, n, dmax=np.inf, domega_ref=1e-4)
|
|
81
179
|
|
82
180
|
return omegas
|
83
181
|
|
84
|
-
|
85
|
-
#%% Matrix manipulation / generation
|
86
182
|
def blkdiag(mat, n):
|
183
|
+
"""
|
184
|
+
Create a block diagonal matrix.
|
185
|
+
|
186
|
+
Parameters
|
187
|
+
----------
|
188
|
+
mat : ndarray
|
189
|
+
Matrix to repeat along the diagonal.
|
190
|
+
n : int
|
191
|
+
Number of blocks.
|
192
|
+
|
193
|
+
Returns
|
194
|
+
-------
|
195
|
+
blk : ndarray
|
196
|
+
Block diagonal matrix.
|
197
|
+
"""
|
87
198
|
return np.kron(np.eye(n), mat)
|
88
199
|
|
89
200
|
def fun_sum(*functions):
|
201
|
+
"""
|
202
|
+
Sum multiple functions.
|
203
|
+
|
204
|
+
Parameters
|
205
|
+
----------
|
206
|
+
*functions : callable
|
207
|
+
Functions to sum.
|
208
|
+
|
209
|
+
Returns
|
210
|
+
-------
|
211
|
+
fun : callable
|
212
|
+
Function representing the sum.
|
213
|
+
"""
|
90
214
|
def fun(x):
|
91
215
|
return sum([f(x) for f in functions])
|
92
216
|
|
93
217
|
return fun
|
94
218
|
|
95
219
|
def fun_scale(function, scaling):
|
220
|
+
"""
|
221
|
+
Scale a function by a constant.
|
222
|
+
|
223
|
+
Parameters
|
224
|
+
----------
|
225
|
+
function : callable
|
226
|
+
Function to scale.
|
227
|
+
scaling : float
|
228
|
+
Scaling factor.
|
229
|
+
|
230
|
+
Returns
|
231
|
+
-------
|
232
|
+
fun : callable
|
233
|
+
Scaled function.
|
234
|
+
"""
|
96
235
|
def fun(x):
|
97
236
|
return function(x)*scaling
|
98
237
|
|
99
238
|
return fun
|
100
239
|
|
101
240
|
def fun_const_sum(function, constant):
|
241
|
+
"""
|
242
|
+
Add a constant to a function.
|
243
|
+
|
244
|
+
Parameters
|
245
|
+
----------
|
246
|
+
function : callable
|
247
|
+
Function to add to.
|
248
|
+
constant : float
|
249
|
+
Constant to add.
|
250
|
+
|
251
|
+
Returns
|
252
|
+
-------
|
253
|
+
fun : callable
|
254
|
+
Function plus constant.
|
255
|
+
"""
|
102
256
|
def fun(x):
|
103
257
|
return function(x) + constant
|
104
258
|
return fun
|
105
259
|
|
106
260
|
def eval_3d_fun(fun, z):
|
261
|
+
"""
|
262
|
+
Evaluate a function over a 1D array and stack results into a 3D array.
|
263
|
+
|
264
|
+
Parameters
|
265
|
+
----------
|
266
|
+
fun : callable
|
267
|
+
Function to evaluate.
|
268
|
+
z : ndarray
|
269
|
+
1D array of input values.
|
270
|
+
|
271
|
+
Returns
|
272
|
+
-------
|
273
|
+
result : ndarray
|
274
|
+
Stacked results.
|
275
|
+
"""
|
107
276
|
return np.stack([fun(z_k) for z_k in z], axis=2)
|
108
277
|
|
109
278
|
def correct_matrix_size(mat, n_freqs, n_dofs):
|
@@ -111,20 +280,19 @@ def correct_matrix_size(mat, n_freqs, n_dofs):
|
|
111
280
|
Extend matrix to specified dimensions (frequencies and DOFs).
|
112
281
|
|
113
282
|
Parameters
|
114
|
-
|
115
|
-
mat :
|
116
|
-
|
283
|
+
----------
|
284
|
+
mat : ndarray
|
285
|
+
Matrix to be checked and possibly modified.
|
117
286
|
n_freqs : int
|
118
|
-
|
287
|
+
Number of frequencies (depth of final matrix).
|
119
288
|
n_dofs : int
|
120
|
-
|
289
|
+
Number of degrees of freedom (height and width of final matrix).
|
121
290
|
|
122
291
|
Returns
|
123
|
-
|
124
|
-
mat_extended :
|
125
|
-
|
292
|
+
-------
|
293
|
+
mat_extended : ndarray
|
294
|
+
Corrected/verified matrix.
|
126
295
|
"""
|
127
|
-
|
128
296
|
mat_shape = np.array(np.shape(mat))
|
129
297
|
|
130
298
|
for ix in range(0, 3-len(mat_shape)):
|
@@ -142,9 +310,37 @@ def correct_matrix_size(mat, n_freqs, n_dofs):
|
|
142
310
|
return mat_extended
|
143
311
|
|
144
312
|
def wrap_to_pi(angle):
|
313
|
+
"""
|
314
|
+
Wrap angle to [-pi, pi].
|
315
|
+
|
316
|
+
Parameters
|
317
|
+
----------
|
318
|
+
angle : float or ndarray
|
319
|
+
Input angle(s).
|
320
|
+
|
321
|
+
Returns
|
322
|
+
-------
|
323
|
+
wrapped : float or ndarray
|
324
|
+
Wrapped angle(s).
|
325
|
+
"""
|
145
326
|
return (angle + np.pi) % (2*np.pi) - np.pi
|
146
327
|
|
147
328
|
def wrap_to_circular(x, rng=[-np.pi, np.pi]):
|
329
|
+
"""
|
330
|
+
Wrap values to a circular range.
|
331
|
+
|
332
|
+
Parameters
|
333
|
+
----------
|
334
|
+
x : float or ndarray
|
335
|
+
Input value(s).
|
336
|
+
rng : list or ndarray, optional
|
337
|
+
Range to wrap to (default is [-pi, pi]).
|
338
|
+
|
339
|
+
Returns
|
340
|
+
-------
|
341
|
+
wrapped : float or ndarray
|
342
|
+
Wrapped value(s).
|
343
|
+
"""
|
148
344
|
x0 = np.mean(rng)
|
149
345
|
dx_sym = (np.max(rng) - np.min(rng))/2
|
150
346
|
x_centered = (x + dx_sym) % (2*dx_sym) - dx_sym
|
@@ -155,15 +351,20 @@ def merge_tr_phi(phi_trans, phi_rot, thread_stack=True):
|
|
155
351
|
"""
|
156
352
|
Merge matrices of phi for translational and rotational DOFs.
|
157
353
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
354
|
+
Parameters
|
355
|
+
----------
|
356
|
+
phi_trans : ndarray
|
357
|
+
Phi matrix with only translational DOFs.
|
358
|
+
phi_rot : ndarray
|
359
|
+
Phi matrix with only rotational DOFs.
|
360
|
+
thread_stack : bool, optional
|
361
|
+
If True, stack DOFs in thread order (default is True).
|
166
362
|
|
363
|
+
Returns
|
364
|
+
-------
|
365
|
+
phi_combined : ndarray
|
366
|
+
Phi matrix with all DOFs.
|
367
|
+
"""
|
167
368
|
Ndofs = phi_trans.shape[0]*2
|
168
369
|
|
169
370
|
if thread_stack is True:
|
@@ -179,9 +380,22 @@ def merge_tr_phi(phi_trans, phi_rot, thread_stack=True):
|
|
179
380
|
|
180
381
|
return phi_combined
|
181
382
|
|
182
|
-
|
183
383
|
def mat3d_sel(mat, k):
|
384
|
+
"""
|
385
|
+
Select a 2D slice from a 3D matrix.
|
386
|
+
|
387
|
+
Parameters
|
388
|
+
----------
|
389
|
+
mat : ndarray
|
390
|
+
3D matrix.
|
391
|
+
k : int
|
392
|
+
Index of the slice.
|
184
393
|
|
394
|
+
Returns
|
395
|
+
-------
|
396
|
+
matsel : ndarray
|
397
|
+
Selected 2D matrix.
|
398
|
+
"""
|
185
399
|
if len(np.shape(mat)) is 3:
|
186
400
|
matsel = mat[:, :, k]
|
187
401
|
else:
|
@@ -189,40 +403,47 @@ def mat3d_sel(mat, k):
|
|
189
403
|
|
190
404
|
return matsel
|
191
405
|
|
192
|
-
|
193
|
-
|
194
406
|
def interp1z(z,mat,znew):
|
195
407
|
"""
|
196
|
-
Interpolate 3D matrix along z-component.
|
408
|
+
Interpolate 3D matrix along z-component.
|
197
409
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
410
|
+
Parameters
|
411
|
+
----------
|
412
|
+
z : ndarray
|
413
|
+
z axis.
|
414
|
+
mat : ndarray
|
415
|
+
3D matrix.
|
416
|
+
znew : ndarray
|
417
|
+
New z axis.
|
204
418
|
|
205
|
-
|
206
|
-
|
419
|
+
Returns
|
420
|
+
-------
|
421
|
+
matnew : ndarray
|
422
|
+
Interpolated 3D matrix.
|
423
|
+
"""
|
207
424
|
matnew = np.zeros([1,len(mat[0]),len(mat[0][0])])
|
208
425
|
for dof1 in range(0,len(mat[0])):
|
209
426
|
for dof2 in range(0,len(mat[0][0])):
|
210
427
|
matnew[:,dof1,dof2]=np.interp(znew,z,mat[:,dof1,dof2])
|
211
428
|
return matnew
|
212
429
|
|
213
|
-
|
214
430
|
def interpolate_3d(z, mat, zi):
|
215
431
|
"""
|
216
432
|
Interpolates 3D NumPy array.
|
217
433
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
434
|
+
Parameters
|
435
|
+
----------
|
436
|
+
z : ndarray
|
437
|
+
Original z axis.
|
438
|
+
mat : ndarray
|
439
|
+
3D matrix (n_x-by-n_y-by-n_z).
|
440
|
+
zi : ndarray
|
441
|
+
Interpolated z axis.
|
225
442
|
|
443
|
+
Returns
|
444
|
+
-------
|
445
|
+
mati : ndarray
|
446
|
+
Interpolated matrix.
|
226
447
|
"""
|
227
448
|
mat_shape = np.shape(mat)
|
228
449
|
mati = np.zeros([mat_shape[0], mat_shape[1], len(zi)])
|
@@ -233,8 +454,24 @@ def interpolate_3d(z, mat, zi):
|
|
233
454
|
|
234
455
|
return mati
|
235
456
|
|
236
|
-
|
237
457
|
def fast_interpolation(x, y, new_x):
|
458
|
+
"""
|
459
|
+
Fast cubic spline interpolation for multidimensional arrays.
|
460
|
+
|
461
|
+
Parameters
|
462
|
+
----------
|
463
|
+
x : ndarray
|
464
|
+
Original x values.
|
465
|
+
y : ndarray
|
466
|
+
Original y values.
|
467
|
+
new_x : ndarray
|
468
|
+
New x values for interpolation.
|
469
|
+
|
470
|
+
Returns
|
471
|
+
-------
|
472
|
+
result : ndarray
|
473
|
+
Interpolated values.
|
474
|
+
"""
|
238
475
|
from scipy.interpolate import interp1d
|
239
476
|
from scipy.interpolate._fitpack import _bspleval
|
240
477
|
f = interp1d(x, y, axis=-1, kind=3)
|
@@ -244,10 +481,22 @@ def fast_interpolation(x, y, new_x):
|
|
244
481
|
result[i, j] = _bspleval(value, x, cvals[:, i, j], k, 0)
|
245
482
|
return result
|
246
483
|
|
484
|
+
def transformation_matrix(angles, axis):
|
485
|
+
"""
|
486
|
+
Generate transformation matrices for a set of angles and a given axis.
|
247
487
|
|
488
|
+
Parameters
|
489
|
+
----------
|
490
|
+
angles : ndarray
|
491
|
+
Array of angles.
|
492
|
+
axis : int
|
493
|
+
Axis of rotation (0, 1, or 2).
|
248
494
|
|
249
|
-
|
250
|
-
|
495
|
+
Returns
|
496
|
+
-------
|
497
|
+
T : ndarray
|
498
|
+
Transformation matrices (len(angles), 6, 6).
|
499
|
+
"""
|
251
500
|
T = np.empty([len(angles),6,6])
|
252
501
|
for idx,angle in enumerate(angles):
|
253
502
|
c = np.cos(angle)
|
@@ -263,21 +512,24 @@ def transformation_matrix(angles, axis):
|
|
263
512
|
|
264
513
|
return T
|
265
514
|
|
266
|
-
|
267
515
|
def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
|
268
516
|
"""
|
269
517
|
Establishes 3D rotation matrix based on Euler-Rodrigues formula.
|
270
|
-
See https://en.wikipedia.org/wiki/Euler-Rodrigues_formula.
|
271
|
-
|
272
|
-
Args:
|
273
|
-
theta: the rotation angle (in radians)
|
274
|
-
[rotaxis]: vector defining rotation axis (default [0, 0, 1])
|
275
518
|
|
276
|
-
|
277
|
-
|
519
|
+
Parameters
|
520
|
+
----------
|
521
|
+
theta : float
|
522
|
+
Rotation angle (in radians).
|
523
|
+
rotaxis : array_like, optional
|
524
|
+
Vector defining rotation axis (default [0, 0, 1]).
|
525
|
+
style : str, optional
|
526
|
+
Output style ('row' or other, default 'row').
|
278
527
|
|
528
|
+
Returns
|
529
|
+
-------
|
530
|
+
T : ndarray
|
531
|
+
Transformation matrix.
|
279
532
|
"""
|
280
|
-
|
281
533
|
axis = np.asarray(rotaxis)
|
282
534
|
axis = rotaxis/np.sqrt(np.dot(rotaxis, rotaxis)) # Normalize
|
283
535
|
a = np.cos(theta/2.0)
|
@@ -293,16 +545,44 @@ def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
|
|
293
545
|
|
294
546
|
return T
|
295
547
|
|
296
|
-
|
297
548
|
def multi_rodrot(angles, rotaxis=[0,0,1], style='row'):
|
549
|
+
"""
|
550
|
+
Generate multiple 6x6 rotation matrices for a set of angles.
|
551
|
+
|
552
|
+
Parameters
|
553
|
+
----------
|
554
|
+
angles : ndarray
|
555
|
+
Array of angles.
|
556
|
+
rotaxis : array_like, optional
|
557
|
+
Rotation axis (default [0, 0, 1]).
|
558
|
+
style : str, optional
|
559
|
+
Output style (default 'row').
|
560
|
+
|
561
|
+
Returns
|
562
|
+
-------
|
563
|
+
T : list of ndarray
|
564
|
+
List of 6x6 rotation matrices.
|
565
|
+
"""
|
298
566
|
T = [None]*len(angles)
|
299
567
|
for ix, angle in enumerate(angles):
|
300
568
|
T[ix] = blkdiag(rodrot(angle, rotaxis=rotaxis, style=style), 2)
|
301
569
|
|
302
570
|
return T
|
303
571
|
|
304
|
-
|
305
572
|
def stack_T(T_multi):
|
573
|
+
"""
|
574
|
+
Stack multiple transformation matrices into a block diagonal matrix.
|
575
|
+
|
576
|
+
Parameters
|
577
|
+
----------
|
578
|
+
T_multi : list of ndarray
|
579
|
+
List of transformation matrices.
|
580
|
+
|
581
|
+
Returns
|
582
|
+
-------
|
583
|
+
T : ndarray
|
584
|
+
Block diagonal transformation matrix.
|
585
|
+
"""
|
306
586
|
n_dofs = np.shape(T_multi[0])[0]
|
307
587
|
N = len(T_multi)
|
308
588
|
|
@@ -313,8 +593,22 @@ def stack_T(T_multi):
|
|
313
593
|
|
314
594
|
return T
|
315
595
|
|
316
|
-
|
317
596
|
def transform_unit(e1, e2p):
|
597
|
+
"""
|
598
|
+
Generate a transformation matrix from two unit vectors.
|
599
|
+
|
600
|
+
Parameters
|
601
|
+
----------
|
602
|
+
e1 : array_like
|
603
|
+
First unit vector.
|
604
|
+
e2p : array_like
|
605
|
+
Second vector (not necessarily unit).
|
606
|
+
|
607
|
+
Returns
|
608
|
+
-------
|
609
|
+
T : ndarray
|
610
|
+
Transformation matrix (3x3).
|
611
|
+
"""
|
318
612
|
e1 = np.array(e1).flatten()
|
319
613
|
e2p = np.array(e2p).flatten()
|
320
614
|
|
@@ -329,8 +623,22 @@ def transform_unit(e1, e2p):
|
|
329
623
|
|
330
624
|
return T
|
331
625
|
|
332
|
-
|
333
626
|
def transform_3dmat(mat, T):
|
627
|
+
"""
|
628
|
+
Transform a 3D matrix using a transformation matrix.
|
629
|
+
|
630
|
+
Parameters
|
631
|
+
----------
|
632
|
+
mat : ndarray
|
633
|
+
3D matrix (n_dofs, n_dofs, n_freqs).
|
634
|
+
T : ndarray
|
635
|
+
Transformation matrix.
|
636
|
+
|
637
|
+
Returns
|
638
|
+
-------
|
639
|
+
mat_transformed : ndarray
|
640
|
+
Transformed 3D matrix.
|
641
|
+
"""
|
334
642
|
M, N = np.shape(T)[0:2]
|
335
643
|
[n_dofs, __, n_freqs] = np.shape(mat)
|
336
644
|
|
@@ -347,9 +655,20 @@ def transform_3dmat(mat, T):
|
|
347
655
|
|
348
656
|
return mat_transformed
|
349
657
|
|
350
|
-
#%% Other
|
351
658
|
def assess_diagonality_fro(M):
|
352
|
-
|
659
|
+
"""
|
660
|
+
Assess diagonality of a Hermitian positive-definite matrix using Frobenius norm.
|
661
|
+
|
662
|
+
Parameters
|
663
|
+
----------
|
664
|
+
M : ndarray
|
665
|
+
Input matrix (2D, square).
|
666
|
+
|
667
|
+
Returns
|
668
|
+
-------
|
669
|
+
diagonality : float
|
670
|
+
Diagonality measure.
|
671
|
+
"""
|
353
672
|
if M.ndim != 2:
|
354
673
|
raise ValueError('Input must be 2d array.')
|
355
674
|
if np.shape(M)[0] != np.shape(M)[1]:
|
@@ -364,9 +683,20 @@ def assess_diagonality_fro(M):
|
|
364
683
|
|
365
684
|
return diagonality
|
366
685
|
|
367
|
-
|
368
686
|
def assess_diagonality(M):
|
369
|
-
|
687
|
+
"""
|
688
|
+
Assess diagonality of a Hermitian positive-definite matrix.
|
689
|
+
|
690
|
+
Parameters
|
691
|
+
----------
|
692
|
+
M : ndarray
|
693
|
+
Input matrix (2D, square).
|
694
|
+
|
695
|
+
Returns
|
696
|
+
-------
|
697
|
+
diagonality : float
|
698
|
+
Diagonality measure.
|
699
|
+
"""
|
370
700
|
if M.ndim != 2:
|
371
701
|
raise ValueError('Input must be 2d array.')
|
372
702
|
if np.shape(M)[0] != np.shape(M)[1]:
|
@@ -380,16 +710,52 @@ def assess_diagonality(M):
|
|
380
710
|
|
381
711
|
return diagonality
|
382
712
|
|
383
|
-
|
384
713
|
def modify_stiffness(stiffness, submerged_vol, water_dens, g, z_cog, z_cog_mod):
|
714
|
+
"""
|
715
|
+
Modify stiffness matrix for change in center of gravity.
|
716
|
+
|
717
|
+
Parameters
|
718
|
+
----------
|
719
|
+
stiffness : ndarray
|
720
|
+
Original stiffness matrix.
|
721
|
+
submerged_vol : float
|
722
|
+
Submerged volume.
|
723
|
+
water_dens : float
|
724
|
+
Water density.
|
725
|
+
g : float
|
726
|
+
Gravitational acceleration.
|
727
|
+
z_cog : float
|
728
|
+
Original center of gravity (z).
|
729
|
+
z_cog_mod : float
|
730
|
+
Modified center of gravity (z).
|
731
|
+
|
732
|
+
Returns
|
733
|
+
-------
|
734
|
+
stiffness_mod : ndarray
|
735
|
+
Modified stiffness matrix.
|
736
|
+
"""
|
385
737
|
stiffness_mod = stiffness
|
386
738
|
stiffness_mod[3, 3] = stiffness[3, 3] + submerged_vol * water_dens * g * (z_cog - z_cog_mod)
|
387
739
|
stiffness_mod[4, 4] = stiffness[4, 4]+ submerged_vol * water_dens * g * (z_cog - z_cog_mod)
|
388
740
|
|
389
741
|
return stiffness_mod
|
390
742
|
|
391
|
-
|
392
743
|
def maxreal(phi, preserve_conjugates=False):
|
744
|
+
"""
|
745
|
+
Rotate complex mode shapes to maximize real part.
|
746
|
+
|
747
|
+
Parameters
|
748
|
+
----------
|
749
|
+
phi : ndarray
|
750
|
+
Mode shape matrix.
|
751
|
+
preserve_conjugates : bool, optional
|
752
|
+
If True, preserve conjugate pairs (default False).
|
753
|
+
|
754
|
+
Returns
|
755
|
+
-------
|
756
|
+
phi_max_real : ndarray
|
757
|
+
Rotated mode shapes with maximized real part.
|
758
|
+
"""
|
393
759
|
angles = np.expand_dims(np.arange(0,np.pi/2, 0.01), axis=0)
|
394
760
|
|
395
761
|
if phi.ndim==1:
|
@@ -405,8 +771,26 @@ def maxreal(phi, preserve_conjugates=False):
|
|
405
771
|
|
406
772
|
return phi_max_real
|
407
773
|
|
408
|
-
|
409
774
|
def create_circular_x(x, tol=1e-5, rng=np.array([-np.pi, np.pi])):
|
775
|
+
"""
|
776
|
+
Create a circularly wrapped and sorted version of x.
|
777
|
+
|
778
|
+
Parameters
|
779
|
+
----------
|
780
|
+
x : ndarray
|
781
|
+
Input array.
|
782
|
+
tol : float, optional
|
783
|
+
Tolerance for uniqueness (default 1e-5).
|
784
|
+
rng : ndarray, optional
|
785
|
+
Range for wrapping (default [-pi, pi]).
|
786
|
+
|
787
|
+
Returns
|
788
|
+
-------
|
789
|
+
x_ang : ndarray
|
790
|
+
Wrapped and sorted array.
|
791
|
+
sort_ix : ndarray
|
792
|
+
Indices of unique values.
|
793
|
+
"""
|
410
794
|
xrng = np.max(x)-np.min(x)
|
411
795
|
|
412
796
|
wrapped_x = np.sort(wrap_to_circular(x, rng))
|
@@ -421,24 +805,93 @@ def create_circular_x(x, tol=1e-5, rng=np.array([-np.pi, np.pi])):
|
|
421
805
|
|
422
806
|
return x_ang, sort_ix
|
423
807
|
|
424
|
-
|
425
808
|
def interp1d_angular(x, mat, rng=[-np.pi, np.pi], axis=-1, **kwargs):
|
809
|
+
"""
|
810
|
+
Interpolate data defined on a circular domain.
|
811
|
+
|
812
|
+
Parameters
|
813
|
+
----------
|
814
|
+
x : ndarray
|
815
|
+
Input angular values.
|
816
|
+
mat : ndarray
|
817
|
+
Data to interpolate.
|
818
|
+
rng : list or ndarray, optional
|
819
|
+
Range for wrapping (default [-pi, pi]).
|
820
|
+
axis : int, optional
|
821
|
+
Axis along which to interpolate (default -1).
|
822
|
+
**kwargs
|
823
|
+
Additional arguments to scipy.interpolate.interp1d.
|
824
|
+
|
825
|
+
Returns
|
826
|
+
-------
|
827
|
+
interp_fun : callable
|
828
|
+
Interpolation function.
|
829
|
+
"""
|
426
830
|
from scipy.interpolate import interp1d
|
427
831
|
x_ang, sort_ix = create_circular_x(x, rng=rng)
|
428
832
|
mat = np.take(mat, sort_ix, axis=axis)
|
429
833
|
return lambda x: interp1d(x_ang, mat, axis=axis, **kwargs)(wrap_to_circular(x, rng=rng))
|
430
834
|
|
431
|
-
|
432
835
|
def interp_hydro_transfer(omega, theta, Q, rng=[-np.pi, np.pi], theta_axis=1, omega_axis=2, interpolation_kind='linear', theta_settings=dict(), omega_settings=dict()):
|
836
|
+
"""
|
837
|
+
Interpolate hydrodynamic transfer function in both frequency and angle.
|
838
|
+
|
839
|
+
Parameters
|
840
|
+
----------
|
841
|
+
omega : ndarray
|
842
|
+
Frequency vector.
|
843
|
+
theta : ndarray
|
844
|
+
Angle vector.
|
845
|
+
Q : ndarray
|
846
|
+
Transfer function data.
|
847
|
+
rng : list or ndarray, optional
|
848
|
+
Range for wrapping (default [-pi, pi]).
|
849
|
+
theta_axis : int, optional
|
850
|
+
Axis for theta (default 1).
|
851
|
+
omega_axis : int, optional
|
852
|
+
Axis for omega (default 2).
|
853
|
+
interpolation_kind : str, optional
|
854
|
+
Interpolation kind (default 'linear').
|
855
|
+
theta_settings : dict, optional
|
856
|
+
Additional settings for theta interpolation.
|
857
|
+
omega_settings : dict, optional
|
858
|
+
Additional settings for omega interpolation.
|
859
|
+
|
860
|
+
Returns
|
861
|
+
-------
|
862
|
+
Qi : callable
|
863
|
+
Interpolated transfer function.
|
864
|
+
"""
|
433
865
|
from scipy.interpolate import interp1d
|
434
866
|
Qi = lambda om, th: interp1d(omega, interp1d(theta, Q, axis=theta_axis, fill_value='extrapolate', **theta_settings)(wrap_to_circular(th, rng=rng)),
|
435
867
|
fill_value='extrapolate', kind=interpolation_kind, axis=omega_axis, **omega_settings)(om)
|
436
868
|
|
437
869
|
return Qi
|
438
|
-
|
439
|
-
|
440
870
|
|
441
871
|
def interp2d_angular(x, theta, mat, rng=[-np.pi, np.pi], method='linear', **kwargs):
|
872
|
+
"""
|
873
|
+
2D interpolation on a circular domain.
|
874
|
+
|
875
|
+
Parameters
|
876
|
+
----------
|
877
|
+
x : ndarray
|
878
|
+
First coordinate array.
|
879
|
+
theta : ndarray
|
880
|
+
Angular coordinate array.
|
881
|
+
mat : ndarray
|
882
|
+
Data to interpolate.
|
883
|
+
rng : list or ndarray, optional
|
884
|
+
Range for wrapping (default [-pi, pi]).
|
885
|
+
method : str, optional
|
886
|
+
Interpolation method (default 'linear').
|
887
|
+
**kwargs
|
888
|
+
Additional arguments to scipy.interpolate.griddata.
|
889
|
+
|
890
|
+
Returns
|
891
|
+
-------
|
892
|
+
funi : callable
|
893
|
+
Interpolation function.
|
894
|
+
"""
|
442
895
|
from scipy.interpolate import griddata
|
443
896
|
|
444
897
|
def funi(xi, thetai):
|
@@ -456,9 +909,24 @@ def interp2d_angular(x, theta, mat, rng=[-np.pi, np.pi], method='linear', **kwar
|
|
456
909
|
|
457
910
|
return funi
|
458
911
|
|
912
|
+
def uniquetol(a, tol):
|
913
|
+
"""
|
914
|
+
Return unique values of an array within a tolerance.
|
459
915
|
|
916
|
+
Parameters
|
917
|
+
----------
|
918
|
+
a : ndarray
|
919
|
+
Input array.
|
920
|
+
tol : float
|
921
|
+
Tolerance for uniqueness.
|
460
922
|
|
461
|
-
|
923
|
+
Returns
|
924
|
+
-------
|
925
|
+
result : ndarray
|
926
|
+
Unique values.
|
927
|
+
i : ndarray
|
928
|
+
Indices of unique values.
|
929
|
+
"""
|
462
930
|
import numpy as np
|
463
931
|
i = np.argsort(a.flat)
|
464
932
|
d = np.append(True, np.diff(a.flat[i]))
|