wawi 0.0.13__py3-none-any.whl → 0.0.17__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/general.py CHANGED
@@ -4,6 +4,27 @@ import numpy as np
4
4
  from inspect import isfunction
5
5
 
6
6
  def block_truncate(M, p, n_dofs=6):
7
+ """
8
+ Truncate a block matrix to a specified size.
9
+
10
+ Parameters
11
+ ----------
12
+ M : ndarray
13
+ Input matrix (2D or 3D).
14
+ p : int
15
+ Number of blocks to keep.
16
+ n_dofs : int, optional
17
+ Number of degrees of freedom per block (default is 6).
18
+
19
+ Returns
20
+ -------
21
+ Mtr : ndarray
22
+ Truncated matrix.
23
+
24
+ Notes
25
+ -----
26
+ Docstring generated using GitHub Copilot.
27
+ """
7
28
  Mtr = M*1
8
29
  if np.ndim(M)==2:
9
30
  is_2d = True
@@ -28,6 +49,31 @@ def block_truncate(M, p, n_dofs=6):
28
49
  return Mtr
29
50
 
30
51
  def eval_fun_or_scalar(par, x, y, x0=0, y0=0):
52
+ """
53
+ Evaluate a function or return a scalar.
54
+
55
+ Parameters
56
+ ----------
57
+ par : callable or scalar
58
+ Function or scalar to evaluate.
59
+ x : float
60
+ x value.
61
+ y : float
62
+ y value.
63
+ x0 : float, optional
64
+ x offset (default is 0).
65
+ y0 : float, optional
66
+ y offset (default is 0).
67
+
68
+ Returns
69
+ -------
70
+ result : float
71
+ Evaluated result.
72
+
73
+ Notes
74
+ -----
75
+ Docstring generated using GitHub Copilot.
76
+ """
31
77
  if isfunction(par):
32
78
  if 'x' in par.__code__.co_varnames and 'y' in par.__code__.co_varnames:
33
79
  return par(x - x0, y - y0)
@@ -38,9 +84,24 @@ def eval_fun_or_scalar(par, x, y, x0=0, y0=0):
38
84
  else:
39
85
  return par
40
86
 
41
-
42
- #%%
43
87
  def zero_pad_upsample(B, omega, omega_max):
88
+ """
89
+ Zero-pad and upsample a vector to a new maximum frequency.
90
+
91
+ Parameters
92
+ ----------
93
+ B : ndarray
94
+ Input vector.
95
+ omega : ndarray
96
+ Frequency vector.
97
+ omega_max : float
98
+ Maximum frequency after upsampling.
99
+
100
+ Returns
101
+ -------
102
+ Bi : ndarray
103
+ Zero-padded and upsampled vector.
104
+ """
44
105
  dw = omega[1]-omega[0]
45
106
  omega_max0 = np.max(omega)
46
107
  n_add = int(np.floor((omega_max-omega_max0)/dw))
@@ -48,14 +109,51 @@ def zero_pad_upsample(B, omega, omega_max):
48
109
  return Bi
49
110
 
50
111
  def get_omega_upsampled(omega, omega_max):
112
+ """
113
+ Get upsampled frequency vector.
114
+
115
+ Parameters
116
+ ----------
117
+ omega : ndarray
118
+ Original frequency vector.
119
+ omega_max : float
120
+ Maximum frequency after upsampling.
121
+
122
+ Returns
123
+ -------
124
+ omegai : ndarray
125
+ Upsampled frequency vector.
126
+ """
51
127
  dw = omega[1]-omega[0]
52
128
  omega_max0 = np.max(omega)
53
129
  omega_add = np.arange(omega_max0+dw, omega_max, dw)
54
130
  omegai = np.hstack([omega, omega_add])
55
131
  return omegai
56
132
 
57
- #%% Misc
58
133
  def equal_energy_omega(S, omega_min, omega_max, n, dmax=np.inf, domega_ref=1e-4):
