wawi 0.0.1__py3-none-any.whl → 0.0.5__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/fe.py +134 -0
- wawi/general.py +468 -0
- wawi/identification.py +66 -0
- wawi/io.py +719 -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 +1109 -0
- wawi/wind_code.py +14 -0
- {wawi-0.0.1.dist-info → wawi-0.0.5.dist-info}/METADATA +7 -6
- wawi-0.0.5.dist-info/RECORD +21 -0
- wawi-0.0.1.dist-info/RECORD +0 -6
- {wawi-0.0.1.dist-info → wawi-0.0.5.dist-info}/LICENSE +0 -0
- {wawi-0.0.1.dist-info → wawi-0.0.5.dist-info}/WHEEL +0 -0
- {wawi-0.0.1.dist-info → wawi-0.0.5.dist-info}/top_level.txt +0 -0
wawi/__init__.py
CHANGED
wawi/fe.py
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import numpy as np
|
3
|
+
from .general import blkdiag, transform_unit
|
4
|
+
|
5
|
+
'''
|
6
|
+
FE-related tools.
|
7
|
+
'''
|
8
|
+
|
9
|
+
def intpoints_from_elements(nodes, elements, sort_axis=0):
|
10
|
+
'''
|
11
|
+
Get integration points from elements.
|
12
|
+
|
13
|
+
Parameters
|
14
|
+
------------
|
15
|
+
nodes :
|
16
|
+
elements :
|
17
|
+
sort_axis : 0, optional
|
18
|
+
'''
|
19
|
+
nodeix = nodeix_from_elements(elements, nodes).astype('int')
|
20
|
+
|
21
|
+
intpoints = (nodes[nodeix[:,0], 1:4]+nodes[nodeix[:,1], 1:4])/2
|
22
|
+
sortix = np.argsort(intpoints[:,sort_axis])
|
23
|
+
intpoints = intpoints[sortix, :]
|
24
|
+
|
25
|
+
x = intpoints[:, 0]
|
26
|
+
y = intpoints[:, 1]
|
27
|
+
z = intpoints[:, 2]
|
28
|
+
|
29
|
+
return x, y, z
|
30
|
+
|
31
|
+
|
32
|
+
def nodeix_from_elements(element_matrix, node_matrix, return_as_flat=False):
|
33
|
+
nodeix = [None]*len(element_matrix[:,0])
|
34
|
+
for element_ix, __ in enumerate(element_matrix[:,0]):
|
35
|
+
node1 = element_matrix[element_ix, 1]
|
36
|
+
node2 = element_matrix[element_ix, 2]
|
37
|
+
|
38
|
+
nodeix1 = np.where(node_matrix[:,0]==node1)[0][0]
|
39
|
+
nodeix2 = np.where(node_matrix[:,0]==node2)[0][0]
|
40
|
+
nodeix[element_ix] = [nodeix1, nodeix2]
|
41
|
+
|
42
|
+
nodeix = np.array(nodeix)
|
43
|
+
|
44
|
+
if return_as_flat:
|
45
|
+
nodeix = np.unique(nodeix.flatten())
|
46
|
+
|
47
|
+
return nodeix
|
48
|
+
|
49
|
+
|
50
|
+
def create_node_dict(element_dict, node_labels, x_nodes, y_nodes, z_nodes):
|
51
|
+
node_dict = dict()
|
52
|
+
node_matrix = np.vstack([node_labels, x_nodes, y_nodes, z_nodes]).T
|
53
|
+
|
54
|
+
for key in element_dict.keys():
|
55
|
+
node_ix = nodeix_from_elements(element_dict[key], node_matrix, return_as_flat=True)
|
56
|
+
node_dict[key] = node_matrix[node_ix, :]
|
57
|
+
|
58
|
+
return node_dict
|
59
|
+
|
60
|
+
|
61
|
+
def elements_with_node(element_matrix, node_label):
|
62
|
+
element_ixs1 = np.array(np.where(element_matrix[:,1]==node_label)).flatten()
|
63
|
+
element_ixs2 = np.array(np.where(element_matrix[:,2]==node_label)).flatten()
|
64
|
+
|
65
|
+
element_ixs = np.hstack([element_ixs1, element_ixs2])
|
66
|
+
element_labels = element_matrix[element_ixs, 0]
|
67
|
+
|
68
|
+
local_node_ix = np.zeros(np.shape(element_ixs))
|
69
|
+
local_node_ix[np.arange(0,len(element_ixs1))] = 0
|
70
|
+
local_node_ix[np.arange(len(element_ixs1), len(element_ixs1) + len(element_ixs2))] = 1
|
71
|
+
|
72
|
+
return element_labels, element_ixs, local_node_ix
|
73
|
+
|
74
|
+
|
75
|
+
def nodes_to_beam_element_matrix(node_labels, first_element_label=1):
|
76
|
+
n_nodes = len(node_labels)
|
77
|
+
n_elements = n_nodes-1
|
78
|
+
|
79
|
+
element_matrix = np.empty([n_elements, 3])
|
80
|
+
element_matrix[:, 0] = np.arange(first_element_label,first_element_label+n_elements)
|
81
|
+
element_matrix[:, 1] = node_labels[0:-1]
|
82
|
+
element_matrix[:, 2] = node_labels[1:]
|
83
|
+
|
84
|
+
return element_matrix
|
85
|
+
|
86
|
+
|
87
|
+
def node_ix_to_dof_ix(node_ix, n_dofs=6):
|
88
|
+
start = node_ix*n_dofs
|
89
|
+
stop = node_ix*n_dofs+n_dofs
|
90
|
+
dof_ix = []
|
91
|
+
for (i,j) in zip(start,stop):
|
92
|
+
dof_ix.append(np.arange(i,j))
|
93
|
+
|
94
|
+
dof_ix = np.array(dof_ix).flatten()
|
95
|
+
|
96
|
+
return dof_ix
|
97
|
+
|
98
|
+
|
99
|
+
def dof_sel(arr, dof_sel, n_dofs=6, axis=0):
|
100
|
+
N = np.shape(arr)[axis]
|
101
|
+
all_ix = [range(dof_sel_i, N, n_dofs) for dof_sel_i in dof_sel]
|
102
|
+
sel_ix = np.array(all_ix).T.flatten()
|
103
|
+
|
104
|
+
# Select the elements
|
105
|
+
arr_sel = np.take(arr, sel_ix, axis=axis)
|
106
|
+
|
107
|
+
return arr_sel
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
def elements_from_common_nodes(element_matrix, selected_nodes):
|
112
|
+
sel_ix = np.where(np.logical_and([selnode in element_matrix[:,1] for selnode in selected_nodes], [selnode in element_matrix[:,2] for selnode in selected_nodes]))[0]
|
113
|
+
selected_element_matrix = element_matrix[sel_ix, :]
|
114
|
+
|
115
|
+
return selected_element_matrix, sel_ix
|
116
|
+
|
117
|
+
|
118
|
+
def transform_elements(node_matrix, element_matrix, e2p, repeats=1):
|
119
|
+
|
120
|
+
n_elements = np.shape(element_matrix)[0]
|
121
|
+
T_g2el = [None]*n_elements
|
122
|
+
|
123
|
+
for el in range(0, n_elements):
|
124
|
+
n1_ix = np.where(node_matrix[:,0]==element_matrix[el, 1])
|
125
|
+
n2_ix = np.where(node_matrix[:,0]==element_matrix[el, 2])
|
126
|
+
|
127
|
+
X1 = node_matrix[n1_ix, 1:]
|
128
|
+
X2 = node_matrix[n2_ix, 1:]
|
129
|
+
dx = X2-X1
|
130
|
+
e1 = dx/np.linalg.norm(dx)
|
131
|
+
|
132
|
+
T_g2el[el] = blkdiag(transform_unit(e1, e2p), repeats) # Transform from global to local coordinates VLocal = T*VGlobal
|
133
|
+
|
134
|
+
return T_g2el
|
wawi/general.py
ADDED
@@ -0,0 +1,468 @@
|
|
1
|
+
import csv
|
2
|
+
from itertools import chain, count
|
3
|
+
import numpy as np
|
4
|
+
from inspect import isfunction
|
5
|
+
|
6
|
+
def block_truncate(M, p, n_dofs=6):
|
7
|
+
Mtr = M*1
|
8
|
+
if np.ndim(M)==2:
|
9
|
+
is_2d = True
|
10
|
+
Mtr = Mtr[:,:,np.newaxis]
|
11
|
+
else:
|
12
|
+
is_2d = False
|
13
|
+
|
14
|
+
N = int(Mtr.shape[0]/n_dofs)
|
15
|
+
if N*n_dofs != Mtr.shape[0]:
|
16
|
+
raise ValueError('Incompatible integers defined')
|
17
|
+
|
18
|
+
mask = Mtr[:,:,0]*False
|
19
|
+
for i in range(N-p+1):
|
20
|
+
mask[i*6:(i+p)*6, i*6:(i+p)*6] = True
|
21
|
+
|
22
|
+
for k in range(Mtr.shape[2]):
|
23
|
+
Mtr[:,:,k] = Mtr[:,:,k]*mask
|
24
|
+
|
25
|
+
if is_2d:
|
26
|
+
Mtr = Mtr[:,:,0]
|
27
|
+
|
28
|
+
return Mtr
|
29
|
+
|
30
|
+
def eval_fun_or_scalar(par, x, y, x0=0, y0=0):
|
31
|
+
if isfunction(par):
|
32
|
+
if 'x' in par.__code__.co_varnames and 'y' in par.__code__.co_varnames:
|
33
|
+
return par(x - x0, y - y0)
|
34
|
+
elif 'x' in par.__code__.co_varnames:
|
35
|
+
return par(x - x0)
|
36
|
+
elif 'y' in par.__code__.co_varnames:
|
37
|
+
return par(y - y0)
|
38
|
+
else:
|
39
|
+
return par
|
40
|
+
|
41
|
+
|
42
|
+
#%%
|
43
|
+
def zero_pad_upsample(B, omega, omega_max):
|
44
|
+
dw = omega[1]-omega[0]
|
45
|
+
omega_max0 = np.max(omega)
|
46
|
+
n_add = int(np.floor((omega_max-omega_max0)/dw))
|
47
|
+
Bi = np.hstack([B, np.zeros(n_add)])
|
48
|
+
return Bi
|
49
|
+
|
50
|
+
def get_omega_upsampled(omega, omega_max):
|
51
|
+
dw = omega[1]-omega[0]
|
52
|
+
omega_max0 = np.max(omega)
|
53
|
+
omega_add = np.arange(omega_max0+dw, omega_max, dw)
|
54
|
+
omegai = np.hstack([omega, omega_add])
|
55
|
+
return omegai
|
56
|
+
|
57
|
+
#%% Misc
|
58
|
+
def equal_energy_omega(S, omega_min, omega_max, n, dmax=np.inf, domega_ref=1e-4):
|
59
|
+
from scipy.integrate import cumtrapz
|
60
|
+
|
61
|
+
omega_ref = np.arange(omega_min, omega_max, domega_ref)
|
62
|
+
E = np.trapz(S(omega_ref), x=omega_ref)
|
63
|
+
dE = E/n
|
64
|
+
E_cum = cumtrapz(S(omega_ref), x=omega_ref)
|
65
|
+
relE = (E_cum/dE-0.5)
|
66
|
+
indices = np.abs(np.round(relE))
|
67
|
+
__, jump_ix = np.unique(indices, return_index=True)
|
68
|
+
|
69
|
+
omegas = np.hstack([omega_min,(omega_ref[jump_ix[:-1]] + omega_ref[jump_ix[1:]])/2, omega_max])
|
70
|
+
add_omegas = []
|
71
|
+
for range_ix in range(len(omegas)-1):
|
72
|
+
om0, om1 = omegas[range_ix:range_ix+2]
|
73
|
+
domega = om1 - om0
|
74
|
+
|
75
|
+
n_sub = domega/dmax
|
76
|
+
if n_sub>1.0:
|
77
|
+
add_omegas.append(np.linspace(om0, om1, int(np.ceil(n_sub)))[1:-1])
|
78
|
+
|
79
|
+
if len(add_omegas)>0:
|
80
|
+
omegas = np.unique(np.hstack([omegas, np.hstack(add_omegas)]))
|
81
|
+
|
82
|
+
return omegas
|
83
|
+
|
84
|
+
|
85
|
+
#%% Matrix manipulation / generation
|
86
|
+
def blkdiag(mat, n):
|
87
|
+
return np.kron(np.eye(n), mat)
|
88
|
+
|
89
|
+
def fun_sum(*functions):
|
90
|
+
def fun(x):
|
91
|
+
return sum([f(x) for f in functions])
|
92
|
+
|
93
|
+
return fun
|
94
|
+
|
95
|
+
def fun_scale(function, scaling):
|
96
|
+
def fun(x):
|
97
|
+
return function(x)*scaling
|
98
|
+
|
99
|
+
return fun
|
100
|
+
|
101
|
+
def fun_const_sum(function, constant):
|
102
|
+
def fun(x):
|
103
|
+
return function(x) + constant
|
104
|
+
return fun
|
105
|
+
|
106
|
+
def eval_3d_fun(fun, z):
|
107
|
+
return np.stack([fun(z_k) for z_k in z], axis=2)
|
108
|
+
|
109
|
+
def correct_matrix_size(mat, n_freqs, n_dofs):
|
110
|
+
"""
|
111
|
+
Extend matrix to specified dimensions (frequencies and DOFs).
|
112
|
+
|
113
|
+
Parameters
|
114
|
+
---------------------------
|
115
|
+
mat : double
|
116
|
+
matrix to be checked and possibly modified
|
117
|
+
n_freqs : int
|
118
|
+
number of frequencies (depth of final matrix)
|
119
|
+
n_dofs : int
|
120
|
+
number of degrees of freedom (height and width of final matrix)
|
121
|
+
|
122
|
+
Returns
|
123
|
+
---------------------------
|
124
|
+
mat_extended : double
|
125
|
+
corrected/verified matrix
|
126
|
+
"""
|
127
|
+
|
128
|
+
mat_shape = np.array(np.shape(mat))
|
129
|
+
|
130
|
+
for ix in range(0, 3-len(mat_shape)):
|
131
|
+
mat_shape = np.append(mat_shape, 1)
|
132
|
+
|
133
|
+
if mat_shape[0] == 1 and mat_shape[2] == 1: #scalar
|
134
|
+
mat_extended = np.ones([n_dofs, n_dofs, n_freqs])*mat
|
135
|
+
elif mat_shape[2] == 1 and mat_shape[0] == n_dofs: # If constant matrix (nDofs-by-nDofs)
|
136
|
+
mat_extended = np.tile(mat[:,:,np.newaxis], [1, 1, n_freqs])
|
137
|
+
elif mat_shape[2] == n_freqs and mat_shape[0] == n_dofs: # If correct
|
138
|
+
mat_extended = mat
|
139
|
+
else:
|
140
|
+
raise ValueError('Input dimensions are not valid!')
|
141
|
+
|
142
|
+
return mat_extended
|
143
|
+
|
144
|
+
def wrap_to_pi(angle):
|
145
|
+
return (angle + np.pi) % (2*np.pi) - np.pi
|
146
|
+
|
147
|
+
def wrap_to_circular(x, rng=[-np.pi, np.pi]):
|
148
|
+
x0 = np.mean(rng)
|
149
|
+
dx_sym = (np.max(rng) - np.min(rng))/2
|
150
|
+
x_centered = (x + dx_sym) % (2*dx_sym) - dx_sym
|
151
|
+
|
152
|
+
return x_centered+x0
|
153
|
+
|
154
|
+
def merge_tr_phi(phi_trans, phi_rot, thread_stack=True):
|
155
|
+
"""
|
156
|
+
Merge matrices of phi for translational and rotational DOFs.
|
157
|
+
|
158
|
+
Args:
|
159
|
+
phi_trans: phi matrix with only translational DOFs
|
160
|
+
phi_rot: phi matrix with only rotational DOFs
|
161
|
+
Optional keywords:
|
162
|
+
thread_stack: specify if the matrices should be thread in or not (if True, 3 translational DOFs and 3 rotational DOFs ...) (default = True)
|
163
|
+
Returns:
|
164
|
+
phi_combined: phi matrix with all DOFs
|
165
|
+
"""
|
166
|
+
|
167
|
+
Ndofs = phi_trans.shape[0]*2
|
168
|
+
|
169
|
+
if thread_stack is True:
|
170
|
+
trans_dofs = np.array([np.array([0, 1, 2]) + n*6 for n in range(0, Ndofs/6)]).reshape(-1)
|
171
|
+
rot_dofs = trans_dofs+3
|
172
|
+
else:
|
173
|
+
trans_dofs = np.array(range(0, Ndofs/2)).reshape(-1)
|
174
|
+
rot_dofs = trans_dofs + Ndofs/2
|
175
|
+
|
176
|
+
phi_combined = np.empty([phi_trans.shape[0]*2, phi_trans.shape[1]])
|
177
|
+
phi_combined[trans_dofs, :] = phi_trans
|
178
|
+
phi_combined[rot_dofs, :] = phi_rot
|
179
|
+
|
180
|
+
return phi_combined
|
181
|
+
|
182
|
+
|
183
|
+
def mat3d_sel(mat, k):
|
184
|
+
|
185
|
+
if len(np.shape(mat)) is 3:
|
186
|
+
matsel = mat[:, :, k]
|
187
|
+
else:
|
188
|
+
matsel = mat
|
189
|
+
|
190
|
+
return matsel
|
191
|
+
|
192
|
+
|
193
|
+
|
194
|
+
def interp1z(z,mat,znew):
|
195
|
+
"""
|
196
|
+
Interpolate 3D matrix along z-component. !! Deprecated - use numpy functions directly instead !!
|
197
|
+
|
198
|
+
Args:
|
199
|
+
z: z axis (Numpy array)
|
200
|
+
mat: 3D matrix (Numpy array)
|
201
|
+
znew: new z axis (Numpy array)
|
202
|
+
Returns:
|
203
|
+
matnew: interpolated 3D matrix (Numpy array)
|
204
|
+
|
205
|
+
"""
|
206
|
+
|
207
|
+
matnew = np.zeros([1,len(mat[0]),len(mat[0][0])])
|
208
|
+
for dof1 in range(0,len(mat[0])):
|
209
|
+
for dof2 in range(0,len(mat[0][0])):
|
210
|
+
matnew[:,dof1,dof2]=np.interp(znew,z,mat[:,dof1,dof2])
|
211
|
+
return matnew
|
212
|
+
|
213
|
+
|
214
|
+
def interpolate_3d(z, mat, zi):
|
215
|
+
"""
|
216
|
+
Interpolates 3D NumPy array.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
mats: list of 3D NumPy matrices (n_x-by-n_y-by-n_z)
|
220
|
+
z: original z axis
|
221
|
+
zi: interpolated z axis
|
222
|
+
|
223
|
+
Returns:
|
224
|
+
mati: interpolated matrix
|
225
|
+
|
226
|
+
"""
|
227
|
+
mat_shape = np.shape(mat)
|
228
|
+
mati = np.zeros([mat_shape[0], mat_shape[1], len(zi)])
|
229
|
+
|
230
|
+
for dof1 in range(0, mat_shape[0]):
|
231
|
+
for dof2 in range(0, mat_shape[1]):
|
232
|
+
mati[dof1, dof2, :] = np.interp(zi, z, mat[dof1, dof2, :])
|
233
|
+
|
234
|
+
return mati
|
235
|
+
|
236
|
+
|
237
|
+
def fast_interpolation(x, y, new_x):
|
238
|
+
from scipy.interpolate import interp1d
|
239
|
+
from scipy.interpolate._fitpack import _bspleval
|
240
|
+
f = interp1d(x, y, axis=-1, kind=3)
|
241
|
+
xj,cvals,k = f._spline
|
242
|
+
result = np.empty_like(new_x)
|
243
|
+
for (i, j), value in np.ndenumerate(new_x):
|
244
|
+
result[i, j] = _bspleval(value, x, cvals[:, i, j], k, 0)
|
245
|
+
return result
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
#%% Transformation matrix generation / manipulation
|
250
|
+
def transformation_matrix(angles, axis):
|
251
|
+
T = np.empty([len(angles),6,6])
|
252
|
+
for idx,angle in enumerate(angles):
|
253
|
+
c = np.cos(angle)
|
254
|
+
s = np.sin(angle)
|
255
|
+
|
256
|
+
if axis==0:
|
257
|
+
T0 = np.matrix([[1,0,0],[0,c,-s],[0,s,c]])
|
258
|
+
elif axis==1:
|
259
|
+
T0 = np.matrix([[c,0,s],[0,1,0],[-s,0,c]])
|
260
|
+
elif axis==2:
|
261
|
+
T0 = np.matrix([[c,-s,0],[s,c,0],[0,0,1]])
|
262
|
+
T[idx]=np.bmat([[T0,np.zeros([3,3])],[np.zeros([3,3]),T0]])
|
263
|
+
|
264
|
+
return T
|
265
|
+
|
266
|
+
|
267
|
+
def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
|
268
|
+
"""
|
269
|
+
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
|
+
|
276
|
+
Returns:
|
277
|
+
T: transformation matrix in NumPy format
|
278
|
+
|
279
|
+
"""
|
280
|
+
|
281
|
+
axis = np.asarray(rotaxis)
|
282
|
+
axis = rotaxis/np.sqrt(np.dot(rotaxis, rotaxis)) # Normalize
|
283
|
+
a = np.cos(theta/2.0)
|
284
|
+
b, c, d = axis*np.sin(theta/2.0)
|
285
|
+
a2, b2, c2, d2 = a*a, b*b, c*c, d*d
|
286
|
+
bc, ad, ac, ab, bd, cd = b*c, a*d, a*c, a*b, b*d, c*d
|
287
|
+
T = np.array([[a2+b2-c2-d2, 2*(bc-ad), 2*(bd+ac)],
|
288
|
+
[2*(bc+ad), a2+c2-b2-d2, 2*(cd-ab)],
|
289
|
+
[2*(bd-ac), 2*(cd+ab), a2+d2-b2-c2]])
|
290
|
+
|
291
|
+
if style=='row':
|
292
|
+
T = T.T
|
293
|
+
|
294
|
+
return T
|
295
|
+
|
296
|
+
|
297
|
+
def multi_rodrot(angles, rotaxis=[0,0,1], style='row'):
|
298
|
+
T = [None]*len(angles)
|
299
|
+
for ix, angle in enumerate(angles):
|
300
|
+
T[ix] = blkdiag(rodrot(angle, rotaxis=rotaxis, style=style), 2)
|
301
|
+
|
302
|
+
return T
|
303
|
+
|
304
|
+
|
305
|
+
def stack_T(T_multi):
|
306
|
+
n_dofs = np.shape(T_multi[0])[0]
|
307
|
+
N = len(T_multi)
|
308
|
+
|
309
|
+
T = np.zeros([n_dofs*N, n_dofs*N])
|
310
|
+
|
311
|
+
for n, Tn in enumerate(T_multi):
|
312
|
+
T[n_dofs*n:n_dofs*n+n_dofs, n_dofs*n:n_dofs*n+n_dofs] = Tn
|
313
|
+
|
314
|
+
return T
|
315
|
+
|
316
|
+
|
317
|
+
def transform_unit(e1, e2p):
|
318
|
+
e1 = np.array(e1).flatten()
|
319
|
+
e2p = np.array(e2p).flatten()
|
320
|
+
|
321
|
+
e3 = np.cross(e1, e2p) # Direction of the third unit vector
|
322
|
+
e2 = np.cross(e3, e1) # Direction of the second unit vector
|
323
|
+
|
324
|
+
e1 = e1/np.linalg.norm(e1) # Normalize the direction vectors to become unit vectors
|
325
|
+
e2 = e2/np.linalg.norm(e2)
|
326
|
+
e3 = np.cross(e1,e2)
|
327
|
+
|
328
|
+
T = np.vstack([e1,e2,e3])
|
329
|
+
|
330
|
+
return T
|
331
|
+
|
332
|
+
|
333
|
+
def transform_3dmat(mat, T):
|
334
|
+
M, N = np.shape(T)[0:2]
|
335
|
+
[n_dofs, __, n_freqs] = np.shape(mat)
|
336
|
+
|
337
|
+
if n_dofs != M:
|
338
|
+
raise ValueError('The dimensions of T and mat must match.')
|
339
|
+
|
340
|
+
mat_transformed = np.zeros([N, N, n_freqs])
|
341
|
+
|
342
|
+
if np.iscomplexobj(T) or np.iscomplexobj(mat):
|
343
|
+
mat_transformed = mat_transformed.astype('complex')
|
344
|
+
|
345
|
+
for k in range(0, n_freqs):
|
346
|
+
mat_transformed[:, :, k] = np.dot(np.dot(T.conj().T, mat[:, :, k]), T)
|
347
|
+
|
348
|
+
return mat_transformed
|
349
|
+
|
350
|
+
#%% Other
|
351
|
+
def assess_diagonality_fro(M):
|
352
|
+
# Diagonality Measures of Hermitian Positive-Definite Matrices with Application to the Approximate Joint Diagonalization Problem
|
353
|
+
if M.ndim != 2:
|
354
|
+
raise ValueError('Input must be 2d array.')
|
355
|
+
if np.shape(M)[0] != np.shape(M)[1]:
|
356
|
+
raise ValueError('Matrix must be square.')
|
357
|
+
|
358
|
+
N = np.shape(M)[0]
|
359
|
+
M_m05 = np.diag(np.diag(M)**-0.5)
|
360
|
+
M_hat = np.dot(np.dot(M_m05, M), M_m05)
|
361
|
+
M_o = M_hat-np.eye(N)
|
362
|
+
|
363
|
+
diagonality = 0.5*np.linalg.norm(M_o, 'fro')**2
|
364
|
+
|
365
|
+
return diagonality
|
366
|
+
|
367
|
+
|
368
|
+
def assess_diagonality(M):
|
369
|
+
# Diagonality Measures of Hermitian Positive-Definite Matrices with Application to the Approximate Joint Diagonalization Problem
|
370
|
+
if M.ndim != 2:
|
371
|
+
raise ValueError('Input must be 2d array.')
|
372
|
+
if np.shape(M)[0] != np.shape(M)[1]:
|
373
|
+
raise ValueError('Matrix must be square.')
|
374
|
+
|
375
|
+
N = np.shape(M)[0]
|
376
|
+
Mdiag = np.diag(np.diag(M))
|
377
|
+
Moffdiag = M-Mdiag
|
378
|
+
|
379
|
+
diagonality = np.linalg.norm(Mdiag)/np.linalg.norm(M)
|
380
|
+
|
381
|
+
return diagonality
|
382
|
+
|
383
|
+
|
384
|
+
def modify_stiffness(stiffness, submerged_vol, water_dens, g, z_cog, z_cog_mod):
|
385
|
+
stiffness_mod = stiffness
|
386
|
+
stiffness_mod[3, 3] = stiffness[3, 3] + submerged_vol * water_dens * g * (z_cog - z_cog_mod)
|
387
|
+
stiffness_mod[4, 4] = stiffness[4, 4]+ submerged_vol * water_dens * g * (z_cog - z_cog_mod)
|
388
|
+
|
389
|
+
return stiffness_mod
|
390
|
+
|
391
|
+
|
392
|
+
def maxreal(phi, preserve_conjugates=False):
|
393
|
+
angles = np.expand_dims(np.arange(0,np.pi/2, 0.01), axis=0)
|
394
|
+
|
395
|
+
if phi.ndim==1:
|
396
|
+
phi = np.array([phi]).T
|
397
|
+
|
398
|
+
phi_max_real = np.zeros(np.shape(phi)).astype('complex')
|
399
|
+
|
400
|
+
for mode in range(np.shape(phi)[1]):
|
401
|
+
rot_mode = np.dot(np.expand_dims(phi[:, mode], axis=1), np.exp(angles*1j))
|
402
|
+
max_angle_ix = np.argmax(np.sum(np.real(rot_mode)**2, axis=0), axis=0)
|
403
|
+
|
404
|
+
phi_max_real[:, mode] = phi[:, mode] * np.exp(angles[0, max_angle_ix]*1j)*np.sign(sum(np.real(phi[:, mode])))
|
405
|
+
|
406
|
+
return phi_max_real
|
407
|
+
|
408
|
+
|
409
|
+
def create_circular_x(x, tol=1e-5, rng=np.array([-np.pi, np.pi])):
|
410
|
+
xrng = np.max(x)-np.min(x)
|
411
|
+
|
412
|
+
wrapped_x = np.sort(wrap_to_circular(x, rng))
|
413
|
+
wrapped_x_mod = wrap_to_circular(wrapped_x, rng + np.mean(rng))
|
414
|
+
dx = np.abs(wrapped_x_mod[-1]-wrapped_x_mod[0])
|
415
|
+
|
416
|
+
x_ang = wrap_to_circular(x, rng)
|
417
|
+
x_ang, sort_ix = uniquetol(x_ang, 1e-10)
|
418
|
+
|
419
|
+
x_ang = np.hstack([x_ang[0]-dx, x_ang, x_ang[-1]+dx])
|
420
|
+
sort_ix = np.hstack([sort_ix[-1], sort_ix, sort_ix[0]])
|
421
|
+
|
422
|
+
return x_ang, sort_ix
|
423
|
+
|
424
|
+
|
425
|
+
def interp1d_angular(x, mat, rng=[-np.pi, np.pi], axis=-1, **kwargs):
|
426
|
+
from scipy.interpolate import interp1d
|
427
|
+
x_ang, sort_ix = create_circular_x(x, rng=rng)
|
428
|
+
mat = np.take(mat, sort_ix, axis=axis)
|
429
|
+
return lambda x: interp1d(x_ang, mat, axis=axis, **kwargs)(wrap_to_circular(x, rng=rng))
|
430
|
+
|
431
|
+
|
432
|
+
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()):
|
433
|
+
from scipy.interpolate import interp1d
|
434
|
+
Qi = lambda om, th: interp1d(omega, interp1d(theta, Q, axis=theta_axis, fill_value='extrapolate', **theta_settings)(wrap_to_circular(th, rng=rng)),
|
435
|
+
fill_value='extrapolate', kind=interpolation_kind, axis=omega_axis, **omega_settings)(om)
|
436
|
+
|
437
|
+
return Qi
|
438
|
+
|
439
|
+
|
440
|
+
|
441
|
+
def interp2d_angular(x, theta, mat, rng=[-np.pi, np.pi], method='linear', **kwargs):
|
442
|
+
from scipy.interpolate import griddata
|
443
|
+
|
444
|
+
def funi(xi, thetai):
|
445
|
+
xi = np.array([xi]).flatten()
|
446
|
+
thetai = np.array([thetai]).flatten()
|
447
|
+
|
448
|
+
Xi, Yi = np.meshgrid(xi, wrap_to_circular(thetai, rng=[-np.pi, np.pi]))
|
449
|
+
X, Y = np.meshgrid(x, wrap_to_circular(theta, rng=[-np.pi, np.pi]))
|
450
|
+
mati = np.zeros([mat.shape[0], len(xi), len(thetai)]).astype(complex)
|
451
|
+
|
452
|
+
for comp in range(mati.shape[0]):
|
453
|
+
mati[comp,:,:] = griddata((X.flatten(),Y.flatten()), mat[comp,:,:].flatten(), (Xi, Yi), method='linear')
|
454
|
+
|
455
|
+
return mati
|
456
|
+
|
457
|
+
return funi
|
458
|
+
|
459
|
+
|
460
|
+
|
461
|
+
def uniquetol(a, tol):
|
462
|
+
import numpy as np
|
463
|
+
i = np.argsort(a.flat)
|
464
|
+
d = np.append(True, np.diff(a.flat[i]))
|
465
|
+
result = a.flat[i[d>tol]]
|
466
|
+
i = i[d>tol]
|
467
|
+
|
468
|
+
return result, i
|
wawi/identification.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from scipy.optimize import fsolve
|
3
|
+
|
4
|
+
def get_combinations(n_p,n_q=None):
|
5
|
+
'''
|
6
|
+
Establish all combinations of n indices.
|
7
|
+
|
8
|
+
Parameters
|
9
|
+
------------
|
10
|
+
n_p : int
|
11
|
+
number of indices
|
12
|
+
n_q : None, optional
|
13
|
+
if integer is given, this is used - otherwise standard value None makes n_q = n_p
|
14
|
+
|
15
|
+
Returns
|
16
|
+
------------
|
17
|
+
combos : int
|
18
|
+
numpy array of all combinations (2-by-n_comb)
|
19
|
+
'''
|
20
|
+
|
21
|
+
p = np.arange(n_p)
|
22
|
+
if n_q is None:
|
23
|
+
q = p
|
24
|
+
|
25
|
+
combos = []
|
26
|
+
for pi in p:
|
27
|
+
for qi in q:
|
28
|
+
combo = list(np.sort([pi,qi]))
|
29
|
+
if (pi!=qi) and (combo not in combos):
|
30
|
+
combos.append(combo)
|
31
|
+
|
32
|
+
return combos
|
33
|
+
|
34
|
+
def wave_number_id(Sx, x, k0, Sref=None):
|
35
|
+
'''
|
36
|
+
Experimental function to establish wave number nonparametrically from cross spectral density.
|
37
|
+
'''
|
38
|
+
k = np.zeros(Sx.shape[2])
|
39
|
+
n_freq = Sx.shape[2]
|
40
|
+
combos = get_combinations(len(x))
|
41
|
+
|
42
|
+
b = np.zeros(len(combos)).astype(complex)
|
43
|
+
|
44
|
+
for ix, combo in enumerate(combos):
|
45
|
+
dx = x[combo[1]] - x[combo[0]]
|
46
|
+
b[ix] = -dx*1j
|
47
|
+
|
48
|
+
for n in range(n_freq):
|
49
|
+
lnGamma = np.zeros(len(combos)).astype(complex)
|
50
|
+
k_all = [None]*len(combos)
|
51
|
+
for ix,combo in enumerate(combos):
|
52
|
+
dof1,dof2 = combo
|
53
|
+
S = np.sqrt(Sx[dof1,dof1, n]*Sx[dof2,dof2, n])
|
54
|
+
|
55
|
+
if S==0:
|
56
|
+
lnGamma[ix] = np.nan
|
57
|
+
else:
|
58
|
+
lnGamma[ix] = np.log(Sx[dof1, dof2, n]/S)
|
59
|
+
k_all[ix] = 1j/dx * lnGamma[ix]
|
60
|
+
|
61
|
+
# k[n] = (b[np.newaxis,:] @ np.linalg.pinv(lnGamma[np.newaxis,:]))[0][0]
|
62
|
+
k[n] = np.mean(k_all)
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
return k
|