acoular 25.7__py3-none-any.whl → 26.1__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.
acoular/fbeamform.py CHANGED
@@ -1,7 +1,16 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements beamformers in the frequency domain.
4
+ """
5
+ Implements beamformers in the frequency domain.
6
+
7
+ .. inheritance-diagram::
8
+ acoular.fbeamform.BeamformerBase
9
+ :include-subclasses:
10
+ :top-classes:
11
+ acoular.grids.Grid,
12
+ acoular.fbeamform.BeamformerBase
13
+ :parts: 1
5
14
 
6
15
  .. autosummary::
7
16
  :toctree: generated/
@@ -28,7 +37,6 @@
28
37
  PointSpreadFunction
29
38
  L_p
30
39
  integrate
31
-
32
40
  """
33
41
 
34
42
  # imports from other packages
@@ -36,53 +44,12 @@
36
44
  import warnings
37
45
  from warnings import warn
38
46
 
47
+ import numpy as np
48
+ import scipy.linalg as spla
49
+
39
50
  # check for sklearn version to account for incompatible behavior
40
51
  import sklearn
41
- from numpy import (
42
- absolute,
43
- arange,
44
- argsort,
45
- array,
46
- atleast_2d,
47
- clip,
48
- delete,
49
- diag,
50
- dot,
51
- einsum,
52
- einsum_path,
53
- eye,
54
- fill_diagonal,
55
- full,
56
- hsplit,
57
- hstack,
58
- index_exp,
59
- inf,
60
- integer,
61
- invert,
62
- isscalar,
63
- log10,
64
- ndarray,
65
- newaxis,
66
- ones,
67
- pi,
68
- real,
69
- reshape,
70
- round, # noqa: A004
71
- searchsorted,
72
- sign,
73
- size,
74
- sqrt,
75
- sum, # noqa: A004
76
- tile,
77
- trace,
78
- tril,
79
- unique,
80
- vstack,
81
- zeros,
82
- zeros_like,
83
- )
84
52
  from packaging.version import parse
85
- from scipy.linalg import eigh, eigvals, fractional_matrix_power, inv, norm
86
53
  from scipy.optimize import fmin_l_bfgs_b, linprog, nnls, shgo
87
54
  from sklearn.linear_model import LassoLars, LassoLarsCV, LassoLarsIC, LinearRegression, OrthogonalMatchingPursuitCV