134
+ """
135
+ Generate frequency vector with equal energy intervals.
136
+
137
+ Parameters
138
+ ----------
139
+ S : callable
140
+ Spectral density function.
141
+ omega_min : float
142
+ Minimum frequency.
143
+ omega_max : float
144
+ Maximum frequency.
145
+ n : int
146
+ Number of intervals.
147
+ dmax : float, optional
148
+ Maximum interval width (default is np.inf).
149
+ domega_ref : float, optional
150
+ Reference frequency step (default is 1e-4).
151
+
152
+ Returns
153
+ -------
154
+ omegas : ndarray
155
+ Frequency vector with equal energy intervals.
156
+ """
59
157
  from scipy.integrate import cumtrapz
60
158
 
61
159
  omega_ref = np.arange(omega_min, omega_max, domega_ref)
@@ -81,29 +179,100 @@ def equal_energy_omega(S, omega_min, omega_max, n, dmax=np.inf, domega_ref=1e-4)
81
179
 
82
180
  return omegas
83
181
 
84
-
85
- #%% Matrix manipulation / generation
86
182
  def blkdiag(mat, n):
183
+ """
184
+ Create a block diagonal matrix.
185
+
186
+ Parameters
187
+ ----------
188
+ mat : ndarray
189
+ Matrix to repeat along the diagonal.
190
+ n : int
191
+ Number of blocks.
192
+
193
+ Returns
194
+ -------
195
+ blk : ndarray
196
+ Block diagonal matrix.
197
+ """
87
198
  return np.kron(np.eye(n), mat)
88
199
 
89
200
  def fun_sum(*functions):
201
+ """
202
+ Sum multiple functions.
203
+
204
+ Parameters
205
+ ----------
206
+ *functions : callable
207
+ Functions to sum.
208
+
209
+ Returns
210
+ -------
211
+ fun : callable
212
+ Function representing the sum.
213
+ """
90
214
  def fun(x):
91
215
  return sum([f(x) for f in functions])
92
216
 
93
217
  return fun
94
218
 
95
219
  def fun_scale(function, scaling):
220
+ """
221
+ Scale a function by a constant.
222
+
223
+ Parameters
224
+ ----------
225
+ function : callable
226
+ Function to scale.
227
+ scaling : float
228
+ Scaling factor.
229
+
230
+ Returns
231
+ -------
232
+ fun : callable
233
+ Scaled function.
234
+ """
96
235
  def fun(x):
97
236
  return function(x)*scaling
98
237
 
99
238
  return fun
100
239
 
101
240
  def fun_const_sum(function, constant):
241
+ """
242
+ Add a constant to a function.
243
+
244
+ Parameters
245
+ ----------
246
+ function : callable
247
+ Function to add to.
248
+ constant : float
249
+ Constant to add.
250
+
251
+ Returns
252
+ -------
253
+ fun : callable
254
+ Function plus constant.
255
+ """
102
256
  def fun(x):
103
257
  return function(x) + constant
104
258
  return fun
105
259
 
106
260
  def eval_3d_fun(fun, z):
261
+ """
262
+ Evaluate a function over a 1D array and stack results into a 3D array.
263
+
264
+ Parameters
265
+ ----------
266
+ fun : callable
267
+ Function to evaluate.
268
+ z : ndarray
269
+ 1D array of input values.
270
+
271
+ Returns
272
+ -------
273
+ result : ndarray
274
+ Stacked results.
275
+ """
107
276
  return np.stack([fun(z_k) for z_k in z], axis=2)
108
277
 
109
278
  def correct_matrix_size(mat, n_freqs, n_dofs):
@@ -111,20 +280,19 @@ def correct_matrix_size(mat, n_freqs, n_dofs):
111
280
  Extend matrix to specified dimensions (frequencies and DOFs).
112
281
 
113
282
  Parameters
114
- ---------------------------
115
- mat : double
116
- matrix to be checked and possibly modified
283
+ ----------
284
+ mat : ndarray
285
+ Matrix to be checked and possibly modified.
117
286
  n_freqs : int
118
- number of frequencies (depth of final matrix)
287
+ Number of frequencies (depth of final matrix).
119
288
  n_dofs : int
