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 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