wawi 0.0.1__py3-none-any.whl → 0.0.5__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 +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
|