120
- number of degrees of freedom (height and width of final matrix)
289
+ Number of degrees of freedom (height and width of final matrix).
121
290
 
122
291
  Returns
123
- ---------------------------
124
- mat_extended : double
125
- corrected/verified matrix
292
+ -------
293
+ mat_extended : ndarray
294
+ Corrected/verified matrix.
126
295
  """
127
-
128
296
  mat_shape = np.array(np.shape(mat))
129
297
 
130
298
  for ix in range(0, 3-len(mat_shape)):
@@ -142,9 +310,37 @@ def correct_matrix_size(mat, n_freqs, n_dofs):
142
310
  return mat_extended
143
311
 
144
312
  def wrap_to_pi(angle):
313
+ """
314
+ Wrap angle to [-pi, pi].
315
+
316
+ Parameters
317
+ ----------
318
+ angle : float or ndarray
319
+ Input angle(s).
320
+
321
+ Returns
322
+ -------
323
+ wrapped : float or ndarray
324
+ Wrapped angle(s).
325
+ """
145
326
  return (angle + np.pi) % (2*np.pi) - np.pi
146
327
 
147
328
  def wrap_to_circular(x, rng=[-np.pi, np.pi]):
329
+ """
330
+ Wrap values to a circular range.
331
+
332
+ Parameters
333
+ ----------
334
+ x : float or ndarray
335
+ Input value(s).
336
+ rng : list or ndarray, optional
337
+ Range to wrap to (default is [-pi, pi]).
338
+
339
+ Returns
340
+ -------
341
+ wrapped : float or ndarray
342
+ Wrapped value(s).
343
+ """
148
344
  x0 = np.mean(rng)
149
345
  dx_sym = (np.max(rng) - np.min(rng))/2
150
346
  x_centered = (x + dx_sym) % (2*dx_sym) - dx_sym
@@ -155,15 +351,20 @@ def merge_tr_phi(phi_trans, phi_rot, thread_stack=True):
155
351
  """
156
352
  Merge matrices of phi for translational and rotational DOFs.
157
353
 
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
- """
354
+ Parameters
355
+ ----------
356
+ phi_trans : ndarray
357
+ Phi matrix with only translational DOFs.
358
+ phi_rot : ndarray
359
+ Phi matrix with only rotational DOFs.
360
+ thread_stack : bool, optional
361
+ If True, stack DOFs in thread order (default is True).
166
362
 
363
+ Returns
364
+ -------
365
+ phi_combined : ndarray
366
+ Phi matrix with all DOFs.
367
+ """
167
368
  Ndofs = phi_trans.shape[0]*2
168
369
 
169
370
  if thread_stack is True:
@@ -179,9 +380,22 @@ def merge_tr_phi(phi_trans, phi_rot, thread_stack=True):
179
380
 
180
381
  return phi_combined
181
382
 
182
-
183
383
  def mat3d_sel(mat, k):
384
+ """
385
+ Select a 2D slice from a 3D matrix.
386
+
387
+ Parameters
388
+ ----------
389
+ mat : ndarray
390
+ 3D matrix.
391
+ k : int
392
+ Index of the slice.
184
393
 
394
+ Returns
395
+ -------
396
+ matsel : ndarray
397
+ Selected 2D matrix.
398
+ """
185
399
  if len(np.shape(mat)) is 3:
186
400
  matsel = mat[:, :, k]
187
401
  else:
@@ -189,40 +403,47 @@ def mat3d_sel(mat, k):
189
403
 
190
404
  return matsel
191
405
 
192
-
193
-
194
406
  def interp1z(z,mat,znew):