88
55
  from traits.api import (
@@ -100,14 +67,13 @@ from traits.api import (
100
67
  Range,
101
68
  Tuple,
102
69
  cached_property,
103
- on_trait_change,
70
+ observe,
104
71
  property_depends_on,
105
72
  )
106
73
  from traits.trait_errors import TraitError
107
74
 
108
75
  # acoular imports
109
76
  from .configuration import config
110
- from .deprecation import deprecated_alias
111
77
  from .environments import Environment
112
78
  from .fastFuncs import beamformerFreq, calcPointSpreadFunction, calcTransfer, damasSolverGaussSeidel
113
79
  from .grids import Grid, Sector
@@ -126,54 +92,56 @@ BEAMFORMER_BASE_DIGEST_DEPENDENCIES = ['freq_data.digest', 'r_diag', 'r_diag_nor
126
92
 
127
93
 
128
94
  class SteeringVector(HasStrictTraits):
129
- """Basic class for implementing steering vectors with monopole source transfer models.
95
+ """
96
+ Basic class for implementing steering vectors with monopole source transfer models.
130
97
 
131
98
  Handles four different steering vector formulations. See :cite:`Sarradj2012` for details.
132
99
  """
133
100
 
134
101
  #: :class:`~acoular.grids.Grid`-derived object that provides the grid locations.
135
- grid = Instance(Grid, desc='beamforming grid')
102
+ grid = Instance(Grid)
136
103
 
137
104
  #: :class:`~acoular.microphones.MicGeom` object that provides the microphone locations.
138
- mics = Instance(MicGeom, desc='microphone geometry')
105
+ mics = Instance(MicGeom)
139
106
 
140
107
  #: Type of steering vectors, see also :cite:`Sarradj2012`. Defaults to 'true level'.
141
- steer_type = Enum('true level', 'true location', 'classic', 'inverse', desc='type of steering vectors used')
108
+ steer_type = Enum('true level', 'true location', 'classic', 'inverse')
142
109
 
143
110
  #: :class:`~acoular.environments.Environment` or derived object,
144
111
  #: which provides information about the sound propagation in the medium.
145
112
  #: Defaults to standard :class:`~acoular.environments.Environment` object.
146
113
  env = Instance(Environment(), Environment)
147
114
 
148
- # Sound travel distances from microphone array center to grid
149
- # points or reference position (readonly). Feature may change.
150
- r0 = Property(desc='array center to grid distances')
115
+ #: Sound travel distances from microphone array center to grid : points or reference position
116
+ #: (readonly). Feature may change.
117
+ r0 = Property()
151
118
 
152
- # Sound travel distances from array microphones to grid
153
- # points (readonly). Feature may change.
154
- rm = Property(desc='all array mics to grid distances')
119
+ #: Sound travel distances from array microphones to grid : points (readonly). Feature may
120
+ #: change.
121
+ rm = Property()
155
122
 
156
- # mirror trait for ref
157
- _ref = Any(array([0.0, 0.0, 0.0]), desc='reference position or distance')
123
+ # reference position or distance
124
+ _ref = Any(np.array([0.0, 0.0, 0.0]))
158
125
 
159
126
  #: Reference position or distance at which to evaluate the sound pressure
160
127
  #: of a grid point.
161
128
  #: If set to a scalar, this is used as reference distance to the grid points.
162
129
  #: If set to a vector, this is interpreted as x,y,z coordinates of the reference position.
163
130
  #: Defaults to [0.,0.,0.].
164
- ref = Property(desc='reference position or distance')
131
+ ref = Property()
165
132
 
133
+ #: dictionary of frequency domain steering vector functions
166
134
  _steer_funcs_freq = Dict(
167
135
  {
168
- 'classic': lambda x: x / absolute(x) / x.shape[-1],
136
+ 'classic': lambda x: x / np.abs(x) / x.shape[-1],
169
137
  'inverse': lambda x: 1.0 / x.conj() / x.shape[-1],
170
- 'true level': lambda x: x / einsum('ij,ij->i', x, x.conj())[:, newaxis],
171
- 'true location': lambda x: x / sqrt(einsum('ij,ij->i', x, x.conj()) * x.shape[-1])[:, newaxis],
138
+ 'true level': lambda x: x / np.einsum('ij,ij->i', x, x.conj())[:, np.newaxis],
139
+ 'true location': lambda x: x / np.sqrt(np.einsum('ij,ij->i', x, x.conj()) * x.shape[-1])[:, np.newaxis],
172
140
  },
173
141
  transient=True,
174
- desc='dictionary of frequency domain steering vector functions',
175
142
  )
176
143
 
144
+ #: dictionary of time domain steering vector functions
177
145
  _steer_funcs_time = Dict(
178
146
  {
179
147
  'classic': _steer_I,
@@ -182,40 +150,40 @@ class SteeringVector(HasStrictTraits):
182
150
  'true location': _steer_IV,
183
151
  },
184
152
  transient=True,
185
- desc='dictionary of time domain steering vector functions',
186
153
  )
187
154
 
188
155
  def _set_ref(self, ref):
189
- if isscalar(ref):
156
+ if np.isscalar(ref):
190
157
  try:
191
- self._ref = absolute(float(ref))
158
+ self._ref = np.abs(float(ref))
192
159
  except ValueError as ve:
193
160
  raise TraitError(args=self, name='ref', info='Float or CArray(3,)', value=ref) from ve
194
161
  elif len(ref) == 3:
195
- self._ref = array(ref, dtype=float)
162
+ self._ref = np.array(ref, dtype=float)
196
163
  else:
197
164
  raise TraitError(args=self, name='ref', info='Float or CArray(3,)', value=ref)
198
165
 
199
166
  def _get_ref(self):
200
167
  return self._ref
201
168
 
202
- # internal identifier
169
+ #: A unique identifier for the steering vector, based on its properties. (read-only)
203
170
  digest = Property(depends_on=['steer_type', 'env.digest', 'grid.digest', 'mics.digest', '_ref'])
204
171
 
205
- # internal identifier, use for inverse methods, excluding steering vector type
172
+ #: A unique identifier for the grid, excluding :attr:`steer_type`.
173
+ #: Use for inverse methods. (read-only)
206
174
  inv_digest = Property(depends_on=['env.digest', 'grid.digest', 'mics.digest', '_ref'])
207
175
 
208
176
  @property_depends_on(['grid.digest', 'env.digest', '_ref'])
209
177
  def _get_r0(self):
210
- if isscalar(self.ref):
178
+ if np.isscalar(self.ref):
211
179
  if self.ref > 0:
212
- return full((self.grid.size,), self.ref)
180
+ return np.full((self.grid.size,), self.ref)
213
181
  return self.env._r(self.grid.pos())
214
- return self.env._r(self.grid.pos, self.ref[:, newaxis])
182
+ return self.env._r(self.grid.pos, self.ref[:, np.newaxis])
215
183
 
216
184
  @property_depends_on(['grid.digest', 'mics.digest', 'env.digest'])
217
185
  def _get_rm(self):
218
- return atleast_2d(self.env._r(self.grid.pos, self.mics.pos))
186
+ return np.atleast_2d(self.env._r(self.grid.pos, self.mics.pos))
219
187
 
220
188
  @cached_property
221
189
  def _get_digest(self):
@@ -226,7 +194,8 @@ class SteeringVector(HasStrictTraits):
226
194
  return digest(self)
227
195
 
228
196
  def transfer(self, f, ind=None):
229
- """Calculates the transfer matrix for one frequency.
197
+ """
198
+ Calculates the transfer matrix for one frequency.
230
199
 
231
200
  Parameters
232
201
  ----------
@@ -241,22 +210,22 @@ class SteeringVector(HasStrictTraits):
241
210
  -------
242
211
  array of complex128
243
212
  array of shape (ngridpts, nmics) containing the transfer matrix for the given frequency
244
-
245
213
  """
246
214
  # if self.cached:
247
215
  # warn('Caching of transfer function is not yet supported!', Warning)
248
216
  # self.cached = False
249
217
 
250
218
  if ind is None:
251
- trans = calcTransfer(self.r0, self.rm, array(2 * pi * f / self.env.c))
252
- elif not isinstance(ind, ndarray):
253
- trans = calcTransfer(self.r0[ind], self.rm[ind, :][newaxis], array(2 * pi * f / self.env.c)) # [0, :]
219
+ trans = calcTransfer(self.r0, self.rm, np.array(2 * np.pi * f / self.env.c))
220
+ elif not isinstance(ind, np.ndarray):
221
+ trans = calcTransfer(self.r0[ind], self.rm[ind, :][np.newaxis], np.array(2 * np.pi * f / self.env.c))
254
222
  else:
255
- trans = calcTransfer(self.r0[ind], self.rm[ind, :], array(2 * pi * f / self.env.c))
223
+ trans = calcTransfer(self.r0[ind], self.rm[ind, :], np.array(2 * np.pi * f / self.env.c))
256
224
  return trans
257
225
 
258
226
  def steer_vector(self, f, ind=None):
259
- """Calculates the steering vectors based on the transfer function.
227
+ """
228
+ Calculates the steering vectors based on the transfer function.
260
229
 
261
230
  See also :cite:`Sarradj2012`.
262
231
 
@@ -273,7 +242,6 @@ class SteeringVector(HasStrictTraits):
273
242
  -------
274
243
  array of complex128
275
244
  array of shape (ngridpts, nmics) containing the steering vectors for the given frequency
276
-
277
245
  """
278
246
  func = self._steer_funcs_freq[self.steer_type]
279
247
  return func(self.transfer(f, ind))
@@ -293,11 +261,11 @@ class LazyBfResult:
293
261
 
294
262
  def __getitem__(self, key):
295
263
  """'intelligent' [] operator, checks if results are available and triggers calculation."""
296
- sl = index_exp[key][0]
297
- if isinstance(sl, (int, integer)):
264
+ sl = np.index_exp[key][0]
265
+ if isinstance(sl, (int, np.integer)):
298
266
  sl = slice(sl, sl + 1)
299
267
  # indices which are missing
300
- missingind = arange(*sl.indices(self.bf._numfreq))[self.bf._fr[sl] == 0]
268
+ missingind = np.arange(*sl.indices(self.bf._numfreq))[self.bf._fr[sl] == 0]
301
269
  # calc if needed
302
270
  if missingind.size:
303
271
  self.bf._calc(missingind)
@@ -310,61 +278,65 @@ class LazyBfResult:
310
278
  class BeamformerBase(HasStrictTraits):
311
279
  """Beamforming using the basic delay-and-sum algorithm in the frequency domain."""
312
280
 
313
- # Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
314
- # that contains information about the steering vector. This is a private trait.
315
- # Do not set this directly, use `steer` trait instead.
281
+ #: Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
282
+ #: that contains information about the steering vector. This is a private trait.
283
+ #: Do not set this directly, use `steer` trait instead.
316
284
  steer = Instance(SteeringVector, args=())
317
285
 
318
286
  #: :class:`~acoular.spectra.PowerSpectra` object that provides the
319
287
  #: cross spectral matrix and eigenvalues
320
- freq_data = Instance(PowerSpectra, desc='freq data object')
288
+ freq_data = Instance(PowerSpectra)
321
289
 
322
290
  #: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
323
- r_diag = Bool(True, desc='removal of diagonal')
291
+ r_diag = Bool(True)
324
292
 
325
- #: If r_diag==True: if r_diag_norm==0.0, the standard
293
+ #: If diagonal of the CSM is removed, some signal energy is lost.
294
+ #: This is handled via this normalization factor.
295
+ #: Internally, the default is: num_mics / (num_mics - 1).
296
+ #:
297
+ #: If r_diag==True: if r_diag_norm==0.0, the default
326
298
  #: normalization = num_mics/(num_mics-1) is used.
327
299
  #: If r_diag_norm !=0.0, the user input is used instead.
328
300
  #: If r_diag==False, the normalization is 1.0 either way.
329
- r_diag_norm = Float(
330
- 0.0,
331
- desc='If diagonal of the csm is removed, some signal energy is lost.'
332
- 'This is handled via this normalization factor.'
333
- 'Internally, the default is: num_mics / (num_mics - 1).',
334
- )
301
+ r_diag_norm = Float(0.0)
335
302
 
336
303
  #: Floating point precision of property result. Corresponding to numpy dtypes. Default = 64 Bit.
337
- precision = Enum('float64', 'float32', desc='precision (32/64 Bit) of result, corresponding to numpy dtypes')
304
+ precision = Enum('float64', 'float32')
338
305
 
339
306
  #: Boolean flag, if 'True' (default), the result is cached in h5 files.
340
- cached = Bool(True, desc='cached flag')
307
+ cached = Bool(True)
341
308
 
342
- # hdf5 cache file
309
+ #: hdf5 cache file
343
310
  h5f = Instance(H5CacheFileBase, transient=True)
344
311
 
345
312
  #: The beamforming result as squared sound pressure values
346
313
  #: at all grid point locations (readonly).
347
314
  #: Returns a (number of frequencies, number of gridpoints) array-like
348
315
  #: of floats. Values can only be accessed via the index operator [].
349
- result = Property(desc='beamforming result')
316
+ result = Property()
350
317
 
351
- # internal identifier
318
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
352
319
  digest = Property(depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES)
353
320
 
354
321
  # private traits
355
- _ac = Any(desc='beamforming result')
356
- _fr = Any(desc='flag for beamforming result at frequency index')
357
- _f = CArray(dtype='float64', desc='frequencies')
358
- _numfreq = Int(desc='number of frequencies')
322
+ #: beamforming result
323
+ _ac = Any()
324
+ #: flag for beamforming result at frequency index
325
+ _fr = Any()
326
+ #: frequencies
327
+ _f = CArray(dtype='float64')
328
+ #: number of frequencies
329
+ _numfreq = Int()
359
330
 
360
331
  @cached_property
361
332
  def _get_digest(self):
362
333
  return digest(self)
363
334
 
364
335
  def _get_filecache(self):
365
- """Function collects cached results from file depending on
366
- global/local caching behaviour. Returns (None, None) if no cachefile/data
367
- exist and global caching mode is 'readonly'.
336
+ """
337
+ Function collects cached results from file depending on global/local caching behaviour.
338
+
339
+ Returns (None, None) if no cachefile/data exist and global caching mode is 'readonly'.
368
340
  """
369
341
  # print("get cachefile:", self.freq_data.basename)
370
342
  H5cache.get_cache_file(self, self.freq_data.basename)
@@ -419,7 +391,9 @@ class BeamformerBase(HasStrictTraits):
419
391
 
420
392
  @property_depends_on(['digest'])
421
393
  def _get_result(self):
422
- """Implements the :attr:`result` getter routine.
394
+ """
395
+ Implements the :attr:`result` getter routine.
396
+
423
397
  The beamforming result is either loaded or calculated.
424
398
  """
425
399
  # store locally for performance
@@ -439,20 +413,22 @@ class BeamformerBase(HasStrictTraits):
439
413
  else:
440
414
  # no caching or not activated, init numpy arrays
441
415
  if isinstance(self, BeamformerAdaptiveGrid):
442
- self._gpos = zeros((3, self.size), dtype=self.precision)
443
- ac = zeros((self._numfreq, self.size), dtype=self.precision)
416
+ self._gpos = np.zeros((3, self.size), dtype=self.precision)
417
+ ac = np.zeros((self._numfreq, self.size), dtype=self.precision)
444
418
  elif isinstance(self, BeamformerSODIX):
445
- ac = zeros((self._numfreq, self.steer.grid.size * self.steer.mics.num_mics), dtype=self.precision)
419
+ ac = np.zeros((self._numfreq, self.steer.grid.size * self.steer.mics.num_mics), dtype=self.precision)
446
420
  else:
447
- ac = zeros((self._numfreq, self.steer.grid.size), dtype=self.precision)
448
- fr = zeros(self._numfreq, dtype='int8')
421
+ ac = np.zeros((self._numfreq, self.steer.grid.size), dtype=self.precision)
422
+ fr = np.zeros(self._numfreq, dtype='int8')
449
423
  self._ac = ac
450
424
  self._fr = fr
451
425
  return LazyBfResult(self)
452
426
 
453
427
  def sig_loss_norm(self):
454
- """If the diagonal of the CSM is removed one has to handle the loss
455
- of signal energy --> Done via a normalization factor.
428
+ """
429
+ If the diagonal of the CSM is removed one has to handle the loss of signal energy.
430
+
431
+ Done via a normalization factor.
456
432
  """
457
433
  if not self.r_diag: # Full CSM --> no normalization needed
458
434
  normfactor = 1.0
@@ -464,28 +440,29 @@ class BeamformerBase(HasStrictTraits):
464
440
  return normfactor
465
441
 
466
442
  def _beamformer_params(self):
467
- """Manages the parameters for calling of the core beamformer functionality.
468
- This is a workaround to allow faster calculation and may change in the
469
- future.
443
+ """
444
+ Manages the parameters for calling of the core beamformer functionality.
445
+
446
+ This is a workaround to allow faster calculation and may change in the future.
470
447
 
471
448
  Returns
472
449
  -------
473
450
  - String containing the steering vector type
474
451
  - Function for frequency-dependent steering vector calculation
475
-
476
452
  """
477
453
  if type(self.steer) is SteeringVector: # for simple steering vector, use faster method
478
454
  param_type = self.steer.steer_type
479
455
 
480
456
  def param_steer_func(f):
481
- return (self.steer.r0, self.steer.rm, 2 * pi * f / self.steer.env.c)
457
+ return (self.steer.r0, self.steer.rm, 2 * np.pi * f / self.steer.env.c)
482
458
  else:
483
459
  param_type = 'custom'
484
460
  param_steer_func = self.steer.steer_vector
485
461
  return param_type, param_steer_func
486
462
 
487
463
  def _calc(self, ind):
488
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
464
+ """
465
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
489
466
 
490
467
  This is an internal helper function that is automatically called when
491
468
  accessing the beamformer's :attr:`result` or calling
@@ -500,14 +477,13 @@ class BeamformerBase(HasStrictTraits):
500
477
  Returns
501
478
  -------
502
479
  This method only returns values through :attr:`_ac` and :attr:`_fr`
503
-
504
480
  """
505
481
  f = self._f
506
482
  normfactor = self.sig_loss_norm()
507
483
  param_steer_type, steer_vector = self._beamformer_params()
508
484
  for i in ind:
509
485
  # print(f'compute{i}')
510
- csm = array(self.freq_data.csm[i], dtype='complex128')
486
+ csm = np.array(self.freq_data.csm[i], dtype='complex128')
511
487
  beamformerOutput = beamformerFreq(
512
488
  param_steer_type,
513
489
  self.r_diag,
@@ -516,13 +492,14 @@ class BeamformerBase(HasStrictTraits):
516
492
  csm,
517
493
  )[0]
518
494
  if self.r_diag: # set (unphysical) negative output values to 0
519
- indNegSign = sign(beamformerOutput) < 0
495
+ indNegSign = np.sign(beamformerOutput) < 0
520
496
  beamformerOutput[indNegSign] = 0.0
521
497
  self._ac[i] = beamformerOutput
522
498
  self._fr[i] = 1
523
499
 
524
500
  def synthetic(self, f, num=0):
525
- """Evaluates the beamforming result for an arbitrary frequency band.
501
+ """
502
+ Evaluates the beamforming result for an arbitrary frequency band.
526
503
 
527
504
  Parameters
528
505
  ----------
@@ -550,7 +527,6 @@ class BeamformerBase(HasStrictTraits):
550
527
  represented by a single frequency line depends on
551
528
  the :attr:`sampling frequency<acoular.base.SamplesGenerator.sample_freq>` and
552
529
  used :attr:`FFT block size<acoular.spectra.PowerSpectra.block_size>`.
553
-
554
530
  """
555
531
  res = self.result # trigger calculation
556
532
  freq = self.freq_data.fftfreq()
@@ -559,14 +535,14 @@ class BeamformerBase(HasStrictTraits):
559
535
 
560
536
  if num == 0:
561
537
  # single frequency line
562
- ind = searchsorted(freq, f)
538
+ ind = np.searchsorted(freq, f)
563
539
  if ind >= len(freq):
564
540
  warn(
565
541
  f'Queried frequency ({f:g} Hz) not in resolved frequency range. Returning zeros.',
566
542
  Warning,
567
543
  stacklevel=2,
568
544
  )
569
- h = zeros_like(res[0])
545
+ h = np.zeros_like(res[0])
570
546
  else:
571
547
  if freq[ind] != f:
572
548
  warn(
@@ -585,8 +561,8 @@ class BeamformerBase(HasStrictTraits):
585
561
  else:
586
562
  f1 = f * 2.0 ** (-0.5 / num)
587
563
  f2 = f * 2.0 ** (+0.5 / num)
588
- ind1 = searchsorted(freq, f1)
589
- ind2 = searchsorted(freq, f2)
564
+ ind1 = np.searchsorted(freq, f1)
565
+ ind2 = np.searchsorted(freq, f2)
590
566
  if ind1 == ind2:
591
567
  warn(
592
568
  f'Queried frequency band ({f1:g} to {f2:g} Hz) does not '
@@ -595,9 +571,9 @@ class BeamformerBase(HasStrictTraits):
595
571
  Warning,
596
572
  stacklevel=2,
597
573
  )
598
- h = zeros_like(res[0])
574
+ h = np.zeros_like(res[0])
599
575
  else:
600
- h = sum(res[ind1:ind2], 0)
576
+ h = np.sum(res[ind1:ind2], 0)
601
577
  if isinstance(self, BeamformerAdaptiveGrid):
602
578
  return h
603
579
  if isinstance(self, BeamformerSODIX):
@@ -605,7 +581,8 @@ class BeamformerBase(HasStrictTraits):
605
581
  return h.reshape(self.steer.grid.shape)
606
582
 
607
583
  def integrate(self, sector, frange=None, num=0):
608
- """Integrates result map over a given sector.
584
+ """
585
+ Integrates result map over a given sector.
609
586
 
610
587
  Parameters
611
588
  ----------
@@ -674,13 +651,13 @@ class BeamformerBase(HasStrictTraits):
674
651
  irange = (ind_low, ind_high)
675
652
  num = 0
676
653
  elif len(frange) == 2:
677
- irange = (searchsorted(self._f, frange[0]), searchsorted(self._f, frange[1]))
654
+ irange = (np.searchsorted(self._f, frange[0]), np.searchsorted(self._f, frange[1]))
678
655
  else:
679
656
  msg = 'Only a tuple of length 2 is allowed for frange if num==0'
680
657
  raise TypeError(
681
658
  msg,
682
659
  )
683
- h = zeros(num_freqs, dtype=float)
660
+ h = np.zeros(num_freqs, dtype=float)
684
661
  sl = slice(*irange)
685
662
  r = self.result[sl]
686
663
  for i in range(num_freqs)[sl]:
@@ -690,32 +667,30 @@ class BeamformerBase(HasStrictTraits):
690
667
  return h
691
668
  return self._f[sl], h[sl]
692
669
 
693
- h = zeros(len(frange), dtype=float)
670
+ h = np.zeros(len(frange), dtype=float)
694
671
  for i, f in enumerate(frange):
695
672
  h[i] = self.synthetic(f, num).reshape(gshape)[ind].sum()
696
673
  return h
697
674
 
698
675
 
699
676
  class BeamformerFunctional(BeamformerBase):
700
- """Functional beamforming algorithm.
677
+ """
678
+ Functional beamforming algorithm.
701
679
 
702
680
  See :cite:`Dougherty2014` for details.
703
681
  """
704
682
 
705
683
  #: Functional exponent, defaults to 1 (= Classic Beamforming).
706
- gamma = Float(1, desc='functional exponent')
684
+ gamma = Float(1)
707
685
 
708
686
  #: Functional Beamforming is only well defined for full CSM
709
- r_diag = Enum(False, desc='False, as Functional Beamformer is only well defined for the full CSM')
687
+ r_diag = Enum(False)
710
688
 
711
689
  #: Normalization factor in case of CSM diagonal removal. Defaults to 1.0 since Functional
712
690
  #: Beamforming is only well defined for full CSM.
713
- r_diag_norm = Enum(
714
- 1.0,
715
- desc='No normalization needed. Functional Beamforming is only well defined for full CSM.',
716
- )
691
+ r_diag_norm = Enum(1.0)
717
692
 
718
- # internal identifier
693
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
719
694
  digest = Property(depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['gamma'])
720
695
 
721
696
  @cached_property
@@ -723,7 +698,8 @@ class BeamformerFunctional(BeamformerBase):
723
698
  return digest(self)
724
699
 
725
700
  def _calc(self, ind):
726
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
701
+ """
702
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
727
703
 
728
704
  This is an internal helper function that is automatically called when
729
705
  accessing the beamformer's :attr:`result` or calling
@@ -738,7 +714,6 @@ class BeamformerFunctional(BeamformerBase):
738
714
  Returns
739
715
  -------
740
716
  This method only returns values through :attr:`_ac` and :attr:`_fr`
741
-
742
717
  """
743
718
  f = self._f
744
719
  normfactor = self.sig_loss_norm()
@@ -756,8 +731,8 @@ class BeamformerFunctional(BeamformerBase):
756
731
  # WATCH OUT: This doesn't really produce good results.
757
732
  # ==============================================================================
758
733
  csm = self.freq_data.csm[i]
759
- fill_diagonal(csm, 0)
760
- csmRoot = fractional_matrix_power(csm, 1.0 / self.gamma)
734
+ np.fill_diagonal(csm, 0)
735
+ csmRoot = spla.fractional_matrix_power(csm, 1.0 / self.gamma)
761
736
  beamformerOutput, steerNorm = beamformerFreq(
762
737
  param_steer_type,
763
738
  self.r_diag,
@@ -768,11 +743,11 @@ class BeamformerFunctional(BeamformerBase):
768
743
  beamformerOutput /= steerNorm # take normalized steering vec
769
744
 
770
745
  # set (unphysical) negative output values to 0
771
- indNegSign = sign(beamformerOutput) < 0
746
+ indNegSign = np.sign(beamformerOutput) < 0
772
747
  beamformerOutput[indNegSign] = 0.0
773
748
  else:
774
- eva = array(self.freq_data.eva[i], dtype='float64') ** (1.0 / self.gamma)
775
- eve = array(self.freq_data.eve[i], dtype='complex128')
749
+ eva = np.array(self.freq_data.eva[i], dtype='float64') ** (1.0 / self.gamma)
750
+ eve = np.array(self.freq_data.eve[i], dtype='complex128')
776
751
  beamformerOutput, steerNorm = beamformerFreq(
777
752
  param_steer_type,
778
753
  self.r_diag,
@@ -788,24 +763,23 @@ class BeamformerFunctional(BeamformerBase):
788
763
 
789
764
 
790
765
  class BeamformerCapon(BeamformerBase):
791
- """Beamforming using the Capon (Mininimum Variance) algorithm.
766
+ """
767
+ Beamforming using the Capon (Mininimum Variance) algorithm.
792
768
 
793
769
  See :cite:`Capon1969` for details.
794
770
  """
795
771
 
796
- # Boolean flag, if 'True', the main diagonal is removed before beamforming;
797
- # for Capon beamforming r_diag is set to 'False'.
798
- r_diag = Enum(False, desc='removal of diagonal')
772
+ #: Boolean flag, if ``True``, the main diagonal is removed before beamforming;
773
+ #: for Capon beamforming :attr:`r_diag` is set to ``False``.
774
+ r_diag = Enum(False)
799
775
 
800
- #: Normalization factor in case of CSM diagonal removal. Defaults to 1.0 since Beamformer Capon
801
- #: is only well defined for full CSM.
802
- r_diag_norm = Enum(
803
- 1.0,
804
- desc='No normalization. BeamformerCapon is only well defined for full CSM.',
805
- )
776
+ #: Normalization factor in case of CSM diagonal removal.
777
+ #: Defaults to ``1.0`` since Beamformer Capon is only well defined for full CSM.
778
+ r_diag_norm = Enum(1.0)
806
779
 
807
780
  def _calc(self, ind):
808
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
781
+ """
782
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
809
783
 
810
784
  This is an internal helper function that is automatically called when
811
785
  accessing the beamformer's :attr:`result` or calling
@@ -820,34 +794,35 @@ class BeamformerCapon(BeamformerBase):
820
794
  Returns
821
795
  -------
822
796
  This method only returns values through :attr:`_ac` and :attr:`_fr`
823
-
824
797
  """
825
798
  f = self._f
826
799
  nMics = self.freq_data.num_channels
827
800
  normfactor = self.sig_loss_norm() * nMics**2
828
801
  param_steer_type, steer_vector = self._beamformer_params()
829
802
  for i in ind:
830
- csm = array(inv(array(self.freq_data.csm[i], dtype='complex128')), order='C')
803
+ csm = np.array(spla.inv(np.array(self.freq_data.csm[i], dtype='complex128')), order='C')
831
804
  beamformerOutput = beamformerFreq(param_steer_type, self.r_diag, normfactor, steer_vector(f[i]), csm)[0]
832
805
  self._ac[i] = 1.0 / beamformerOutput
833
806
  self._fr[i] = 1
834
807
 
835
808
 
836
809
  class BeamformerEig(BeamformerBase):
837
- """Beamforming using eigenvalue and eigenvector techniques.
810
+ """
811
+ Beamforming using eigenvalue and eigenvector techniques.
838
812
 
839
813
  See :cite:`Sarradj2005` for details.
840
814
  """
841
815
 
842
816
  #: Number of component to calculate:
843
- #: 0 (smallest) ... :attr:`~acoular.base.SamplesGenerator.num_channels`-1;
844
- #: defaults to -1, i.e. num_channels-1
845
- n = Int(-1, desc='No. of eigenvalue')
817
+ #: ``0`` (smallest) ... :attr:`~acoular.base.SamplesGenerator.num_channels`-1;
818
+ #: defaults to ``-1``, i.e. :attr:`~acoular.base.SamplesGenerator.num_channels`-1.
819
+ n = Int(-1)
846
820
 
847
821
  # Actual component to calculate, internal, readonly.
848
- na = Property(desc='No. of eigenvalue')
822
+ #: No. of eigenvalue
823
+ na = Property()
849
824
 
850
- # internal identifier
825
+ #: A uniquernal identifier for the beamformer, based on its properties. (read-only)
851
826
  digest = Property(depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['n'])
852
827
 
853
828
  @cached_property
@@ -863,7 +838,8 @@ class BeamformerEig(BeamformerBase):
863
838
  return min(nm - 1, na)
864
839
 
865
840
  def _calc(self, ind):
866
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
841
+ """
842
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
867
843
 
868
844
  This is an internal helper function that is automatically called when
869
845
  accessing the beamformer's :attr:`result` or calling
@@ -878,15 +854,14 @@ class BeamformerEig(BeamformerBase):
878
854
  Returns
879
855
  -------
880
856
  This method only returns values through :attr:`_ac` and :attr:`_fr`
881
-
882
857
  """
883
858
  f = self._f
884
859
  na = int(self.na) # eigenvalue taken into account
885
860
  normfactor = self.sig_loss_norm()
886
861
  param_steer_type, steer_vector = self._beamformer_params()
887
862
  for i in ind:
888
- eva = array(self.freq_data.eva[i], dtype='float64')
889
- eve = array(self.freq_data.eve[i], dtype='complex128')
863
+ eva = np.array(self.freq_data.eva[i], dtype='float64')
864
+ eve = np.array(self.freq_data.eve[i], dtype='complex128')
890
865
  beamformerOutput = beamformerFreq(
891
866
  param_steer_type,
892
867
  self.r_diag,
@@ -895,35 +870,35 @@ class BeamformerEig(BeamformerBase):
895
870
  (eva[na : na + 1], eve[:, na : na + 1]),
896
871
  )[0]
897
872
  if self.r_diag: # set (unphysical) negative output values to 0
898
- indNegSign = sign(beamformerOutput) < 0
873
+ indNegSign = np.sign(beamformerOutput) < 0
899
874
  beamformerOutput[indNegSign] = 0
900
875
  self._ac[i] = beamformerOutput
901
876
  self._fr[i] = 1
902
877
 
903
878
 
904
879
  class BeamformerMusic(BeamformerEig):
905
- """Beamforming using the MUSIC algorithm.
880
+ """
881
+ Beamforming using the MUSIC algorithm.
906
882
 
907
883
  See :cite:`Schmidt1986` for details.
908
884
  """
909
885
 
910
- # Boolean flag, if 'True', the main diagonal is removed before beamforming;
911
- # for MUSIC beamforming r_diag is set to 'False'.
912
- r_diag = Enum(False, desc='removal of diagonal')
886
+ #: Boolean flag, if ``True``, the main diagonal is removed before beamforming;
887
+ #: for MUSIC beamforming :attr:`r_diag` is set to ``False``.
888
+ r_diag = Enum(False)
913
889
 
914
890
  #: Normalization factor in case of CSM diagonal removal. Defaults to 1.0 since BeamformerMusic
915
891
  #: is only well defined for full CSM.
916
- r_diag_norm = Enum(
917
- 1.0,
918
- desc='No normalization. BeamformerMusic is only well defined for full CSM.',
919
- )
892
+ r_diag_norm = Enum(1.0)
920
893
 
921
894
  # assumed number of sources, should be set to a value not too small
922
895
  # defaults to 1
923
- n = Int(1, desc='assumed number of sources')
896
+ #: assumed number of sources
897
+ n = Int(1)
924
898
 
925
899
  def _calc(self, ind):
926
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
900
+ """
901
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
927
902
 
928
903
  This is an internal helper function that is automatically called when
929
904
  accessing the beamformer's :attr:`result` or calling
@@ -938,7 +913,6 @@ class BeamformerMusic(BeamformerEig):
938
913
  Returns
939
914
  -------
940
915
  This method only returns values through :attr:`_ac` and :attr:`_fr`
941
-
942
916
  """
943
917
  f = self._f
944
918
  nMics = self.freq_data.num_channels
@@ -946,8 +920,8 @@ class BeamformerMusic(BeamformerEig):
946
920
  normfactor = self.sig_loss_norm() * nMics**2
947
921
  param_steer_type, steer_vector = self._beamformer_params()
948
922
  for i in ind:
949
- eva = array(self.freq_data.eva[i], dtype='float64')
950
- eve = array(self.freq_data.eve[i], dtype='complex128')
923
+ eva = np.array(self.freq_data.eva[i], dtype='float64')
924
+ eve = np.array(self.freq_data.eve[i], dtype='complex128')
951
925
  beamformerOutput = beamformerFreq(
952
926
  param_steer_type,
953
927
  self.r_diag,
@@ -960,7 +934,8 @@ class BeamformerMusic(BeamformerEig):
960
934
 
961
935
 
962
936
  class PointSpreadFunction(HasStrictTraits):
963
- """The point spread function.
937
+ """
938
+ The point spread function.
964
939
 
965
940
  This class provides tools to calculate the PSF depending on the used
966
941
  microphone geometry, focus grid, flow environment, etc.
@@ -968,16 +943,15 @@ class PointSpreadFunction(HasStrictTraits):
968
943
  the aberrations when using simple delay-and-sum beamforming.
969
944
  """
970
945
 
971
- # Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
972
- # that contains information about the steering vector. This is a private trait.
973
- # Do not set this directly, use `steer` trait instead.
946
+ #: Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
947
+ #: that contains information about the steering vector. This is a private trait.
948
+ #: Do not set this directly, use :attr:`steer` trait instead.
974
949
  steer = Instance(SteeringVector, args=())
975
950
 
976
951
  #: Indices of grid points to calculate the PSF for.
977
952
  grid_indices = CArray(
978
953
  dtype=int,
979
- value=array([]),
980
- desc='indices of grid points for psf',
954
+ value=np.array([]),
981
955
  ) # value=array([]), value=self.steer.grid.pos(),
982
956
 
983
957
  #: Flag that defines how to calculate and store the point spread function
@@ -991,21 +965,21 @@ class PointSpreadFunction(HasStrictTraits):
991
965
  #: (useful if not all PSFs are needed, as with :class:`CLEAN<BeamformerClean>`)
992
966
  #: * 'readonly': Do not attempt to calculate the PSF since it should already be cached (useful
993
967
  #: if multiple processes have to access the cache file)
994
- calcmode = Enum('single', 'block', 'full', 'readonly', desc='mode of calculation / storage')
968
+ calcmode = Enum('single', 'block', 'full', 'readonly')
995
969
 
996
970
  #: Floating point precision of property psf. Corresponding to numpy dtypes. Default = 64 Bit.
997
- precision = Enum('float64', 'float32', desc='precision (32/64 Bit) of result, corresponding to numpy dtypes')
971
+ precision = Enum('float64', 'float32')
998
972
 
999
973
  #: The actual point spread function.
1000
- psf = Property(desc='point spread function')
974
+ psf = Property()
1001
975
 
1002
976
  #: Frequency to evaluate the PSF for; defaults to 1.0.
1003
- freq = Float(1.0, desc='frequency')
977
+ freq = Float(1.0)
1004
978
 
1005
979
  # hdf5 cache file
1006
980
  h5f = Instance(H5CacheFileBase, transient=True)
1007
981
 
1008
- # internal identifier
982
+ #: A unique identifier for the object, based on its properties. (read-only)
1009
983
  digest = Property(depends_on=['steer.digest', 'precision'], cached=True)
1010
984
 
1011
985
  @cached_property
@@ -1013,9 +987,10 @@ class PointSpreadFunction(HasStrictTraits):
1013
987
  return digest(self)
1014
988
 
1015
989
  def _get_filecache(self):
1016
- """Function collects cached results from file depending on
1017
- global/local caching behaviour. Returns (None, None) if no cachefile/data
1018
- exist and global caching mode is 'readonly'.
990
+ """
991
+ Function collects cached results from file depending on global/local caching behaviour.
992
+
993
+ Returns (None, None) if no cachefile/data exist and global caching mode is 'readonly'.
1019
994
  """
1020
995
  filename = 'psf' + self.digest
1021
996
  nodename = (f'Hz_{self.freq:.2f}').replace('.', '_')
@@ -1047,12 +1022,14 @@ class PointSpreadFunction(HasStrictTraits):
1047
1022
  return (ac, gp)
1048
1023
 
1049
1024
  def _get_psf(self):
1050
- """Implements the :attr:`psf` getter routine.
1025
+ """
1026
+ Implements the :attr:`psf` getter routine.
1027
+
1051
1028
  The point spread function is either loaded or calculated.
1052
1029
  """
1053
1030
  gs = self.steer.grid.size
1054
1031
  if not self.grid_indices.size:
1055
- self.grid_indices = arange(gs)
1032
+ self.grid_indices = np.arange(gs)
1056
1033
 
1057
1034
  if config.global_caching != 'none':
1058
1035
  # print("get filecache..")
@@ -1075,13 +1052,13 @@ class PointSpreadFunction(HasStrictTraits):
1075
1052
  # print("cached results are complete! return.")
1076
1053
  return ac[:, self.grid_indices]
1077
1054
  # print("no caching, calculate result")
1078
- ac = zeros((gs, gs), dtype=self.precision)
1079
- gp = zeros((gs,), dtype='int8')
1055
+ ac = np.zeros((gs, gs), dtype=self.precision)
1056
+ gp = np.zeros((gs,), dtype='int8')
1080
1057
  self.calc_psf(ac, gp)
1081
1058
  else: # no caching activated
1082
1059
  # print("no caching activated, calculate result")
1083
- ac = zeros((gs, gs), dtype=self.precision)
1084
- gp = zeros((gs,), dtype='int8')
1060
+ ac = np.zeros((gs, gs), dtype=self.precision)
1061
+ gp = np.zeros((gs,), dtype='int8')
1085
1062
  self.calc_psf(ac, gp)
1086
1063
  return ac[:, self.grid_indices]
1087
1064
 
@@ -1090,7 +1067,7 @@ class PointSpreadFunction(HasStrictTraits):
1090
1067
  if self.calcmode != 'full':
1091
1068
  # calc_ind has the form [True, True, False, True], except
1092
1069
  # when it has only 1 entry (value True/1 would be ambiguous)
1093
- calc_ind = [0] if self.grid_indices.size == 1 else invert(gp[:][self.grid_indices])
1070
+ calc_ind = [0] if self.grid_indices.size == 1 else np.invert(gp[:][self.grid_indices])
1094
1071
 
1095
1072
  # get indices which have the value True = not yet calculated
1096
1073
  g_ind_calc = self.grid_indices[calc_ind]
@@ -1101,7 +1078,7 @@ class PointSpreadFunction(HasStrictTraits):
1101
1078
  gp[ind] = 1
1102
1079
  elif self.calcmode == 'full': # calculate all psfs in one go
1103
1080
  gp[:] = 1
1104
- ac[:] = self._psf_call(arange(self.steer.grid.size))
1081
+ ac[:] = self._psf_call(np.arange(self.steer.grid.size))
1105
1082
  else: # 'block' # calculate selected psfs in one go
1106
1083
  hh = self._psf_call(g_ind_calc)
1107
1084
  for indh, ind in enumerate(g_ind_calc):
@@ -1110,7 +1087,8 @@ class PointSpreadFunction(HasStrictTraits):
1110
1087
  indh += 1
1111
1088
 
1112
1089
  def _psf_call(self, ind):
1113
- """Manages the calling of the core psf functionality.
1090
+ """
1091
+ Manages the calling of the core psf functionality.
1114
1092
 
1115
1093
  Parameters
1116
1094
  ----------
@@ -1127,38 +1105,48 @@ class PointSpreadFunction(HasStrictTraits):
1127
1105
  self.steer.steer_type,
1128
1106
  self.steer.r0,
1129
1107
  self.steer.rm,
1130
- 2 * pi * self.freq / self.steer.env.c,
1108
+ 2 * np.pi * self.freq / self.steer.env.c,
1131
1109
  ind,
1132
1110
  self.precision,
1133
1111
  )
1134
1112
  else:
1135
1113
  # for arbitrary steering sectors, use general calculation. there is a version of this in
1136
1114
  # fastFuncs, may be used later after runtime testing and debugging
1137
- product = dot(self.steer.steer_vector(self.freq).conj(), self.steer.transfer(self.freq, ind).T)
1115
+ product = np.dot(self.steer.steer_vector(self.freq).conj(), self.steer.transfer(self.freq, ind).T)
1138
1116
  result = (product * product.conj()).real
1139
1117
  return result
1140
1118
 
1141
1119
 
1142
1120
  class BeamformerDamas(BeamformerBase):
1143
- """DAMAS deconvolution algorithm.
1121
+ """
1122
+ DAMAS deconvolution algorithm.
1144
1123
 
1145
1124
  See :cite:`Brooks2006` for details.
1146
1125
  """
1147
1126
 
1148
1127
  #: The floating-number-precision of the PSFs. Default is 64 bit.
1149
- psf_precision = Enum('float64', 'float32', desc='precision of PSF')
1128
+ psf_precision = Enum('float64', 'float32')
1150
1129
 
1151
1130
  #: Number of iterations, defaults to 100.
1152
- n_iter = Int(100, desc='number of iterations')
1131
+ n_iter = Int(100)
1153
1132
 
1154
1133
  #: Damping factor in modified gauss-seidel
1155
- damp = Float(1.0, desc='damping factor in modified gauss-seidel-DAMAS-approach')
1134
+ damp = Float(1.0)
1156
1135
 
1157
- #: Flag that defines how to calculate and store the point spread function,
1158
- #: defaults to 'full'. See :attr:`PointSpreadFunction.calcmode` for details.
1159
- calcmode = Enum('full', 'single', 'block', 'readonly', desc='mode of psf calculation / storage')
1136
+ #: Flag that defines how to calculate and store the point spread function
1137
+ #: defaults to 'single'.
1138
+ #:
1139
+ #: * 'full': Calculate the full PSF (for all grid points) in one go (should be used if the PSF
1140
+ #: at all grid points is needed, as with :class:`DAMAS<BeamformerDamas>`)
1141
+ #: * 'single': Calculate the PSF for the grid points defined by :attr:`grid_indices`, one by one
1142
+ #: (useful if not all PSFs are needed, as with :class:`CLEAN<BeamformerClean>`)
1143
+ #: * 'block': Calculate the PSF for the grid points defined by :attr:`grid_indices`, in one go
1144
+ #: (useful if not all PSFs are needed, as with :class:`CLEAN<BeamformerClean>`)
1145
+ #: * 'readonly': Do not attempt to calculate the PSF since it should already be cached (useful
1146
+ #: if multiple processes have to access the cache file)
1147
+ calcmode = Enum('full', 'single', 'block', 'readonly')
1160
1148
 
1161
- # internal identifier
1149
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1162
1150
  digest = Property(
1163
1151
  depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['n_iter', 'damp', 'psf_precision'],
1164
1152
  )
@@ -1168,7 +1156,8 @@ class BeamformerDamas(BeamformerBase):
1168
1156
  return digest(self)
1169
1157
 
1170
1158
  def _calc(self, ind):
1171
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1159
+ """
1160
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1172
1161
 
1173
1162
  This is an internal helper function that is automatically called when
1174
1163
  accessing the beamformer's :attr:`result` or calling
@@ -1183,14 +1172,13 @@ class BeamformerDamas(BeamformerBase):
1183
1172
  Returns
1184
1173
  -------
1185
1174
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1186
-
1187
1175
  """
1188
1176
  f = self._f
1189
1177
  normfactor = self.sig_loss_norm()
1190
1178
  p = PointSpreadFunction(steer=self.steer, calcmode=self.calcmode, precision=self.psf_precision)
1191
1179
  param_steer_type, steer_vector = self._beamformer_params()
1192
1180
  for i in ind:
1193
- csm = array(self.freq_data.csm[i], dtype='complex128')
1181
+ csm = np.array(self.freq_data.csm[i], dtype='complex128')
1194
1182
  y = beamformerFreq(
1195
1183
  param_steer_type,
1196
1184
  self.r_diag,
@@ -1199,7 +1187,7 @@ class BeamformerDamas(BeamformerBase):
1199
1187
  csm,
1200
1188
  )[0]
1201
1189
  if self.r_diag: # set (unphysical) negative output values to 0
1202
- indNegSign = sign(y) < 0
1190
+ indNegSign = np.sign(y) < 0
1203
1191
  y[indNegSign] = 0.0
1204
1192
  x = y.copy()
1205
1193
  p.freq = f[i]
@@ -1209,12 +1197,13 @@ class BeamformerDamas(BeamformerBase):
1209
1197
  self._fr[i] = 1
1210
1198
 
1211
1199
 
1212
- @deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
1213
1200
  class BeamformerDamasPlus(BeamformerDamas):
1214
- """DAMAS deconvolution :cite:`Brooks2006` for solving the system of equations, instead of the
1215
- original Gauss-Seidel iterations, this class employs the NNLS or linear programming solvers from
1216
- scipy.optimize or one of several optimization algorithms from the scikit-learn module. Needs
1217
- a-priori delay-and-sum beamforming (:class:`BeamformerBase`).
1201
+ """
1202
+ DAMAS deconvolution :cite:`Brooks2006` for solving the system of equations.
1203
+
1204
+ Instead of the original Gauss-Seidel iterations, this class employs the NNLS or linear
1205
+ programming solvers from scipy.optimize or one of several optimization algorithms from the
1206
+ scikit-learn module. Needs a-priori delay-and-sum beamforming (:class:`BeamformerBase`).
1218
1207
  """
1219
1208
 
1220
1209
  #: Type of fit method to be used ('LassoLars',
@@ -1222,25 +1211,25 @@ class BeamformerDamasPlus(BeamformerDamas):
1222
1211
  #: These methods are implemented in
1223
1212
  #: the `scikit-learn <http://scikit-learn.org/stable/user_guide.html>`_
1224
1213
  #: module or within scipy.optimize respectively.
1225
- method = Enum('NNLS', 'LP', 'LassoLars', 'OMPCV', desc='method used for solving deconvolution problem')
1214
+ method = Enum('NNLS', 'LP', 'LassoLars', 'OMPCV')
1226
1215
 
1227
1216
  #: Weight factor for LassoLars method,
1228
1217
  #: defaults to 0.0.
1229
1218
  # (Values in the order of 10^⁻9 should produce good results.)
1230
- alpha = Range(0.0, 1.0, 0.0, desc='Lasso weight factor')
1219
+ alpha = Range(0.0, 1.0, 0.0)
1231
1220
 
1232
1221
  #: Maximum number of iterations,
1233
1222
  #: tradeoff between speed and precision;
1234
1223
  #: defaults to 500
1235
- n_iter = Int(500, desc='maximum number of iterations')
1224
+ n_iter = Int(500)
1236
1225
 
1237
1226
  #: Unit multiplier for evaluating, e.g., nPa instead of Pa.
1238
1227
  #: Values are converted back before returning.
1239
1228
  #: Temporary conversion may be necessary to not reach machine epsilon
1240
1229
  #: within fitting method algorithms. Defaults to 1e9.
1241
- unit_mult = Float(1e9, desc='unit multiplier')
1230
+ unit_mult = Float(1e9)
1242
1231
 
1243
- # internal identifier
1232
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1244
1233
  digest = Property(
1245
1234
  depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['alpha', 'method', 'n_iter', 'unit_mult'],
1246
1235
  )
@@ -1250,7 +1239,8 @@ class BeamformerDamasPlus(BeamformerDamas):
1250
1239
  return digest(self)
1251
1240
 
1252
1241
  def _calc(self, ind):
1253
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1242
+ """
1243
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1254
1244
 
1255
1245
  This is an internal helper function that is automatically called when
1256
1246
  accessing the beamformer's :attr:`result` or calling
@@ -1265,7 +1255,6 @@ class BeamformerDamasPlus(BeamformerDamas):
1265
1255
  Returns
1266
1256
  -------
1267
1257
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1268
-
1269
1258
  """
1270
1259
  f = self._f
1271
1260
  p = PointSpreadFunction(steer=self.steer, calcmode=self.calcmode, precision=self.psf_precision)
@@ -1273,7 +1262,7 @@ class BeamformerDamasPlus(BeamformerDamas):
1273
1262
  normfactor = self.sig_loss_norm()
1274
1263
  param_steer_type, steer_vector = self._beamformer_params()
1275
1264
  for i in ind:
1276
- csm = array(self.freq_data.csm[i], dtype='complex128')
1265
+ csm = np.array(self.freq_data.csm[i], dtype='complex128')
1277
1266
  y = beamformerFreq(
1278
1267
  param_steer_type,
1279
1268
  self.r_diag,
@@ -1282,7 +1271,7 @@ class BeamformerDamasPlus(BeamformerDamas):
1282
1271
  csm,
1283
1272
  )[0]
1284
1273
  if self.r_diag: # set (unphysical) negative output values to 0
1285
- indNegSign = sign(y) < 0
1274
+ indNegSign = np.sign(y) < 0
1286
1275
  y[indNegSign] = 0.0
1287
1276
  y *= unit
1288
1277
  p.freq = f[i]
@@ -1313,7 +1302,7 @@ class BeamformerDamasPlus(BeamformerDamas):
1313
1302
  # pipeline approach with StandardScaler does scale in a different way, thus we
1314
1303
  # monkeypatch the code and normalize ourselves to make results the same over
1315
1304
  # different sklearn versions
1316
- norms = norm(psf, axis=0)
1305
+ norms = spla.norm(psf, axis=0)
1317
1306
  # get rid of annoying sklearn warnings that appear
1318
1307
  # for sklearn<1.2 despite any settings
1319
1308
  with warnings.catch_warnings():
@@ -1326,7 +1315,8 @@ class BeamformerDamasPlus(BeamformerDamas):
1326
1315
 
1327
1316
 
1328
1317
  class BeamformerOrth(BeamformerBase):
1329
- """Orthogonal deconvolution algorithm.
1318
+ """
1319
+ Orthogonal deconvolution algorithm.
1330
1320
 
1331
1321
  See :cite:`Sarradj2010` for details.
1332
1322
  New faster implementation without explicit (:class:`BeamformerEig`).
@@ -1334,15 +1324,15 @@ class BeamformerOrth(BeamformerBase):
1334
1324
 
1335
1325
  #: List of components to consider, use this to directly set the eigenvalues
1336
1326
  #: used in the beamformer. Alternatively, set :attr:`n`.
1337
- eva_list = CArray(dtype=int, value=array([-1]), desc='components')
1327
+ eva_list = CArray(dtype=int, value=np.array([-1]))
1338
1328
 
1339
- #: Number of components to consider, defaults to 1. If set,
1329
+ #: Number of components to consider, defaults to ``1``. If set,
1340
1330
  #: :attr:`eva_list` will contain
1341
1331
  #: the indices of the n largest eigenvalues. Setting :attr:`eva_list`
1342
1332
  #: afterwards will override this value.
1343
1333
  n = Int(1)
1344
1334
 
1345
- # internal identifier
1335
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1346
1336
  digest = Property(
1347
1337
  depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['eva_list'],
1348
1338
  )
@@ -1351,13 +1341,14 @@ class BeamformerOrth(BeamformerBase):
1351
1341
  def _get_digest(self):
1352
1342
  return digest(self)
1353
1343
 
1354
- @on_trait_change('n')
1355
- def set_eva_list(self):
1344
+ @observe('n')
1345
+ def _update_eva_list(self, event): # noqa ARG002
1356
1346
  """Sets the list of eigenvalues to consider."""
1357
- self.eva_list = arange(-1, -1 - self.n, -1)
1347
+ self.eva_list = np.arange(-1, -1 - self.n, -1)
1358
1348
 
1359
1349
  def _calc(self, ind):
1360
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1350
+ """
1351
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1361
1352
 
1362
1353
  This is an internal helper function that is automatically called when
1363
1354
  accessing the beamformer's :attr:`result` or calling
@@ -1372,30 +1363,29 @@ class BeamformerOrth(BeamformerBase):
1372
1363
  Returns
1373
1364
  -------
1374
1365
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1375
-
1376
1366
  """
1377
1367
  f = self._f
1378
1368
  num_channels = self.freq_data.num_channels
1379
1369
  normfactor = self.sig_loss_norm()
1380
1370
  param_steer_type, steer_vector = self._beamformer_params()
1381
1371
  for i in ind:
1382
- eva = array(self.freq_data.eva[i], dtype='float64')
1383
- eve = array(self.freq_data.eve[i], dtype='complex128')
1372
+ eva = np.array(self.freq_data.eva[i], dtype='float64')
1373
+ eve = np.array(self.freq_data.eve[i], dtype='complex128')
1384
1374
  for n in self.eva_list:
1385
1375
  beamformerOutput = beamformerFreq(
1386
1376
  param_steer_type,
1387
1377
  self.r_diag,
1388
1378
  normfactor,
1389
1379
  steer_vector(f[i]),
1390
- (ones(1), eve[:, n].reshape((-1, 1))),
1380
+ (np.ones(1), eve[:, n].reshape((-1, 1))),
1391
1381
  )[0]
1392
1382
  self._ac[i, beamformerOutput.argmax()] += eva[n] / num_channels
1393
1383
  self._fr[i] = 1
1394
1384
 
1395
1385
 
1396
- @deprecated_alias({'n': 'n_iter'}, removal_version='25.10')
1397
1386
  class BeamformerCleansc(BeamformerBase):
1398
- """CLEAN-SC deconvolution algorithm.
1387
+ """
1388
+ CLEAN-SC deconvolution algorithm.
1399
1389
 
1400
1390
  See :cite:`Sijtsma2007` for details.
1401
1391
  Classic delay-and-sum beamforming is already included.
@@ -1403,18 +1393,18 @@ class BeamformerCleansc(BeamformerBase):
1403
1393
 
1404
1394
  #: no of CLEAN-SC iterations
1405
1395
  #: defaults to 0, i.e. automatic (max 2*num_channels)
1406
- n_iter = Int(0, desc='no of iterations')
1396
+ n_iter = Int(0)
1407
1397
 
1408
1398
  #: iteration damping factor
1409
1399
  #: defaults to 0.6
1410
- damp = Range(0.01, 1.0, 0.6, desc='damping factor')
1400
+ damp = Range(0.01, 1.0, 0.6)
1411
1401
 
1412
1402
  #: iteration stop criterion for automatic detection
1413
1403
  #: iteration stops if power[i]>power[i-stopn]
1414
1404
  #: defaults to 3
1415
- stopn = Int(3, desc='stop criterion index')
1405
+ stopn = Int(3)
1416
1406
 
1417
- # internal identifier
1407
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1418
1408
  digest = Property(depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['n_iter', 'damp', 'stopn'])
1419
1409
 
1420
1410
  @cached_property
@@ -1422,7 +1412,8 @@ class BeamformerCleansc(BeamformerBase):
1422
1412
  return digest(self)
1423
1413
 
1424
1414
  def _calc(self, ind):
1425
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1415
+ """
1416
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1426
1417
 
1427
1418
  This is an internal helper function that is automatically called when
1428
1419
  accessing the beamformer's :attr:`result` or calling
@@ -1437,18 +1428,17 @@ class BeamformerCleansc(BeamformerBase):
1437
1428
  Returns
1438
1429
  -------
1439
1430
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1440
-
1441
1431
  """
1442
1432
  f = self._f
1443
1433
  normfactor = self.sig_loss_norm()
1444
1434
  num_channels = self.freq_data.num_channels
1445
- result = zeros((self.steer.grid.size), 'f')
1435
+ result = np.zeros((self.steer.grid.size), 'f')
1446
1436
  J = num_channels * 2 if not self.n_iter else self.n_iter
1447
- powers = zeros(J, 'd')
1437
+ powers = np.zeros(J, 'd')
1448
1438
 
1449
1439
  param_steer_type, steer_vector = self._beamformer_params()
1450
1440
  for i in ind:
1451
- csm = array(self.freq_data.csm[i], dtype='complex128', copy=1)
1441
+ csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
1452
1442
  # h = self.steer._beamformerCall(f[i], self.r_diag, normfactor, (csm,))[0]
1453
1443
  h = beamformerFreq(param_steer_type, self.r_diag, normfactor, steer_vector(f[i]), csm)[0]
1454
1444
  # CLEANSC Iteration
@@ -1459,25 +1449,25 @@ class BeamformerCleansc(BeamformerBase):
1459
1449
  result[xi_max] += self.damp * hmax
1460
1450
  if j > self.stopn and hmax > powers[j - self.stopn]:
1461
1451
  break
1462
- wmax = self.steer.steer_vector(f[i], xi_max) * sqrt(normfactor)
1452
+ wmax = self.steer.steer_vector(f[i], xi_max) * np.sqrt(normfactor)
1463
1453
  wmax = wmax[0].conj() # as old code worked with conjugated csm..should be updated
1464
1454
  hh = wmax.copy()
1465
- D1 = dot(csm.T - diag(diag(csm)), wmax) / hmax
1455
+ D1 = np.dot(csm.T - np.diag(np.diag(csm)), wmax) / hmax
1466
1456
  ww = wmax.conj() * wmax
1467
- for _m in range(20):
1457
+ for _ in range(20):
1468
1458
  H = hh.conj() * hh
1469
- hh = (D1 + H * wmax) / sqrt(1 + dot(ww, H))
1470
- hh = hh[:, newaxis]
1459
+ hh = (D1 + H * wmax) / np.sqrt(1 + np.dot(ww, H))
1460
+ hh = hh[:, np.newaxis]
1471
1461
  csm1 = hmax * (hh * hh.conj().T)
1472
1462
 
1473
1463
  # h1 = self.steer._beamformerCall(f[i], self.r_diag, normfactor, \
1474
- # (array((hmax, ))[newaxis, :], hh[newaxis, :].conjugate()))[0]
1464
+ # (np.array((hmax, ))[np.newaxis, :], hh[np.newaxis, :].conjugate()))[0]
1475
1465
  h1 = beamformerFreq(
1476
1466
  param_steer_type,
1477
1467
  self.r_diag,
1478
1468
  normfactor,
1479
1469
  steer_vector(f[i]),
1480
- (array((hmax,)), hh.conj()),
1470
+ (np.array((hmax,)), hh.conj()),
1481
1471
  )[0]
1482
1472
  h -= self.damp * h1
1483
1473
  csm -= self.damp * csm1.T # transpose(0,2,1)
@@ -1486,25 +1476,38 @@ class BeamformerCleansc(BeamformerBase):
1486
1476
 
1487
1477
 
1488
1478
  class BeamformerClean(BeamformerBase):
1489
- """CLEAN deconvolution algorithm.
1479
+ """
1480
+ CLEAN deconvolution algorithm.
1490
1481
 
1491
1482
  See :cite:`Hoegbom1974` for details.
1492
1483
  """
1493
1484
 
1494
1485
  #: The floating-number-precision of the PSFs. Default is 64 bit.
1495
- psf_precision = Enum('float64', 'float32', desc='precision of PSF.')
1486
+ psf_precision = Enum('float64', 'float32')
1496
1487
 
1497
1488
  # iteration damping factor
1498
1489
  # defaults to 0.6
1499
- damp = Range(0.01, 1.0, 0.6, desc='damping factor')
1490
+ #: damping factor
1491
+ damp = Range(0.01, 1.0, 0.6)
1500
1492
 
1501
1493
  # max number of iterations
1502
- n_iter = Int(100, desc='maximum number of iterations')
1494
+ #: maximum number of iterations
1495
+ n_iter = Int(100)
1503
1496
 
1504
- # how to calculate and store the psf
1505
- calcmode = Enum('block', 'full', 'single', 'readonly', desc='mode of psf calculation / storage')
1497
+ #: Flag that defines how to calculate and store the point spread function
1498
+ #: defaults to 'single'.
1499
+ #:
1500
+ #: * 'full': Calculate the full PSF (for all grid points) in one go (should be used if the PSF
1501
+ #: at all grid points is needed, as with :class:`DAMAS<BeamformerDamas>`)
1502
+ #: * 'single': Calculate the PSF for the grid points defined by :attr:`grid_indices`, one by one
1503
+ #: (useful if not all PSFs are needed, as with :class:`CLEAN<BeamformerClean>`)
1504
+ #: * 'block': Calculate the PSF for the grid points defined by :attr:`grid_indices`, in one go
1505
+ #: (useful if not all PSFs are needed, as with :class:`CLEAN<BeamformerClean>`)
1506
+ #: * 'readonly': Do not attempt to calculate the PSF since it should already be cached (useful
1507
+ #: if multiple processes have to access the cache file)
1508
+ calcmode = Enum('block', 'full', 'single', 'readonly')
1506
1509
 
1507
- # internal identifier
1510
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1508
1511
  digest = Property(
1509
1512
  depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['n_iter', 'damp', 'psf_precision'],
1510
1513
  )
@@ -1529,7 +1532,6 @@ class BeamformerClean(BeamformerBase):
1529
1532
  Returns
1530
1533
  -------
1531
1534
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1532
-
1533
1535
  """
1534
1536
  f = self._f
1535
1537
  gs = self.steer.grid.size
@@ -1545,7 +1547,7 @@ class BeamformerClean(BeamformerBase):
1545
1547
  param_steer_type, steer_vector = self._beamformer_params()
1546
1548
  for i in ind:
1547
1549
  p.freq = f[i]
1548
- csm = array(self.freq_data.csm[i], dtype='complex128')
1550
+ csm = np.array(self.freq_data.csm[i], dtype='complex128')
1549
1551
  dirty = beamformerFreq(
1550
1552
  param_steer_type,
1551
1553
  self.r_diag,
@@ -1554,16 +1556,16 @@ class BeamformerClean(BeamformerBase):
1554
1556
  csm,
1555
1557
  )[0]
1556
1558
  if self.r_diag: # set (unphysical) negative output values to 0
1557
- indNegSign = sign(dirty) < 0
1559
+ indNegSign = np.sign(dirty) < 0
1558
1560
  dirty[indNegSign] = 0.0
1559
1561
 
1560
- clean = zeros(gs, dtype=dirty.dtype)
1562
+ clean = np.zeros(gs, dtype=dirty.dtype)
1561
1563
  i_iter = 0
1562
1564
  flag = True
1563
1565
  while flag:
1564
1566
  dirty_sum = abs(dirty).sum(0)
1565
1567
  next_max = dirty.argmax(0)
1566
- p.grid_indices = array([next_max])
1568
+ p.grid_indices = np.array([next_max])
1567
1569
  psf = p.psf.reshape(gs)
1568
1570
  new_amp = self.damp * dirty[next_max] # / psf[next_max]
1569
1571
  clean[next_max] += new_amp
@@ -1575,9 +1577,9 @@ class BeamformerClean(BeamformerBase):
1575
1577
  self._fr[i] = 1
1576
1578
 
1577
1579
 
1578
- @deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
1579
1580
  class BeamformerCMF(BeamformerBase):
1580
- """Covariance Matrix Fitting algorithm.
1581
+ """
1582
+ Covariance Matrix Fitting algorithm.
1581
1583
 
1582
1584
  This is not really a beamformer, but an inverse method.
1583
1585
  See :cite:`Yardibi2008` for details.
@@ -1596,37 +1598,33 @@ class BeamformerCMF(BeamformerBase):
1596
1598
  'fmin_l_bfgs_b',
1597
1599
  'Split_Bregman',
1598
1600
  'FISTA',
1599
- desc='fit method used',
1600
1601
  )
1601
1602
 
1602
1603
  #: Weight factor for LassoLars method,
1603
1604
  #: defaults to 0.0.
1604
1605
  #: (Use values in the order of 10^⁻9 for good results.)
1605
- alpha = Range(0.0, 1.0, 0.0, desc='Lasso weight factor')
1606
+ alpha = Range(0.0, 1.0, 0.0)
1606
1607
 
1607
1608
  #: Total or maximum number of iterations
1608
1609
  #: (depending on :attr:`method`),
1609
1610
  #: tradeoff between speed and precision;
1610
1611
  #: defaults to 500
1611
- n_iter = Int(500, desc='maximum number of iterations')
1612
+ n_iter = Int(500)
1612
1613
 
1613
1614
  #: Unit multiplier for evaluating, e.g., nPa instead of Pa.
1614
1615
  #: Values are converted back before returning.
1615
1616
  #: Temporary conversion may be necessary to not reach machine epsilon
1616
1617
  #: within fitting method algorithms. Defaults to 1e9.
1617
- unit_mult = Float(1e9, desc='unit multiplier')
1618
+ unit_mult = Float(1e9)
1618
1619
 
1619
1620
  #: If True, shows the status of the PyLops solver. Only relevant in case of FISTA or
1620
1621
  #: Split_Bregman
1621
- show = Bool(False, desc='show output of PyLops solvers')
1622
+ show = Bool(False)
1622
1623
 
1623
1624
  #: Energy normalization in case of diagonal removal not implemented for inverse methods.
1624
- r_diag_norm = Enum(
1625
- None,
1626
- desc='Energy normalization in case of diagonal removal not implemented for inverse methods',
1627
- )
1625
+ r_diag_norm = Enum(None)
1628
1626
 
1629
- # internal identifier
1627
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1630
1628
  digest = Property(
1631
1629
  depends_on=[
1632
1630
  'freq_data.digest',
@@ -1644,8 +1642,8 @@ class BeamformerCMF(BeamformerBase):
1644
1642
  def _get_digest(self):
1645
1643
  return digest(self)
1646
1644
 
1647
- @on_trait_change('method')
1648
- def _validate(self):
1645
+ @observe('method')
1646
+ def _validate(self, event): # noqa ARG002
1649
1647
  if self.method in ['FISTA', 'Split_Bregman'] and not config.have_pylops:
1650
1648
  msg = (
1651
1649
  'Cannot import Pylops package. No Pylops installed.'
@@ -1654,7 +1652,8 @@ class BeamformerCMF(BeamformerBase):
1654
1652
  raise ImportError(msg)
1655
1653
 
1656
1654
  def _calc(self, ind):
1657
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1655
+ """
1656
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1658
1657
 
1659
1658
  This is an internal helper function that is automatically called when
1660
1659
  accessing the beamformer's :attr:`result` or calling
@@ -1669,13 +1668,12 @@ class BeamformerCMF(BeamformerBase):
1669
1668
  Returns
1670
1669
  -------
1671
1670
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1672
-
1673
1671
  """
1674
1672
  f = self._f
1675
1673
 
1676
1674
  # function to repack complex matrices to deal with them in real number space
1677
1675
  def realify(matrix):
1678
- return vstack([matrix.real, matrix.imag])
1676
+ return np.vstack([matrix.real, matrix.imag])
1679
1677
 
1680
1678
  # prepare calculation
1681
1679
  nc = self.freq_data.num_channels
@@ -1683,29 +1681,29 @@ class BeamformerCMF(BeamformerBase):
1683
1681
  unit = self.unit_mult
1684
1682
 
1685
1683
  for i in ind:
1686
- csm = array(self.freq_data.csm[i], dtype='complex128', copy=1)
1684
+ csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
1687
1685
 
1688
1686
  h = self.steer.transfer(f[i]).T
1689
1687
 
1690
1688
  # reduced Kronecker product (only where solution matrix != 0)
1691
- Bc = (h[:, :, newaxis] * h.conjugate().T[newaxis, :, :]).transpose(2, 0, 1)
1689
+ Bc = (h[:, :, np.newaxis] * h.conjugate().T[np.newaxis, :, :]).transpose(2, 0, 1)
1692
1690
  Ac = Bc.reshape(nc * nc, num_points)
1693
1691
 
1694
- # get indices for upper triangular matrices (use tril b/c transposed)
1695
- ind = reshape(tril(ones((nc, nc))), (nc * nc,)) > 0
1692
+ # get indices for upper triangular matrices (use np.tril b/c transposed)
1693
+ ind = np.reshape(np.tril(np.ones((nc, nc))), (nc * nc,)) > 0
1696
1694
 
1697
- ind_im0 = (reshape(eye(nc), (nc * nc,)) == 0)[ind]
1695
+ ind_im0 = (np.reshape(np.eye(nc), (nc * nc,)) == 0)[ind]
1698
1696
  if self.r_diag:
1699
1697
  # omit main diagonal for noise reduction
1700
- ind_reim = hstack([ind_im0, ind_im0])
1698
+ ind_reim = np.hstack([ind_im0, ind_im0])
1701
1699
  else:
1702
1700
  # take all real parts -- also main diagonal
1703
- ind_reim = hstack([ones(size(ind_im0)) > 0, ind_im0])
1701
+ ind_reim = np.hstack([np.ones(np.size(ind_im0)) > 0, ind_im0])
1704
1702
  ind_reim[0] = True # why this ?
1705
1703
 
1706
1704
  A = realify(Ac[ind, :])[ind_reim, :]
1707
1705
  # use csm.T for column stacking reshape!
1708
- R = realify(reshape(csm.T, (nc * nc, 1))[ind, :])[ind_reim, :] * unit
1706
+ R = realify(np.reshape(csm.T, (nc * nc, 1))[ind, :])[ind_reim, :] * unit
1709
1707
  # choose method
1710
1708
  if self.method == 'LassoLars':
1711
1709
  model = LassoLars(alpha=self.alpha * unit, max_iter=self.n_iter, positive=True, **sklearn_ndict)
@@ -1759,13 +1757,13 @@ class BeamformerCMF(BeamformerBase):
1759
1757
  # function
1760
1758
  func = x.T @ A.T @ A @ x - 2 * R.T @ A @ x + R.T @ R
1761
1759
  # derivitaive
1762
- der = 2 * A.T @ A @ x.T[:, newaxis] - 2 * A.T @ R
1760
+ der = 2 * A.T @ A @ x.T[:, np.newaxis] - 2 * A.T @ R
1763
1761
  return func[0].T, der[:, 0]
1764
1762
 
1765
1763
  # initial guess
1766
- x0 = ones([num_points])
1764
+ x0 = np.ones([num_points])
1767
1765
  # boundaries - set to non negative
1768
- boundaries = tile((0, +inf), (len(x0), 1))
1766
+ boundaries = np.tile((0, np.inf), (len(x0), 1))
1769
1767
 
1770
1768
  # optimize
1771
1769
  self._ac[i], yval, dicts = fmin_l_bfgs_b(
@@ -1791,7 +1789,7 @@ class BeamformerCMF(BeamformerBase):
1791
1789
  # pipeline approach with StandardScaler does scale in a different way, thus we
1792
1790
  # monkeypatch the code and normalize ourselves to make results the same over
1793
1791
  # different sklearn versions
1794
- norms = norm(A, axis=0)
1792
+ norms = spla.norm(A, axis=0)
1795
1793
  # get rid of sklearn warnings that appear for sklearn<1.2 despite any settings
1796
1794
  with warnings.catch_warnings():
1797
1795
  warnings.simplefilter('ignore', category=FutureWarning)
@@ -1802,9 +1800,9 @@ class BeamformerCMF(BeamformerBase):
1802
1800
  self._fr[i] = 1
1803
1801
 
1804
1802
 
1805
- @deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
1806
1803
  class BeamformerSODIX(BeamformerBase):
1807
- """Source directivity modeling in the cross-spectral matrix (SODIX) algorithm.
1804
+ """
1805
+ Source directivity modeling in the cross-spectral matrix (SODIX) algorithm.
1808
1806
 
1809
1807
  See :cite:`Funke2017` and :cite:`Oertwig2019` for details.
1810
1808
  """
@@ -1812,30 +1810,27 @@ class BeamformerSODIX(BeamformerBase):
1812
1810
  #: Type of fit method to be used ('fmin_l_bfgs_b').
1813
1811
  #: These methods are implemented in
1814
1812
  #: the scipy module.
1815
- method = Enum('fmin_l_bfgs_b', desc='fit method used')
1813
+ method = Enum('fmin_l_bfgs_b')
1816
1814
 
1817
1815
  #: Maximum number of iterations,
1818
1816
  #: tradeoff between speed and precision;
1819
1817
  #: defaults to 200
1820
- n_iter = Int(200, desc='maximum number of iterations')
1818
+ n_iter = Int(200)
1821
1819
 
1822
1820
  #: Weight factor for regularization,
1823
1821
  #: defaults to 0.0.
1824
- alpha = Range(0.0, 1.0, 0.0, desc='regularization factor')
1822
+ alpha = Range(0.0, 1.0, 0.0)
1825
1823
 
1826
1824
  #: Unit multiplier for evaluating, e.g., nPa instead of Pa.
1827
1825
  #: Values are converted back before returning.
1828
1826
  #: Temporary conversion may be necessary to not reach machine epsilon
1829
1827
  #: within fitting method algorithms. Defaults to 1e9.
1830
- unit_mult = Float(1e9, desc='unit multiplier')
1828
+ unit_mult = Float(1e9)
1831
1829
 
1832
1830
  #: Energy normalization in case of diagonal removal not implemented for inverse methods.
1833
- r_diag_norm = Enum(
1834
- None,
1835
- desc='Energy normalization in case of diagonal removal not implemented for inverse methods',
1836
- )
1831
+ r_diag_norm = Enum(None)
1837
1832
 
1838
- # internal identifier
1833
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
1839
1834
  digest = Property(
1840
1835
  depends_on=[
1841
1836
  'freq_data.digest',
@@ -1854,7 +1849,8 @@ class BeamformerSODIX(BeamformerBase):
1854
1849
  return digest(self)
1855
1850
 
1856
1851
  def _calc(self, ind):
1857
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
1852
+ """
1853
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
1858
1854
 
1859
1855
  This is an internal helper function that is automatically called when
1860
1856
  accessing the beamformer's :attr:`result` or calling
@@ -1869,7 +1865,6 @@ class BeamformerSODIX(BeamformerBase):
1869
1865
  Returns
1870
1866
  -------
1871
1867
  This method only returns values through :attr:`_ac` and :attr:`_fr`
1872
-
1873
1868
  """
1874
1869
  # prepare calculation
1875
1870
  f = self._f
@@ -1884,14 +1879,17 @@ class BeamformerSODIX(BeamformerBase):
1884
1879
  for i in range(1, ind.max() + 1):
1885
1880
  if not self._fr[i]:
1886
1881
  # measured csm
1887
- csm = array(self.freq_data.csm[i], dtype='complex128', copy=1)
1882
+ csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
1888
1883
  # transfer function
1889
1884
  h = self.steer.transfer(f[i]).T
1890
1885
 
1891
1886
  if self.method == 'fmin_l_bfgs_b':
1892
1887
  # function to minimize
1893
1888
  def function(directions):
1894
- """Parameters
1889
+ """
1890
+ Calculates SODIX objective function and derivatives for optimization.
1891
+
1892
+ Parameters
1895
1893
  ----------
1896
1894
  directions
1897
1895
  [num_points*num_mics]
@@ -1902,41 +1900,43 @@ class BeamformerSODIX(BeamformerBase):
1902
1900
  [1]
1903
1901
  derdrl - derivitaives in direction of D
1904
1902
  [num_mics*num_points].
1905
-
1906
1903
  """
1907
1904
  #### the sodix function ####
1908
1905
  Djm = directions.reshape([num_points, num_mics])
1909
1906
  p = h.T * Djm
1910
- csm_mod = dot(p.T, p.conj())
1907
+ csm_mod = np.dot(p.T, p.conj())
1911
1908
  Q = csm - csm_mod
1912
- func = sum((absolute(Q)) ** 2)
1909
+ func = np.sum((np.abs(Q)) ** 2)
1913
1910
 
1914
1911
  # subscripts and operands for numpy einsum and einsum_path
1915
1912
  subscripts = 'rl,rm,ml->rl'
1916
1913
  operands = (h.T, h.T.conj() * Djm, Q)
1917
- es_path = einsum_path(subscripts, *operands, optimize='greedy')[0]
1914
+ es_path = np.einsum_path(subscripts, *operands, optimize='greedy')[0]
1918
1915
 
1919
1916
  #### the sodix derivative ####
1920
- derdrl = einsum(subscripts, *operands, optimize=es_path)
1921
- derdrl = -4 * real(derdrl)
1917
+ derdrl = np.einsum(subscripts, *operands, optimize=es_path)
1918
+ derdrl = -4 * np.real(derdrl)
1922
1919
  return func, derdrl.ravel()
1923
1920
 
1924
1921
  ##### initial guess ####
1925
1922
  if not self._fr[(i - 1)]:
1926
- D0 = ones([num_points, num_mics])
1923
+ D0 = np.ones([num_points, num_mics])
1927
1924
  else:
1928
- D0 = sqrt(
1925
+ D0 = np.sqrt(
1929
1926
  self._ac[(i - 1)]
1930
- * real(trace(csm) / trace(array(self.freq_data.csm[i - 1], dtype='complex128', copy=1))),
1927
+ * np.real(
1928
+ np.trace(csm)
1929
+ / np.trace(np.array(self.freq_data.csm[i - 1], dtype='complex128', copy=True))
1930
+ ),
1931
1931
  )
1932
1932
 
1933
1933
  # boundaries - set to non negative [2*(num_points*num_mics)]
1934
- boundaries = tile((0, +inf), (num_points * num_mics, 1))
1934
+ boundaries = np.tile((0, np.inf), (num_points * num_mics, 1))
1935
1935
 
1936
1936
  # optimize with gradient solver
1937
1937
  # see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html
1938
1938
 
1939
- qi = ones([num_points, num_mics])
1939
+ qi = np.ones([num_points, num_mics])
1940
1940
  qi, yval, dicts = fmin_l_bfgs_b(
1941
1941
  function,
1942
1942
  D0,
@@ -1959,9 +1959,9 @@ class BeamformerSODIX(BeamformerBase):
1959
1959
  self._fr[i] = 1
1960
1960
 
1961
1961
 
1962
- @deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
1963
1962
  class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
1964
- """Beamforming GIB methods with different normalizations.
1963
+ """
1964
+ Beamforming GIB methods with different normalizations.
1965
1965
 
1966
1966
  See :cite:`Suzuki2011` for details.
1967
1967
  """
@@ -1970,13 +1970,13 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
1970
1970
  #: Values are converted back before returning.
1971
1971
  #: Temporary conversion may be necessary to not reach machine epsilon
1972
1972
  #: within fitting method algorithms. Defaults to 1e9.
1973
- unit_mult = Float(1e9, desc='unit multiplier')
1973
+ unit_mult = Float(1e9)
1974
1974
 
1975
1975
  #: Total or maximum number of iterations
1976
1976
  #: (depending on :attr:`method`),
1977
1977
  #: tradeoff between speed and precision;
1978
1978
  #: defaults to 10
1979
- n_iter = Int(10, desc='maximum number of iterations')
1979
+ n_iter = Int(10)
1980
1980
 
1981
1981
  #: Type of fit method to be used ('Suzuki', 'LassoLars', 'LassoLarsCV', 'LassoLarsBIC',
1982
1982
  #: 'OMPCV' or 'NNLS', defaults to 'Suzuki').
@@ -1991,37 +1991,33 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
1991
1991
  'LassoLarsCV',
1992
1992
  'OMPCV',
1993
1993
  'NNLS',
1994
- desc='fit method used',
1995
1994
  )
1996
1995
 
1997
1996
  #: Weight factor for LassoLars method,
1998
1997
  #: defaults to 0.0.
1999
- alpha = Range(0.0, 1.0, 0.0, desc='Lasso weight factor')
1998
+ alpha = Range(0.0, 1.0, 0.0)
2000
1999
  # (use values in the order of 10^⁻9 for good results)
2001
2000
 
2002
2001
  #: Norm to consider for the regularization in InverseIRLS and Suzuki methods
2003
2002
  #: defaults to L-1 Norm
2004
- pnorm = Float(1, desc='Norm for regularization')
2003
+ pnorm = Float(1)
2005
2004
 
2006
2005
  #: Beta - Fraction of sources maintained after each iteration
2007
2006
  #: defaults to 0.9
2008
- beta = Float(0.9, desc='fraction of sources maintained')
2007
+ beta = Float(0.9)
2009
2008
 
2010
2009
  #: eps - Regularization parameter for Suzuki algorithm
2011
2010
  #: defaults to 0.05.
2012
- eps_perc = Float(0.05, desc='regularization parameter')
2011
+ eps_perc = Float(0.05)
2013
2012
 
2014
2013
  # This feature is not fully supported may be changed in the next release
2015
- # First eigenvalue to consider. Defaults to 0.
2016
- m = Int(0, desc='First eigenvalue to consider')
2014
+ #: First eigenvalue to consider. Defaults to 0.
2015
+ m = Int(0)
2017
2016
 
2018
2017
  #: Energy normalization in case of diagonal removal not implemented for inverse methods.
2019
- r_diag_norm = Enum(
2020
- None,
2021
- desc='Energy normalization in case of diagonal removal not implemented for inverse methods',
2022
- )
2018
+ r_diag_norm = Enum(None)
2023
2019
 
2024
- # internal identifier
2020
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
2025
2021
  digest = Property(
2026
2022
  depends_on=[
2027
2023
  'steer.inv_digest',
@@ -2052,7 +2048,8 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2052
2048
  return min(nm - 1, na)
2053
2049
 
2054
2050
  def _calc(self, ind):
2055
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
2051
+ """
2052
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
2056
2053
 
2057
2054
  This is an internal helper function that is automatically called when
2058
2055
  accessing the beamformer's :attr:`result` or calling
@@ -2067,14 +2064,13 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2067
2064
  Returns
2068
2065
  -------
2069
2066
  This method only returns values through :attr:`_ac` and :attr:`_fr`
2070
-
2071
2067
  """
2072
2068
  f = self._f
2073
2069
  n = int(self.na) # number of eigenvalues
2074
2070
  m = int(self.m) # number of first eigenvalue
2075
2071
  num_channels = self.freq_data.num_channels # number of channels
2076
2072
  num_points = self.steer.grid.size
2077
- hh = zeros((1, num_points, num_channels), dtype='D')
2073
+ hh = np.zeros((1, num_points, num_channels), dtype='D')
2078
2074
 
2079
2075
  # Generate a cross spectral matrix, and perform the eigenvalue decomposition
2080
2076
  for i in ind:
@@ -2083,79 +2079,83 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2083
2079
  hh = self.steer.transfer(f[i])
2084
2080
  A = hh.T
2085
2081
  # eigenvalues and vectors
2086
- csm = array(self.freq_data.csm[i], dtype='complex128', copy=1)
2087
- eva, eve = eigh(csm)
2082
+ csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
2083
+ eva, eve = spla.eigh(csm)
2088
2084
  eva = eva[::-1]
2089
2085
  eve = eve[:, ::-1]
2090
2086
  # set small values zo 0, lowers numerical errors in simulated data
2091
2087
  eva[eva < max(eva) / 1e12] = 0
2092
2088
  # init sources
2093
- qi = zeros([n + m, num_points], dtype='complex128')
2089
+ qi = np.zeros([n + m, num_points], dtype='complex128')
2094
2090
  # Select the number of coherent modes to be processed referring to the eigenvalue
2095
2091
  # distribution.
2096
2092
  for s in list(range(m, n + m)):
2097
2093
  if eva[s] > 0:
2098
2094
  # Generate the corresponding eigenmodes
2099
- emode = array(sqrt(eva[s]) * eve[:, s], dtype='complex128')
2095
+ emode = np.array(np.sqrt(eva[s]) * eve[:, s], dtype='complex128')
2100
2096
  # choose method for computation
2101
2097
  if self.method == 'Suzuki':
2102
2098
  leftpoints = num_points
2103
- locpoints = arange(num_points)
2104
- weights = diag(ones(num_points))
2105
- epsilon = arange(self.n_iter)
2106
- for it in arange(self.n_iter):
2099
+ locpoints = np.arange(num_points)
2100
+ weights = np.diag(np.ones(num_points))
2101
+ epsilon = np.arange(self.n_iter)
2102
+ for it in np.arange(self.n_iter):
2107
2103
  if num_channels <= leftpoints:
2108
- AWA = dot(dot(A[:, locpoints], weights), A[:, locpoints].conj().T)
2109
- epsilon[it] = max(absolute(eigvals(AWA))) * self.eps_perc
2110
- qi[s, locpoints] = dot(
2111
- dot(
2112
- dot(weights, A[:, locpoints].conj().T),
2113
- inv(AWA + eye(num_channels) * epsilon[it]),
2104
+ AWA = np.dot(np.dot(A[:, locpoints], weights), A[:, locpoints].conj().T)
2105
+ epsilon[it] = max(np.abs(spla.eigvals(AWA))) * self.eps_perc
2106
+ qi[s, locpoints] = np.dot(
2107
+ np.dot(
2108
+ np.dot(weights, A[:, locpoints].conj().T),
2109
+ spla.inv(AWA + np.eye(num_channels) * epsilon[it]),
2114
2110
  ),
2115
2111
  emode,
2116
2112
  )
2117
2113
  elif num_channels > leftpoints:
2118
- AA = dot(A[:, locpoints].conj().T, A[:, locpoints])
2119
- epsilon[it] = max(absolute(eigvals(AA))) * self.eps_perc
2120
- qi[s, locpoints] = dot(
2121
- dot(inv(AA + inv(weights) * epsilon[it]), A[:, locpoints].conj().T),
2114
+ AA = np.dot(A[:, locpoints].conj().T, A[:, locpoints])
2115
+ epsilon[it] = max(np.abs(spla.eigvals(AA))) * self.eps_perc
2116
+ qi[s, locpoints] = np.dot(
2117
+ np.dot(spla.inv(AA + spla.inv(weights) * epsilon[it]), A[:, locpoints].conj().T),
2122
2118
  emode,
2123
2119
  )
2124
2120
  if self.beta < 1 and it > 1:
2125
2121
  # Reorder from the greatest to smallest magnitude to define a
2126
2122
  # reduced-point source distribution, and reform a reduced transfer
2127
2123
  # matrix
2128
- leftpoints = int(round(num_points * self.beta ** (it + 1)))
2129
- idx = argsort(abs(qi[s, locpoints]))[::-1]
2124
+ leftpoints = int(np.round(num_points * self.beta ** (it + 1)))
2125
+ idx = np.argsort(abs(qi[s, locpoints]))[::-1]
2130
2126
  # print(it, leftpoints, locpoints, idx )
2131
- locpoints = delete(locpoints, [idx[leftpoints::]])
2132
- qix = zeros([n + m, leftpoints], dtype='complex128')
2127
+ locpoints = np.delete(locpoints, [idx[leftpoints::]])
2128
+ qix = np.zeros([n + m, leftpoints], dtype='complex128')
2133
2129
  qix[s, :] = qi[s, locpoints]
2134
2130
  # calc weights for next iteration
2135
- weights = diag(absolute(qix[s, :]) ** (2 - self.pnorm))
2131
+ weights = np.diag(np.abs(qix[s, :]) ** (2 - self.pnorm))
2136
2132
  else:
2137
- weights = diag(absolute(qi[s, :]) ** (2 - self.pnorm))
2133
+ weights = np.diag(np.abs(qi[s, :]) ** (2 - self.pnorm))
2138
2134
 
2139
2135
  elif self.method == 'InverseIRLS':
2140
- weights = eye(num_points)
2141
- locpoints = arange(num_points)
2142
- for _it in arange(self.n_iter):
2136
+ weights = np.eye(num_points)
2137
+ locpoints = np.arange(num_points)
2138
+ for _it in np.arange(self.n_iter):
2143
2139
  if num_channels <= num_points:
2144
- wtwi = inv(dot(weights.T, weights))
2140
+ wtwi = spla.inv(np.dot(weights.T, weights))
2145
2141
  aH = A.conj().T
2146
- qi[s, :] = dot(dot(wtwi, aH), dot(inv(dot(A, dot(wtwi, aH))), emode))
2147
- weights = diag(absolute(qi[s, :]) ** ((2 - self.pnorm) / 2))
2148
- weights = weights / sum(absolute(weights))
2142
+ qi[s, :] = np.dot(
2143
+ np.dot(wtwi, aH), np.dot(spla.inv(np.dot(A, np.dot(wtwi, aH))), emode)
2144
+ )
2145
+ weights = np.diag(np.abs(qi[s, :]) ** ((2 - self.pnorm) / 2))
2146
+ weights = weights / np.sum(np.abs(weights))
2149
2147
  elif num_channels > num_points:
2150
- wtw = dot(weights.T, weights)
2151
- qi[s, :] = dot(dot(inv(dot(dot(A.conj.T, wtw), A)), dot(A.conj().T, wtw)), emode)
2152
- weights = diag(absolute(qi[s, :]) ** ((2 - self.pnorm) / 2))
2153
- weights = weights / sum(absolute(weights))
2148
+ wtw = np.dot(weights.T, weights)
2149
+ qi[s, :] = np.dot(
2150
+ np.dot(spla.inv(np.dot(np.dot(A.conj.T, wtw), A)), np.dot(A.conj().T, wtw)), emode
2151
+ )
2152
+ weights = np.diag(np.abs(qi[s, :]) ** ((2 - self.pnorm) / 2))
2153
+ weights = weights / np.sum(np.abs(weights))
2154
2154
  else:
2155
- locpoints = arange(num_points)
2155
+ locpoints = np.arange(num_points)
2156
2156
  unit = self.unit_mult
2157
- AB = vstack([hstack([A.real, -A.imag]), hstack([A.imag, A.real])])
2158
- R = hstack([emode.real.T, emode.imag.T]) * unit
2157
+ AB = np.vstack([np.hstack([A.real, -A.imag]), np.hstack([A.imag, A.real])])
2158
+ R = np.hstack([emode.real.T, emode.imag.T]) * unit
2159
2159
  if self.method == 'LassoLars':
2160
2160
  model = LassoLars(alpha=self.alpha * unit, max_iter=self.n_iter, positive=True)
2161
2161
  elif self.method == 'LassoLarsBIC':
@@ -2173,7 +2173,7 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2173
2173
  # way, thus we monkeypatch the code and normalize
2174
2174
  # ourselves to make results the same over different
2175
2175
  # sklearn versions
2176
- norms = norm(AB, axis=0)
2176
+ norms = spla.norm(AB, axis=0)
2177
2177
  # get rid of annoying sklearn warnings that appear
2178
2178
  # for sklearn<1.2 despite any settings
2179
2179
  with warnings.catch_warnings():
@@ -2181,7 +2181,7 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2181
2181
  # normalized A
2182
2182
  model.fit(AB / norms, R)
2183
2183
  # recover normalization in the coef's
2184
- qi_real, qi_imag = hsplit(model.coef_[:] / norms / unit, 2)
2184
+ qi_real, qi_imag = np.hsplit(model.coef_[:] / norms / unit, 2)
2185
2185
  # print(s,qi.size)
2186
2186
  qi[s, locpoints] = qi_real + qi_imag * 1j
2187
2187
  else:
@@ -2192,8 +2192,8 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
2192
2192
  )
2193
2193
  # Generate source maps of all selected eigenmodes, and superpose source intensity
2194
2194
  # for each source type.
2195
- temp = zeros(num_points)
2196
- temp[locpoints] = sum(absolute(qi[:, locpoints]) ** 2, axis=0)
2195
+ temp = np.zeros(num_points)
2196
+ temp[locpoints] = np.sum(np.abs(qi[:, locpoints]) ** 2, axis=0)
2197
2197
  self._ac[i] = temp
2198
2198
  self._fr[i] = 1
2199
2199
 
@@ -2211,7 +2211,8 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
2211
2211
  return self._gpos
2212
2212
 
2213
2213
  def integrate(self, sector):
2214
- """Integrates result map over a given sector.
2214
+ """
2215
+ Integrates result map over a given sector.
2215
2216
 
2216
2217
  Parameters
2217
2218
  ----------
@@ -2222,7 +2223,6 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
2222
2223
  -------
2223
2224
  array of floats
2224
2225
  The spectrum (all calculated frequency bands) for the integrated sector.
2225
-
2226
2226
  """
2227
2227
  if not isinstance(sector, Sector):
2228
2228
  msg = (
@@ -2235,21 +2235,22 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
2235
2235
 
2236
2236
  ind = self.subdomain(sector)
2237
2237
  r = self.result
2238
- h = zeros(r.shape[0])
2238
+ h = np.zeros(r.shape[0])
2239
2239
  for i in range(r.shape[0]):
2240
2240
  h[i] = r[i][ind].sum()
2241
2241
  return h
2242
2242
 
2243
2243
 
2244
2244
  class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2245
- """Orthogonal beamforming without predefined grid.
2245
+ """
2246
+ Orthogonal beamforming without predefined grid.
2246
2247
 
2247
2248
  See :cite:`Sarradj2022` for details.
2248
2249
  """
2249
2250
 
2250
2251
  #: List of components to consider, use this to directly set the eigenvalues
2251
2252
  #: used in the beamformer. Alternatively, set :attr:`n`.
2252
- eva_list = CArray(dtype=int, value=array([-1]), desc='components')
2253
+ eva_list = CArray(dtype=int, value=np.array([-1]))
2253
2254
 
2254
2255
  #: Number of components to consider, defaults to 1. If set,
2255
2256
  #: :attr:`eva_list` will contain
@@ -2269,15 +2270,12 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2269
2270
  #: and 1 iteration
2270
2271
  shgo = Dict
2271
2272
 
2272
- #: No normalization implemented. Defaults to 1.0.
2273
- r_diag_norm = Enum(
2274
- 1.0,
2275
- desc='If diagonal of the csm is removed, some signal energy is lost.'
2276
- 'This is handled via this normalization factor.'
2277
- 'For this class, normalization is not implemented. Defaults to 1.0.',
2278
- )
2273
+ #: If diagonal of the csm is removed, some signal energy is lost.
2274
+ #: This is handled via this normalization factor.
2275
+ #: For this class, normalization is not implemented. Defaults to 1.0.
2276
+ r_diag_norm = Enum(1.0)
2279
2277
 
2280
- # internal identifier
2278
+ #: A unique identifier for the beamformer, based on its properties. (read-only)
2281
2279
  digest = Property(
2282
2280
  depends_on=['freq_data.digest', 'steer.digest', 'precision', 'r_diag', 'eva_list', 'bounds', 'shgo'],
2283
2281
  )
@@ -2286,17 +2284,18 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2286
2284
  def _get_digest(self):
2287
2285
  return digest(self)
2288
2286
 
2289
- @on_trait_change('n')
2290
- def set_eva_list(self):
2287
+ @observe('n')
2288
+ def _update_eva_list(self, event): # noqa ARG002
2291
2289
  """Sets the list of eigenvalues to consider."""
2292
- self.eva_list = arange(-1, -1 - self.n, -1)
2290
+ self.eva_list = np.arange(-1, -1 - self.n, -1)
2293
2291
 
2294
2292
  @property_depends_on('n')
2295
2293
  def _get_size(self):
2296
2294
  return self.n * self.freq_data.fftfreq().shape[0]
2297
2295
 
2298
2296
  def _calc(self, ind):
2299
- """Calculates the result for the frequencies defined by :attr:`freq_data`.
2297
+ """
2298
+ Calculates the result for the frequencies defined by :attr:`freq_data`.
2300
2299
 
2301
2300
  This is an internal helper function that is automatically called when
2302
2301
  accessing the beamformer's :attr:`result` or calling
@@ -2311,13 +2310,12 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2311
2310
  Returns
2312
2311
  -------
2313
2312
  This method only returns values through :attr:`_ac` and :attr:`_fr`
2314
-
2315
2313
  """
2316
2314
  f = self._f
2317
2315
  normfactor = self.sig_loss_norm()
2318
2316
  num_channels = self.freq_data.num_channels
2319
2317
  # eigenvalue number list in standard form from largest to smallest
2320
- eva_list = unique(self.eva_list % self.steer.mics.num_mics)[::-1]
2318
+ eva_list = np.unique(self.eva_list % self.steer.mics.num_mics)[::-1]
2321
2319
  steer_type = self.steer.steer_type
2322
2320
  if steer_type == 'custom':
2323
2321
  msg = 'custom steer_type is not implemented'
@@ -2336,27 +2334,27 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2336
2334
  for y in self.bounds[1]:
2337
2335
  for z in self.bounds[2]:
2338
2336
  roi.append((x, y, z))
2339
- self.steer.env.roi = array(roi).T
2340
- bmin = array(tuple(map(min, self.bounds)))
2341
- bmax = array(tuple(map(max, self.bounds)))
2337
+ self.steer.env.roi = np.array(roi).T
2338
+ bmin = np.array(tuple(map(min, self.bounds)))
2339
+ bmax = np.array(tuple(map(max, self.bounds)))
2342
2340
  for i in ind:
2343
- eva = array(self.freq_data.eva[i], dtype='float64')
2344
- eve = array(self.freq_data.eve[i], dtype='complex128')
2345
- k = 2 * pi * f[i] / env.c
2341
+ eva = np.array(self.freq_data.eva[i], dtype='float64')
2342
+ eve = np.array(self.freq_data.eve[i], dtype='complex128')
2343
+ k = 2 * np.pi * f[i] / env.c
2346
2344
  for j, n in enumerate(eva_list):
2347
2345
  # print(f[i],n)
2348
2346
 
2349
2347
  def func(xy):
2350
2348
  # function to minimize globally
2351
- xy = clip(xy, bmin, bmax)
2352
- r0 = env._r(xy[:, newaxis])
2353
- rm = env._r(xy[:, newaxis], mpos)
2349
+ xy = np.clip(xy, bmin, bmax)
2350
+ r0 = env._r(xy[:, np.newaxis])
2351
+ rm = env._r(xy[:, np.newaxis], mpos)
2354
2352
  return -beamformerFreq(
2355
2353
  steer_type,
2356
2354
  self.r_diag,
2357
2355
  normfactor,
2358
2356
  (r0, rm, k),
2359
- (ones(1), eve[:, n : n + 1]),
2357
+ (np.ones(1), eve[:, n : n + 1]),
2360
2358
  )[0][0] # noqa: B023
2361
2359
 
2362
2360
  # simplical global homotopy optimizer
@@ -2372,7 +2370,8 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
2372
2370
 
2373
2371
 
2374
2372
  def L_p(x): # noqa: N802
2375
- r"""Calculates the sound pressure level from the squared sound pressure.
2373
+ r"""
2374
+ Calculates the sound pressure level from the squared sound pressure.
2376
2375
 
2377
2376
  :math:`L_p = 10 \lg ( x / 4\cdot 10^{-10})`
2378
2377
 
@@ -2386,17 +2385,17 @@ def L_p(x): # noqa: N802
2386
2385
  array of floats
2387
2386
  The corresponding sound pressure levels in dB.
2388
2387
  If `x<0`, -350.0 dB is returned.
2389
-
2390
2388
  """
2391
2389
  # new version to prevent division by zero warning for float32 arguments
2392
- return 10 * log10(clip(x / 4e-10, 1e-35, None))
2390
+ return 10 * np.log10(np.clip(x / 4e-10, 1e-35, None))
2393
2391
 
2394
2392
 
2395
- # return where(x>0, 10*log10(x/4e-10), -1000.)
2393
+ # return where(x>0, 10*np.log10(x/4e-10), -1000.)
2396
2394
 
2397
2395
 
2398
2396
  def integrate(data, grid, sector):
2399
- """Integrates a sound pressure map over a given sector.
2397
+ """
2398
+ Integrates a sound pressure map over a given sector.
2400
2399
 
2401
2400
  This function can be applied on beamforming results to
2402
2401
  quantitatively analyze the sound pressure in a given sector.
@@ -2424,8 +2423,8 @@ def integrate(data, grid, sector):
2424
2423
  of a :class:`~acoular.grids.Grid`-derived class
2425
2424
  (e.g. :meth:`RectGrid.indices<acoular.grids.RectGrid.indices>`
2426
2425
  or :meth:`RectGrid3D.indices<acoular.grids.RectGrid3D.indices>`).
2427
- Possible sectors would be `array([xmin, ymin, xmax, ymax])`
2428
- or `array([x, y, radius])`.
2426
+ Possible sectors would be `np.array([xmin, ymin, xmax, ymax])`
2427
+ or `np.array([x, y, radius])`.
2429
2428
  Alternatively, a :class:`~acoular.grids.Sector`-derived object
2430
2429
  can be used.
2431
2430
 
@@ -2433,7 +2432,6 @@ def integrate(data, grid, sector):
2433
2432
  -------
2434
2433
  array of floats
2435
2434
  The spectrum (all calculated frequency bands) for the integrated sector.
2436
-
2437
2435
  """
2438
2436
  if isinstance(sector, Sector):
2439
2437
  ind = grid.subdomain(sector)
@@ -2451,10 +2449,10 @@ def integrate(data, grid, sector):
2451
2449
 
2452
2450
  gshape = grid.shape
2453
2451
  gsize = grid.size
2454
- if size(data) == gsize: # one value per grid point
2452
+ if np.size(data) == gsize: # one value per grid point
2455
2453
  h = data.reshape(gshape)[ind].sum()
2456
2454
  elif data.ndim == 2 and data.shape[1] == gsize:
2457
- h = zeros(data.shape[0])
2455
+ h = np.zeros(data.shape[0])
2458
2456
  for i in range(data.shape[0]):
2459
2457
  h[i] = data[i].reshape(gshape)[ind].sum()
2460
2458
  return h