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 CHANGED
@@ -1,6 +1,10 @@
1
- '''
1
+ __version__ = "0.0.5"
2
2
 
3
- .. include:: ./../README.md
4
- '''
3
+ __pdoc__ = dict()
4
+ __pdoc__['wawi.abq'] = False
5
5
 
6
- __version__ = "0.0.1"
6
+ # Other packages
7
+ import numpy as np
8
+ import shutil
9
+ import csv
10
+ from copy import deepcopy
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