195
407
  """
196
- Interpolate 3D matrix along z-component. !! Deprecated - use numpy functions directly instead !!
408
+ Interpolate 3D matrix along z-component.
197
409
 
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)
410
+ Parameters
411
+ ----------
412
+ z : ndarray
413
+ z axis.
414
+ mat : ndarray
415
+ 3D matrix.
416
+ znew : ndarray
417
+ New z axis.
204
418
 
205
- """
206
-
419
+ Returns
420
+ -------
421
+ matnew : ndarray
422
+ Interpolated 3D matrix.
423
+ """
207
424
  matnew = np.zeros([1,len(mat[0]),len(mat[0][0])])
208
425
  for dof1 in range(0,len(mat[0])):
209
426
  for dof2 in range(0,len(mat[0][0])):
210
427
  matnew[:,dof1,dof2]=np.interp(znew,z,mat[:,dof1,dof2])
211
428
  return matnew
212
429
 
213
-
214
430
  def interpolate_3d(z, mat, zi):
215
431
  """
216
432
  Interpolates 3D NumPy array.
217
433
 
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
434
+ Parameters
435
+ ----------
436
+ z : ndarray
437
+ Original z axis.
438
+ mat : ndarray
439
+ 3D matrix (n_x-by-n_y-by-n_z).
440
+ zi : ndarray
441
+ Interpolated z axis.
225
442
 
443
+ Returns
444
+ -------
445
+ mati : ndarray
446
+ Interpolated matrix.
226
447
  """
227
448
  mat_shape = np.shape(mat)
228
449
  mati = np.zeros([mat_shape[0], mat_shape[1], len(zi)])
@@ -233,8 +454,24 @@ def interpolate_3d(z, mat, zi):
233
454
 
234
455
  return mati
235
456
 
236
-
237
457
  def fast_interpolation(x, y, new_x):
458
+ """
459
+ Fast cubic spline interpolation for multidimensional arrays.
460
+
461
+ Parameters
462
+ ----------
463
+ x : ndarray
464
+ Original x values.
465
+ y : ndarray
466
+ Original y values.
467
+ new_x : ndarray
468
+ New x values for interpolation.
469
+
470
+ Returns
471
+ -------
472
+ result : ndarray
473
+ Interpolated values.
474
+ """
238
475
  from scipy.interpolate import interp1d
239
476
  from scipy.interpolate._fitpack import _bspleval
240
477
  f = interp1d(x, y, axis=-1, kind=3)
@@ -244,10 +481,22 @@ def fast_interpolation(x, y, new_x):
244
481
  result[i, j] = _bspleval(value, x, cvals[:, i, j], k, 0)
245
482
  return result
246
483
 
484
+ def transformation_matrix(angles, axis):
485
+ """
486
+ Generate transformation matrices for a set of angles and a given axis.
247
487
 
488
+ Parameters
489
+ ----------
490
+ angles : ndarray
491
+ Array of angles.
492
+ axis : int
493
+ Axis of rotation (0, 1, or 2).
248
494
 
249
- #%% Transformation matrix generation / manipulation
250
- def transformation_matrix(angles, axis):
495
+ Returns
496
+ -------
497
+ T : ndarray
498
+ Transformation matrices (len(angles), 6, 6).
499
+ """
251
500
  T = np.empty([len(angles),6,6])
252
501
  for idx,angle in enumerate(angles):
253
502
  c = np.cos(angle)
@@ -263,21 +512,24 @@ def transformation_matrix(angles, axis):
263
512
 
264
513
  return T
265
514
 
266
-
267
515
  def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
268
516
  """
269
517
  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
518
 
276
- Returns:
277
- T: transformation matrix in NumPy format
519
+ Parameters
520
+ ----------
521
+ theta : float
522
+ Rotation angle (in radians).
523
+ rotaxis : array_like, optional
524
+ Vector defining rotation axis (default [0, 0, 1]).
525
+ style : str, optional
526
+ Output style ('row' or other, default 'row').
278
527
 
