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/modal.py
ADDED
@@ -0,0 +1,608 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from .tools import print_progress as pp
|
3
|
+
from scipy.interpolate import interp1d
|
4
|
+
|
5
|
+
def maxreal(phi):
|
6
|
+
"""
|
7
|
+
Rotate complex vectors (stacked column-wise) such that the absolute values of the real parts are maximized.
|
8
|
+
|
9
|
+
Arguments
|
10
|
+
---------------------------
|
11
|
+
phi : double
|
12
|
+
complex-valued modal transformation matrix (column-wise stacked mode shapes)
|
13
|
+
|
14
|
+
Returns
|
15
|
+
---------------------------
|
16
|
+
phi_max_real : boolean
|
17
|
+
complex-valued modal transformation matrix, with vectors rotated to have maximum real parts
|
18
|
+
"""
|
19
|
+
|
20
|
+
angles = np.expand_dims(np.arange(0,np.pi/2, 0.01), axis=0)
|
21
|
+
phi_max_real = np.zeros(np.shape(phi)).astype('complex')
|
22
|
+
for mode in range(0,np.shape(phi)[1]):
|
23
|
+
rot_mode = np.dot(np.expand_dims(phi[:, mode], axis=1), np.exp(angles*1j))
|
24
|
+
max_angle_ix = np.argmax(np.sum(np.real(rot_mode)**2,axis=0), axis=0)
|
25
|
+
|
26
|
+
phi_max_real[:, mode] = phi[:, mode] * np.exp(angles[0, max_angle_ix]*1j)*np.sign(sum(np.real(phi[:, mode])))
|
27
|
+
|
28
|
+
return phi_max_real
|
29
|
+
|
30
|
+
def get_mode_sort(lambd, order_fun_dict=None, return_as_dict=True, remove_conjugates=['dynamic'], sort=['dynamic']):
|
31
|
+
order_fun_dict_std = {
|
32
|
+
'dynamic': lambda l: np.logical_and(np.imag(l)!=0, np.real(l)<0),
|
33
|
+
'undamped':lambda l: np.real(l)==0,
|
34
|
+
'unstable': lambda l: np.logical_and(np.imag(l)!=0, np.real(l)>0),
|
35
|
+
'overdamped': lambda l: np.logical_and(np.imag(l)==0, np.real(l)<0)
|
36
|
+
}
|
37
|
+
|
38
|
+
if order_fun_dict is None:
|
39
|
+
order_fun_dict = order_fun_dict_std
|
40
|
+
elif type(order_fun_dict) is list: #specify certain terms from standard dict defined
|
41
|
+
order_fun_dict = {key:order_fun_dict_std[key] for key in order_fun_dict}
|
42
|
+
|
43
|
+
ix_dict = dict()
|
44
|
+
|
45
|
+
for key, fun in order_fun_dict.items():
|
46
|
+
ix = np.where(fun(lambd))[0]
|
47
|
+
|
48
|
+
if key in sort:
|
49
|
+
ix2 = np.argsort(np.abs(lambd[ix]))
|
50
|
+
ix = ix[ix2]
|
51
|
+
|
52
|
+
if remove_conjugates:
|
53
|
+
ix = ix[::2]
|
54
|
+
|
55
|
+
ix_dict[key] = ix
|
56
|
+
|
57
|
+
|
58
|
+
if return_as_dict:
|
59
|
+
return ix_dict
|
60
|
+
else:
|
61
|
+
return np.hstack(list(ix_dict.values()))
|
62
|
+
|
63
|
+
def sort_modes(lambd, phi=None, order_fun_dict=None, return_as_dict=True, remove_conjugates=['dynamic'], sort=['dynamic']):
|
64
|
+
ix_dict = get_mode_sort(lambd, order_fun_dict=order_fun_dict, return_as_dict=True, remove_conjugates=['dynamic'], sort=['dynamic'])
|
65
|
+
lambd_dict = dict()
|
66
|
+
|
67
|
+
if phi is not None:
|
68
|
+
phi_dict = dict()
|
69
|
+
|
70
|
+
for key in ix_dict:
|
71
|
+
ix = ix_dict[key]
|
72
|
+
lambd_dict[key] = lambd[ix]
|
73
|
+
if phi is not None:
|
74
|
+
phi_dict[key] = phi[:, ix]
|
75
|
+
|
76
|
+
if not return_as_dict:
|
77
|
+
lambd_list = [lambdi for lambdi in list(lambd_dict.values()) if lambdi.size!=0]
|
78
|
+
lambd_sorted = np.hstack([lambd_list])
|
79
|
+
|
80
|
+
if phi is not None:
|
81
|
+
phi_list = [phii for phii in list(phi_dict.values()) if phii.size!=0]
|
82
|
+
phi_sorted = np.hstack(phi_list)
|
83
|
+
else:
|
84
|
+
phi_sorted = None
|
85
|
+
return lambd_sorted, phi_sorted
|
86
|
+
else:
|
87
|
+
return lambd_dict, phi_dict
|
88
|
+
|
89
|
+
|
90
|
+
def statespace(K, C, M):
|
91
|
+
ndofs = np.shape(K)[0]
|
92
|
+
A = np.zeros([2*ndofs, 2*ndofs])
|
93
|
+
A[0:ndofs, ndofs:2*ndofs] = np.eye(ndofs)
|
94
|
+
A[ndofs:2*ndofs, 0:ndofs] = -np.linalg.inv(M) @ K
|
95
|
+
A[ndofs:2*ndofs, ndofs:2*ndofs] = -np.linalg.inv(M) @ C
|
96
|
+
|
97
|
+
return A
|
98
|
+
|
99
|
+
|
100
|
+
def iteig(K, C, M, omega=None, omega_ref=0, input_functions=True, itmax=None, tol=None, keep_full=False,
|
101
|
+
mac_min=0.9, w_initial=None, normalize=False, print_progress=False, print_warnings=True,
|
102
|
+
track_by_psi=True, remove_velocity=True, divergence_protection=True):
|
103
|
+
|
104
|
+
mean_w = False
|
105
|
+
|
106
|
+
if itmax is None:
|
107
|
+
itmax = 15
|
108
|
+
|
109
|
+
if tol is None:
|
110
|
+
tol = 1e-4
|
111
|
+
|
112
|
+
|
113
|
+
if not input_functions:
|
114
|
+
K = interp1d(omega, K, kind='quadratic', fill_value='extrapolate')
|
115
|
+
C = interp1d(omega, C, kind='quadratic', fill_value='extrapolate')
|
116
|
+
M = interp1d(omega, M, kind='quadratic', fill_value='extrapolate')
|
117
|
+
|
118
|
+
ndofs = K(0).shape[0]
|
119
|
+
lambd = np.zeros([2*ndofs], dtype=complex)
|
120
|
+
q = np.zeros([2*ndofs, 2*ndofs], dtype=complex)
|
121
|
+
|
122
|
+
# Reference phi
|
123
|
+
A = statespace(K(omega_ref), C(omega_ref), M(omega_ref))
|
124
|
+
lambd_ref, q_ref = np.linalg.eig(A)
|
125
|
+
not_converged = []
|
126
|
+
omega_ref = np.abs(np.imag(lambd_ref))
|
127
|
+
|
128
|
+
if w_initial is None:
|
129
|
+
w_initial = np.sort(np.abs(np.imag(lambd_ref)))
|
130
|
+
|
131
|
+
for m in range(2*ndofs):
|
132
|
+
w = w_initial[m]
|
133
|
+
|
134
|
+
wprev = np.inf
|
135
|
+
wprev2 = -np.inf
|
136
|
+
|
137
|
+
phi = q[:,0]*0
|
138
|
+
phiprev = 0*phi
|
139
|
+
phiprev2 = 0*phi
|
140
|
+
|
141
|
+
q_ref_m = q_ref[:, m:m+1]
|
142
|
+
|
143
|
+
for i in range(0, itmax):
|
144
|
+
A = statespace(K(w), C(w), M(w))
|
145
|
+
lambdai, qi = np.linalg.eig(A)
|
146
|
+
|
147
|
+
if track_by_psi:
|
148
|
+
macs = xmacmat(qi, phi2=q_ref_m, conjugates=False)
|
149
|
+
m_ix = np.argmax(macs, axis=0)[0]
|
150
|
+
|
151
|
+
phi = qi[:, m_ix:m_ix+1]
|
152
|
+
lambdai = lambdai[m_ix]
|
153
|
+
else:
|
154
|
+
phi = qi[:, m:m+1]
|
155
|
+
lambdai = lambdai[m]
|
156
|
+
|
157
|
+
w = abs(np.imag(lambdai))
|
158
|
+
if mean_w:
|
159
|
+
w = (wprev+w)/2
|
160
|
+
|
161
|
+
if w==0:
|
162
|
+
mean_w = False
|
163
|
+
break
|
164
|
+
elif np.abs(w - wprev2)/w <= tol and np.abs(w - wprev)/w <= tol and mac(phi, phiprev)>=mac_min and mac(phi, phiprev2)>=mac_min: # Converged!
|
165
|
+
mean_w = False
|
166
|
+
break
|
167
|
+
elif np.abs(w - wprev2)/w <= tol and np.abs(w - wprev)/w > tol and mac(phi, phiprev)<mac_min and mac(phi, phiprev2)>=mac_min:
|
168
|
+
if divergence_protection:
|
169
|
+
mean_w = True
|
170
|
+
extra_message = 'Divergence protection active - attempting to continue with average values'
|
171
|
+
else:
|
172
|
+
extra_message = ''
|
173
|
+
if print_warnings:
|
174
|
+
print(f'Warning: Suspecting oscillatory divergence on iteration of mode {int(np.ceil(m/2))}. Please conduct checks.')
|
175
|
+
print(f'Re(lambd) = {np.real(lambdai):.2f}, Im(lambd) = {np.imag(lambdai):.2f}%')
|
176
|
+
print(extra_message)
|
177
|
+
|
178
|
+
wprev2 = wprev*1
|
179
|
+
wprev = w*1
|
180
|
+
|
181
|
+
phiprev2 = phiprev*1
|
182
|
+
phiprev = phi*1
|
183
|
+
|
184
|
+
if i is itmax-1:
|
185
|
+
if print_warnings:
|
186
|
+
print(f'Warning: Maximum number of iterations ({i+1}) performed on mode {int(np.ceil(m/2))}. Storing last value.')
|
187
|
+
print(f'Last three damped natural frequencies: {wprev2:.2f}, {wprev:.2f}, {w:.2f} rad/s')
|
188
|
+
print(f'Corresponding automacs (3,1) and (3,2): {xmacmat(phi, phiprev2):.1f}, {xmacmat(phi, phiprev):.1f}')
|
189
|
+
not_converged.append(int(m/2))
|
190
|
+
|
191
|
+
lambd[m] = lambdai
|
192
|
+
q[:, m] = phi[:,0]
|
193
|
+
|
194
|
+
if print_progress:
|
195
|
+
pp(m+2,2*ndofs, sym='>', postfix=' finished with iterative modal analysis.')
|
196
|
+
|
197
|
+
if print_progress:
|
198
|
+
print(' ')
|
199
|
+
|
200
|
+
if remove_velocity:
|
201
|
+
q = q[:ndofs, :]
|
202
|
+
|
203
|
+
if not keep_full:
|
204
|
+
lambd = lambd[::2]
|
205
|
+
q = q[:, ::2]
|
206
|
+
|
207
|
+
if normalize==True:
|
208
|
+
for mode in range(0, np.shape(q)[1]):
|
209
|
+
q[:, mode] = q[:, mode]/max(abs(q[:, mode]))
|
210
|
+
|
211
|
+
# Sort
|
212
|
+
ix = np.argsort(np.abs(lambd))
|
213
|
+
lambd = lambd[ix]
|
214
|
+
q = q[:,ix]
|
215
|
+
|
216
|
+
return lambd, q, not_converged
|
217
|
+
|
218
|
+
|
219
|
+
def iteig_naive(K, C, M, itmax=None, tol=1e-4):
|
220
|
+
|
221
|
+
if itmax is None:
|
222
|
+
itmax = 15
|
223
|
+
|
224
|
+
if tol is None:
|
225
|
+
tol = 1e-4
|
226
|
+
|
227
|
+
ndofs = K(0).shape[0]
|
228
|
+
lambd = np.zeros([2*ndofs], dtype=complex)
|
229
|
+
q = np.zeros([2*ndofs, 2*ndofs], dtype=complex)
|
230
|
+
|
231
|
+
for m in range(0, 2*ndofs, 2):
|
232
|
+
w = 0.0
|
233
|
+
wprev = np.inf
|
234
|
+
|
235
|
+
for i in range(0, itmax):
|
236
|
+
A = statespace(K(w), C(w), M(w))
|
237
|
+
lambdai, qi = np.linalg.eig(A)
|
238
|
+
ix = np.argsort(np.abs(lambdai))
|
239
|
+
lambdai = lambdai[ix][m]
|
240
|
+
qi = qi[:, ix][:, m]
|
241
|
+
|
242
|
+
w = abs(-np.imag(lambdai))
|
243
|
+
|
244
|
+
if np.abs(w - wprev) <= tol:
|
245
|
+
break
|
246
|
+
|
247
|
+
wprev = w*1
|
248
|
+
|
249
|
+
lambd[m] = lambdai
|
250
|
+
q[:, m] = qi
|
251
|
+
|
252
|
+
return lambd, q
|
253
|
+
|
254
|
+
|
255
|
+
def iteig_freq(K, C, M, omega=None, itmax=15, reference_omega=0, input_functions=True,
|
256
|
+
tol=1e-4, keep_full=False, mac_min=0.98, w_initial=None,
|
257
|
+
normalize=False, print_progress=False, print_warnings=True, divergence_protection=True):
|
258
|
+
|
259
|
+
if not input_functions:
|
260
|
+
K = interp1d(omega, K, kind='quadratic', fill_value='extrapolate')
|
261
|
+
C = interp1d(omega, C, kind='quadratic', fill_value='extrapolate')
|
262
|
+
M = interp1d(omega, M, kind='quadratic', fill_value='extrapolate')
|
263
|
+
|
264
|
+
if divergence_protection:
|
265
|
+
extra_message = '[Divergence protection active]'
|
266
|
+
else:
|
267
|
+
extra_message = ''
|
268
|
+
|
269
|
+
ndofs = K(0).shape[0]
|
270
|
+
|
271
|
+
lambd = np.zeros([2*ndofs], dtype=complex)
|
272
|
+
q = np.zeros([2*ndofs, 2*ndofs], dtype=complex)
|
273
|
+
|
274
|
+
not_converged = []
|
275
|
+
|
276
|
+
if not w_initial:
|
277
|
+
w_initial = np.zeros([ndofs])
|
278
|
+
|
279
|
+
for m in range(0, 2*ndofs):
|
280
|
+
w = w_initial[int(m/2)]
|
281
|
+
|
282
|
+
wprev = np.inf
|
283
|
+
wprev2 = -np.inf
|
284
|
+
|
285
|
+
phi = q[:,0]*0
|
286
|
+
phiprev = 0*phi
|
287
|
+
phiprev2 = 0*phi
|
288
|
+
|
289
|
+
for i in range(0, itmax):
|
290
|
+
A = statespace(K(w), C(w), M(w))
|
291
|
+
|
292
|
+
lambdai, qi = np.linalg.eig(A)
|
293
|
+
sortix = np.argsort(np.imag(lambdai))
|
294
|
+
|
295
|
+
qi = qi[:, sortix]
|
296
|
+
lambdai = lambdai[sortix]
|
297
|
+
|
298
|
+
w = abs(np.imag(lambdai[m]))
|
299
|
+
phi = qi[:, m]
|
300
|
+
|
301
|
+
if np.abs(w - wprev2) <= tol and np.abs(w - wprev) <= tol and mac(phi, phiprev)>=mac_min and mac(phi, phiprev2)>=mac_min: # Converged!
|
302
|
+
break
|
303
|
+
elif np.abs(w - wprev2) <= tol and np.abs(w - wprev) > tol and mac(phi, phiprev)<mac_min and mac(phi, phiprev2)>=mac_min:
|
304
|
+
if divergence_protection:
|
305
|
+
w = (w+wprev)/2
|
306
|
+
if print_warnings:
|
307
|
+
print(f'Oscillatory divergence, mode {np.ceil(m/2):.0f} omega = {w:.2f}, xi = {100*-np.real(lambdai[m])/np.abs(lambdai[m]):.1f} % {extra_message}')
|
308
|
+
|
309
|
+
wprev2 = wprev*1
|
310
|
+
wprev = w*1
|
311
|
+
|
312
|
+
phiprev2 = phiprev*1
|
313
|
+
phiprev = phi*1
|
314
|
+
|
315
|
+
if i is itmax-1:
|
316
|
+
if print_warnings:
|
317
|
+
print('** Maximum number of iterations (%i) performed on mode %i. Storing last value.' % (i+1, np.ceil(m/2)))
|
318
|
+
# print('Last three damped natural frequencies: %f, %f, %f rad/s' % (wprev2, wprev, w))
|
319
|
+
# print('Corresponding automacs (3,1) and (3,2): %f, %f' % (xmacmat(phi, phiprev2), xmacmat(phi, phiprev)))
|
320
|
+
not_converged.append(int(m/2))
|
321
|
+
|
322
|
+
lambd[m] = lambdai[m]
|
323
|
+
q[:, m] = qi[:, m]
|
324
|
+
|
325
|
+
if print_progress:
|
326
|
+
pp(m+2,2*ndofs, sym='>', postfix=' finished with iterative modal analysis.')
|
327
|
+
|
328
|
+
if print_progress:
|
329
|
+
print(' ')
|
330
|
+
|
331
|
+
if not keep_full:
|
332
|
+
q = q[0:ndofs, :]
|
333
|
+
lambd = lambd[0::2]
|
334
|
+
q = q[:, 0::2]
|
335
|
+
else:
|
336
|
+
lambd[1::2] = np.conj(lambd[0::2])
|
337
|
+
q[:, 1::2] = np.conj(q[:,0::2])
|
338
|
+
|
339
|
+
if normalize==True:
|
340
|
+
for mode in range(0, np.shape(q)[1]):
|
341
|
+
q[:, mode] = q[:, mode]/max(abs(q[:, mode]))
|
342
|
+
|
343
|
+
# Sort
|
344
|
+
ix = np.argsort(np.abs(lambd))
|
345
|
+
lambd = lambd[ix]
|
346
|
+
q = q[:,ix]
|
347
|
+
|
348
|
+
return lambd, q, not_converged
|
349
|
+
|
350
|
+
|
351
|
+
|
352
|
+
def xmacmat(phi1, phi2=None, conjugates=True):
|
353
|
+
"""
|
354
|
+
Modal assurance criterion numbers, cross-matrix between two modal transformation matrices (modes stacked as columns).
|
355
|
+
|
356
|
+
Arguments
|
357
|
+
---------------------------
|
358
|
+
phi1 : double
|
359
|
+
reference modes
|
360
|
+
phi2 : double, optional
|
361
|
+
modes to compare with, if not given (i.e., equal default value None), phi1 vs phi1 is assumed
|
362
|
+
conjugates : True, optional
|
363
|
+
check the complex conjugates of all modes as well (should normally be True)
|
364
|
+
|
365
|
+
Returns
|
366
|
+
---------------------------
|
367
|
+
macs : double
|
368
|
+
matrix of MAC numbers
|
369
|
+
"""
|
370
|
+
# If no phi2 is given, assign value of phi1
|
371
|
+
if phi2 is None:
|
372
|
+
phi2 = 1.0*phi1
|
373
|
+
|
374
|
+
if len(np.shape(phi1))==1:
|
375
|
+
phi1 = np.expand_dims(phi1, axis=0).T
|
376
|
+
|
377
|
+
if len(np.shape(phi2))==1:
|
378
|
+
phi2 = np.expand_dims(phi2, axis=0).T
|
379
|
+
|
380
|
+
# norms1 = np.dot(np.expand_dims(np.sum(phi1.T * phi1.T.conj(), axis=1), axis=0), np.expand_dims(np.sum(phi2.T * phi2.T.conj(),axis=1), axis=1))
|
381
|
+
norms = np.real(np.sum(phi1.T * np.conj(phi1.T), axis=1))[:,np.newaxis] @ np.real(np.sum(phi2.T * np.conj(phi2.T),axis=1))[np.newaxis,:]
|
382
|
+
|
383
|
+
|
384
|
+
if conjugates:
|
385
|
+
macs1 = np.divide(abs(np.dot(phi1.T, phi2))**2, norms)
|
386
|
+
macs2 = np.divide(abs(np.dot(phi1.T, phi2.conj()))**2, norms)
|
387
|
+
macs = np.maximum(macs1, macs2)
|
388
|
+
else:
|
389
|
+
macs = np.divide(abs(np.dot(phi1.T, phi2))**2, norms)
|
390
|
+
|
391
|
+
macs = np.real(macs)
|
392
|
+
|
393
|
+
if np.size(macs) == 1:
|
394
|
+
macs = macs[0,0]
|
395
|
+
|
396
|
+
return macs
|
397
|
+
|
398
|
+
def mac(phi1, phi2):
|
399
|
+
|
400
|
+
mac_value = np.real(np.abs(np.dot(phi1.T,phi2))**2 / np.abs((np.dot(phi1.T, phi1) * np.dot(phi2.T, phi2))))
|
401
|
+
return mac_value
|
402
|
+
|
403
|
+
|
404
|
+
|
405
|
+
def mcf(phi):
|
406
|
+
|
407
|
+
# Ensure on matrix format
|
408
|
+
if phi.ndim == 1:
|
409
|
+
phi = phi[:,np.newaxis]
|
410
|
+
|
411
|
+
|
412
|
+
n_modes = np.shape(phi)[1]
|
413
|
+
|
414
|
+
X = np.real(phi)
|
415
|
+
Y = np.imag(phi)
|
416
|
+
|
417
|
+
modal_complexity_factor = [None]*n_modes
|
418
|
+
for mode in range(0,n_modes):
|
419
|
+
modal_complexity_factor[mode] = np.abs(np.dot(X[:,mode], Y[:,mode]))**2 / (np.abs(np.dot(X[:,mode], X[:,mode])) * np.abs(np.dot(Y[:,mode], Y[:,mode])))
|
420
|
+
|
421
|
+
modal_complexity_factor = np.array(modal_complexity_factor)
|
422
|
+
return modal_complexity_factor
|
423
|
+
|
424
|
+
|
425
|
+
def mpc(phi):
|
426
|
+
# Based on the current paper:
|
427
|
+
# Pappa, R. S., Elliott, K. B., & Schenk, A. (1993).
|
428
|
+
# Consistent-mode indicator for the eigensystem realization algorithm.
|
429
|
+
# Journal of Guidance, Control, and Dynamics, 16(5), 852–858.
|
430
|
+
|
431
|
+
# Ensure on matrix format
|
432
|
+
if phi.ndim == 1:
|
433
|
+
phi = phi[:,np.newaxis]
|
434
|
+
|
435
|
+
n_modes = np.shape(phi)[1]
|
436
|
+
mpc_val = [None]*n_modes
|
437
|
+
|
438
|
+
for mode in range(0,n_modes):
|
439
|
+
phin = phi[:, mode]
|
440
|
+
Sxx = np.dot(np.real(phin), np.real(phin))
|
441
|
+
Syy = np.dot(np.imag(phin), np.imag(phin))
|
442
|
+
Sxy = np.dot(np.real(phin), np.imag(phin))
|
443
|
+
|
444
|
+
eta = (Syy-Sxx)/(2*Sxy)
|
445
|
+
|
446
|
+
lambda1 = (Sxx+Syy)/2 + Sxy*np.sqrt(eta**2+1)
|
447
|
+
lambda2 = (Sxx+Syy)/2 - Sxy*np.sqrt(eta**2+1)
|
448
|
+
|
449
|
+
mpc_val[mode] = ((lambda1-lambda2)/(lambda1+lambda2))**2
|
450
|
+
|
451
|
+
mpc_val = np.array(mpc_val)
|
452
|
+
return mpc_val
|
453
|
+
|
454
|
+
|
455
|
+
def scale_phi(phi, scaling):
|
456
|
+
phi_scaled = phi*1
|
457
|
+
for mode in range(phi.shape[1]):
|
458
|
+
phi_scaled[:,mode] = phi[:,mode] * scaling[mode]
|
459
|
+
|
460
|
+
return phi_scaled
|
461
|
+
|
462
|
+
def normalize_phi(phi, include_dofs=[0,1,2,3,4,5,6], n_dofs=6):
|
463
|
+
phi_n = phi*0
|
464
|
+
|
465
|
+
phi_for_scaling = np.vstack([phi[dof::n_dofs, :] for dof in include_dofs])
|
466
|
+
mode_scaling = np.max(np.abs(phi_for_scaling), axis=0)
|
467
|
+
ix_max = np.argmax(np.abs(phi_for_scaling), axis=0)
|
468
|
+
signs = np.sign(phi_for_scaling[ix_max, range(0, len(ix_max))])
|
469
|
+
signs[signs==0] = 1
|
470
|
+
mode_scaling[mode_scaling==0] = 1
|
471
|
+
|
472
|
+
phi_n = phi/np.tile(mode_scaling[np.newaxis,:]/signs[np.newaxis,:], [phi.shape[0], 1])
|
473
|
+
|
474
|
+
return phi_n, mode_scaling
|
475
|
+
|
476
|
+
|
477
|
+
def restructure_as_ref(phi_ref, phi, min_mac=0.0, ensure_unique=True,
|
478
|
+
return_all=True, accept_conjugates=True):
|
479
|
+
"""
|
480
|
+
Restructure modes based on reference modal transformation matrix phi_ref.
|
481
|
+
|
482
|
+
Arguments
|
483
|
+
---------------------------
|
484
|
+
phi_ref : double
|
485
|
+
reference modes
|
486
|
+
phi : double
|
487
|
+
modes to compare with
|
488
|
+
min_mac : double, 0.0
|
489
|
+
minimum MAC value for mode to be placed in index arrays
|
490
|
+
|
491
|
+
Returns
|
492
|
+
---------------------------
|
493
|
+
ixs : integers
|
494
|
+
array of indices, describing which elements to copy to reference
|
495
|
+
numbering (indices indicate position in original numbering,
|
496
|
+
as in phi - position relates to reference numbering)
|
497
|
+
ref_ixs : integers
|
498
|
+
array of indices, describing which elements to assign values to
|
499
|
+
(using reference numbering) - equal to 0:n_modes if min_mac=0.0 (all are populated).
|
500
|
+
"""
|
501
|
+
xmacs = xmacmat(phi_ref, phi2=phi, conjugates=accept_conjugates) # n_modes_ref-by-n_modes
|
502
|
+
ixs = np.argmax(xmacs, axis=1) # indices of what mode in phi_ref each mode in phi2 are closest to, i.e, phi_rs = phi[:, ix]
|
503
|
+
ref_ixs = np.arange(len(ixs))
|
504
|
+
xmacs_out = xmacs*1
|
505
|
+
|
506
|
+
if ensure_unique:
|
507
|
+
for row in range(xmacs.shape[0]):
|
508
|
+
xmac_row = xmacs[row, :]
|
509
|
+
xmacs[row, xmac_row<np.max(xmac_row)] = 0
|
510
|
+
|
511
|
+
for col in range(xmacs.shape[1]):
|
512
|
+
xmac_col = xmacs[:, col]
|
513
|
+
xmacs[xmac_col<np.max(xmac_col), col] = 0
|
514
|
+
|
515
|
+
ok = np.diag(xmacs[:, ixs])>min_mac
|
516
|
+
|
517
|
+
ixs = ixs[ok]
|
518
|
+
ref_ixs = ref_ixs[ok]
|
519
|
+
rejected_ixs = np.where(~ok)[0]
|
520
|
+
xmacs_out = xmacs_out[ref_ixs, ixs]
|
521
|
+
|
522
|
+
if return_all:
|
523
|
+
return ixs, ref_ixs, rejected_ixs, xmacs_out
|
524
|
+
else:
|
525
|
+
return ixs
|
526
|
+
|
527
|
+
|
528
|
+
def robust_phi_restructure(phi_ref, phi, accept_conjugates=False):
|
529
|
+
"""
|
530
|
+
Restructure modes based on reference modal transformation matrix phi_ref.
|
531
|
+
|
532
|
+
Arguments
|
533
|
+
---------------------------
|
534
|
+
phi_ref : double
|
535
|
+
reference modes
|
536
|
+
phi : double
|
537
|
+
modes to compare with
|
538
|
+
|
539
|
+
Returns
|
540
|
+
---------------------------
|
541
|
+
ixs : integers
|
542
|
+
array of indices, describing which elements to copy to reference numbering (indices indicate position in original numbering, as in phi - position relates to reference numbering)
|
543
|
+
discarded_ixs : integers
|
544
|
+
rest of modes (if not the best match to any mode in reference mode)
|
545
|
+
"""
|
546
|
+
|
547
|
+
xmacs = xmacmat(phi_ref, phi2=phi, conjugates=accept_conjugates) # n_modes_ref-by-n_modes
|
548
|
+
ixs = np.argmax(xmacs, axis=0)
|
549
|
+
|
550
|
+
# Make unique based on selected columns in ixs
|
551
|
+
for ix in np.unique(ixs):
|
552
|
+
all_this_ix_ix = np.where(ixs==ix)[0]
|
553
|
+
ix_max = np.argmax(xmacs[:, ixs==ix], axis=0)
|
554
|
+
|
555
|
+
n = len(all_this_ix_ix)
|
556
|
+
discards = np.array([i for i in range(n) if i != ix_max])
|
557
|
+
|
558
|
+
if len(discards)>=0:
|
559
|
+
ixs[all_this_ix_ix[discards]] = np.nan
|
560
|
+
|
561
|
+
all_ixs = np.arange(0, phi_ref.shape[1])
|
562
|
+
discarded_ixs = np.sort([i for i in all_ixs if i not in ixs])
|
563
|
+
|
564
|
+
return ixs, discarded_ixs
|
565
|
+
|
566
|
+
|
567
|
+
|
568
|
+
def freq_eig(K, C, M, omega, omega_ref=None, phi_ref=None, min_mac=0.0, input_functions=True, keep_full=False):
|
569
|
+
if not input_functions:
|
570
|
+
K = interp1d(omega, K, kind='quadratic', fill_value='extrapolate')
|
571
|
+
C = interp1d(omega, C, kind='quadratic', fill_value='extrapolate')
|
572
|
+
M = interp1d(omega, M, kind='quadratic', fill_value='extrapolate')
|
573
|
+
|
574
|
+
if phi_ref is None:
|
575
|
+
if omega_ref is None:
|
576
|
+
prev_compare = True
|
577
|
+
omega_ref = omega[0]
|
578
|
+
else:
|
579
|
+
prev_compare = False
|
580
|
+
|
581
|
+
A_ref = statespace(K(omega_ref), C(omega_ref), M(omega_ref))
|
582
|
+
lambda_ref, phi_ref = np.linalg.eig(A_ref)
|
583
|
+
sortix = np.argsort(abs(lambda_ref))
|
584
|
+
phi_ref = phi_ref[:, sortix]
|
585
|
+
else:
|
586
|
+
prev_compare = False
|
587
|
+
|
588
|
+
phi = [None]*len(omega)
|
589
|
+
lambd = [None]*len(omega)
|
590
|
+
|
591
|
+
for k, omega_k in enumerate(omega):
|
592
|
+
phi[k] = np.nan*phi_ref
|
593
|
+
lambd[k] = np.nan*np.ones(phi_ref.shape[1]).astype('complex')
|
594
|
+
|
595
|
+
A = statespace(K(omega_k), C(omega_k), M(omega_k))
|
596
|
+
lambd_k, phi_k = np.linalg.eig(A)
|
597
|
+
mode_ix, ref_ix, __, __ = restructure_as_ref(phi_ref, phi_k, min_mac=min_mac, ensure_unique=False)
|
598
|
+
lambd[k][ref_ix] = lambd_k[mode_ix]
|
599
|
+
phi[k][:, ref_ix] = phi_k[:, mode_ix]
|
600
|
+
|
601
|
+
if prev_compare:
|
602
|
+
phi_ref = phi[k]
|
603
|
+
|
604
|
+
if not keep_full:
|
605
|
+
lambd = [lambd_k[::2] for lambd_k in lambd]
|
606
|
+
phi = [phi_k[::2,::2] for phi_k in phi]
|
607
|
+
|
608
|
+
return lambd, phi
|