528
+ Returns
529
+ -------
530
+ T : ndarray
531
+ Transformation matrix.
279
532
  """
280
-
281
533
  axis = np.asarray(rotaxis)
282
534
  axis = rotaxis/np.sqrt(np.dot(rotaxis, rotaxis)) # Normalize
283
535
  a = np.cos(theta/2.0)
@@ -293,16 +545,44 @@ def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
293
545
 
294
546
  return T
295
547
 
296
-
297
548
  def multi_rodrot(angles, rotaxis=[0,0,1], style='row'):
549
+ """
550
+ Generate multiple 6x6 rotation matrices for a set of angles.
551
+
552
+ Parameters
553
+ ----------
554
+ angles : ndarray
555
+ Array of angles.
556
+ rotaxis : array_like, optional
557
+ Rotation axis (default [0, 0, 1]).
558
+ style : str, optional
559
+ Output style (default 'row').
560
+
561
+ Returns
562
+ -------
563
+ T : list of ndarray
564
+ List of 6x6 rotation matrices.
565
+ """
298
566
  T = [None]*len(angles)
299
567
  for ix, angle in enumerate(angles):
300
568
  T[ix] = blkdiag(rodrot(angle, rotaxis=rotaxis, style=style), 2)
301
569
 
302
570
  return T
303
571
 
304
-
305
572
  def stack_T(T_multi):
573
+ """
574
+ Stack multiple transformation matrices into a block diagonal matrix.
575
+
576
+ Parameters
577
+ ----------
578
+ T_multi : list of ndarray
579
+ List of transformation matrices.
580
+
581
+ Returns
582
+ -------
583
+ T : ndarray
584
+ Block diagonal transformation matrix.
585
+ """
306
586
  n_dofs = np.shape(T_multi[0])[0]
307
587
  N = len(T_multi)
308
588
 
@@ -313,8 +593,22 @@ def stack_T(T_multi):
313
593
 
314
594
  return T
315
595
 
316
-
317
596
  def transform_unit(e1, e2p):
597
+ """
598
+ Generate a transformation matrix from two unit vectors.
599
+
600
+ Parameters
601
+ ----------
602
+ e1 : array_like
603
+ First unit vector.
604
+ e2p : array_like
605
+ Second vector (not necessarily unit).
606
+
607
+ Returns
608
+ -------
609
+ T : ndarray
610
+ Transformation matrix (3x3).
611
+ """
318
612
  e1 = np.array(e1).flatten()
319
613
  e2p = np.array(e2p).flatten()
320
614
 
@@ -329,8 +623,22 @@ def transform_unit(e1, e2p):
329
623
 
330
624
  return T
331
625
 
332
-
333
626
  def transform_3dmat(mat, T):
627
+ """
628
+ Transform a 3D matrix using a transformation matrix.
629
+
630
+ Parameters
631
+ ----------
632
+ mat : ndarray
633
+ 3D matrix (n_dofs, n_dofs, n_freqs).
634
+ T : ndarray
635
+ Transformation matrix.
636
+
637
+ Returns
638
+ -------
639
+ mat_transformed : ndarray
640
+ Transformed 3D matrix.
641
+ """
334
642
  M, N = np.shape(T)[0:2]
335
643
  [n_dofs, __, n_freqs] = np.shape(mat)
336
644
 
@@ -347,9 +655,20 @@ def transform_3dmat(mat, T):
347
655
 
348
656
  return mat_transformed
349
657
 
350
- #%% Other
351
658
  def assess_diagonality_fro(M):
352
- # Diagonality Measures of Hermitian Positive-Definite Matrices with Application to the Approximate Joint Diagonalization Problem
659
+ """
660
+ Assess diagonality of a Hermitian positive-definite matrix using Frobenius norm.
661
+
662
+ Parameters
663
+ ----------
664
+ M : ndarray
665
+ Input matrix (2D, square).
666
+
667
+ Returns
668
+ -------
669
+ diagonality : float
670
+ Diagonality measure.
671
+ """
353
672
  if M.ndim != 2:
354
673
  raise ValueError('Input must be 2d array.')
355
674
  if np.shape(M)[0] != np.shape(M)[1]:
@@ -364,9 +683,20 @@ def assess_diagonality_fro(M):
364
683
 
365
684
  return diagonality
366
685
 
367
-
368
686
  def assess_diagonality(M):
369
- # Diagonality Measures of Hermitian Positive-Definite Matrices with Application to the Approximate Joint Diagonalization Problem
687
+ """
688
+ Assess diagonality of a Hermitian positive-definite matrix.
689
+
690
+ Parameters
691
+ ----------
692
+ M : ndarray
693
+ Input matrix (2D, square).
694
+
695
+ Returns
696
+ -------
697
+ diagonality : float
698
+ Diagonality measure.
699
+ """
370
700
  if M.ndim != 2:
371
701
  raise ValueError('Input must be 2d array.')
372
702
  if np.shape(M)[0] != np.shape(M)[1]:
@@ -380,16 +710,52 @@ def assess_diagonality(M):
380
710
 
381
711
  return diagonality
382
712
 
383
-
384
713
  def modify_stiffness(stiffness, submerged_vol, water_dens, g, z_cog, z_cog_mod):
714
+ """
715
+ Modify stiffness matrix for change in center of gravity.
716
+
717
+ Parameters
718
+ ----------
719
+ stiffness : ndarray
720
+ Original stiffness matrix.
721
+ submerged_vol : float
722
+ Submerged volume.
723
+ water_dens : float
724
+ Water density.
725
+ g : float
726
+ Gravitational acceleration.
727
+ z_cog : float
728
+ Original center of gravity (z).
729
+ z_cog_mod : float
730
+ Modified center of gravity (z).
731
+
732
+ Returns
733
+ -------
734
+ stiffness_mod : ndarray
735
+ Modified stiffness matrix.
736
+ """
385
737
  stiffness_mod = stiffness
386
738
  stiffness_mod[3, 3] = stiffness[3, 3] + submerged_vol * water_dens * g * (z_cog - z_cog_mod)
387
739
  stiffness_mod[4, 4] = stiffness[4, 4]+ submerged_vol * water_dens * g * (z_cog - z_cog_mod)
388
740
 
389
741
  return stiffness_mod
390
742
 
391
-
392
743
  def maxreal(phi, preserve_conjugates=False):
744
+ """
745
+ Rotate complex mode shapes to maximize real part.
746
+
747
+ Parameters
748
+ ----------
749
+ phi : ndarray
750
+ Mode shape matrix.
751
+ preserve_conjugates : bool, optional
752
+ If True, preserve conjugate pairs (default False).
753
+
754
+ Returns
755
+ -------
756
+ phi_max_real : ndarray
757
+ Rotated mode shapes with maximized real part.
758
+ """
393
759
  angles = np.expand_dims(np.arange(0,np.pi/2, 0.01), axis=0)
394
760
 
395
761
  if phi.ndim==1:
@@ -405,8 +771,26 @@ def maxreal(phi, preserve_conjugates=False):
405
771
 
406
772
  return phi_max_real
407
773
 
408
-
409
774
  def create_circular_x(x, tol=1e-5, rng=np.array([-np.pi, np.pi])):
775
+ """
776
+ Create a circularly wrapped and sorted version of x.
777
+
778
+ Parameters
779
+ ----------
780
+ x : ndarray
781
+ Input array.
782
+ tol : float, optional
783
+ Tolerance for uniqueness (default 1e-5).
784
+ rng : ndarray, optional
785
+ Range for wrapping (default [-pi, pi]).
786
+
787
+ Returns
788
+ -------
789
+ x_ang : ndarray
790
+ Wrapped and sorted array.
791
+ sort_ix : ndarray
792
+ Indices of unique values.
793
+ """
410
794
  xrng = np.max(x)-np.min(x)
411
795
 
412
796
  wrapped_x = np.sort(wrap_to_circular(x, rng))
@@ -421,24 +805,93 @@ def create_circular_x(x, tol=1e-5, rng=np.array([-np.pi, np.pi])):
421
805
 
422
806
  return x_ang, sort_ix
423
807
 
424
-
425
808
  def interp1d_angular(x, mat, rng=[-np.pi, np.pi], axis=-1, **kwargs):
809
+ """
810
+ Interpolate data defined on a circular domain.
811
+
812
+ Parameters
813
+ ----------
814
+ x : ndarray
815
+ Input angular values.
816
+ mat : ndarray
817
+ Data to interpolate.
818
+ rng : list or ndarray, optional
819
+ Range for wrapping (default [-pi, pi]).
820
+ axis : int, optional
821
+ Axis along which to interpolate (default -1).
822
+ **kwargs
823
+ Additional arguments to scipy.interpolate.interp1d.
824
+
825
+ Returns
826
+ -------
827
+ interp_fun : callable
828
+ Interpolation function.
829
+ """
426
830
  from scipy.interpolate import interp1d
427
831
  x_ang, sort_ix = create_circular_x(x, rng=rng)
428
832
  mat = np.take(mat, sort_ix, axis=axis)
429
833
  return lambda x: interp1d(x_ang, mat, axis=axis, **kwargs)(wrap_to_circular(x, rng=rng))
430
834
 
431
-
432
835
  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()):
836
+ """
837
+ Interpolate hydrodynamic transfer function in both frequency and angle.
838
+
839
+ Parameters
840
+ ----------
841
+ omega : ndarray
842
+ Frequency vector.
843
+ theta : ndarray
844
+ Angle vector.
845
+ Q : ndarray
846
+ Transfer function data.
847
+ rng : list or ndarray, optional
848
+ Range for wrapping (default [-pi, pi]).
849
+ theta_axis : int, optional
850
+ Axis for theta (default 1).
851
+ omega_axis : int, optional
852
+ Axis for omega (default 2).
853
+ interpolation_kind : str, optional
854
+ Interpolation kind (default 'linear').
855
+ theta_settings : dict, optional
856
+ Additional settings for theta interpolation.
857
+ omega_settings : dict, optional
858
+ Additional settings for omega interpolation.
859
+
860
+ Returns
861
+ -------
862
+ Qi : callable
863
+ Interpolated transfer function.
864
+ """
433
865
  from scipy.interpolate import interp1d
434
866
  Qi = lambda om, th: interp1d(omega, interp1d(theta, Q, axis=theta_axis, fill_value='extrapolate', **theta_settings)(wrap_to_circular(th, rng=rng)),
435
867
  fill_value='extrapolate', kind=interpolation_kind, axis=omega_axis, **omega_settings)(om)
436
868
 
437
869
  return Qi
438
-
439
-
440
870
 
441
871
  def interp2d_angular(x, theta, mat, rng=[-np.pi, np.pi], method='linear', **kwargs):
872
+ """
873
+ 2D interpolation on a circular domain.
874
+
875
+ Parameters
876
+ ----------
877
+ x : ndarray
878
+ First coordinate array.
879
+ theta : ndarray
880
+ Angular coordinate array.
881
+ mat : ndarray
882
+ Data to interpolate.
883
+ rng : list or ndarray, optional
884
+ Range for wrapping (default [-pi, pi]).
885
+ method : str, optional
886
+ Interpolation method (default 'linear').
887
+ **kwargs
888
+ Additional arguments to scipy.interpolate.griddata.
889
+
890
+ Returns
891
+ -------
892
+ funi : callable
893
+ Interpolation function.
894
+ """
442
895
  from scipy.interpolate import griddata
443
896
 
444
897
  def funi(xi, thetai):
@@ -456,9 +909,24 @@ def interp2d_angular(x, theta, mat, rng=[-np.pi, np.pi], method='linear', **kwar
456
909
 
457
910
  return funi
458
911
 
912
+ def uniquetol(a, tol):
913
+ """
914
+ Return unique values of an array within a tolerance.
459
915
 
916
+ Parameters
917
+ ----------
918
+ a : ndarray
919
+ Input array.
920
+ tol : float
921
+ Tolerance for uniqueness.
460
922
 
461
- def uniquetol(a, tol):
923
+ Returns
924
+ -------
925
+ result : ndarray
926
+ Unique values.
927
+ i : ndarray
928
+ Indices of unique values.
929
+ """
462
930
  import numpy as np
463
931
  i = np.argsort(a.flat)
464
932
  d = np.append(True, np.diff(a.flat[i]))