acoular 23.6__py3-none-any.whl → 24.3__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.
Files changed (64) hide show
  1. acoular/__init__.py +2 -2
  2. acoular/configuration.py +37 -1
  3. acoular/environments.py +15 -9
  4. acoular/fastFuncs.py +199 -472
  5. acoular/fbeamform.py +168 -109
  6. acoular/grids.py +33 -114
  7. acoular/sources.py +77 -3
  8. acoular/spectra.py +2 -2
  9. acoular/tbeamform.py +15 -8
  10. acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
  11. acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
  12. acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
  13. acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
  14. acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
  15. acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
  16. acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
  17. acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
  18. acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
  19. acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
  20. acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
  21. acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
  22. acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
  23. acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
  24. acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
  25. acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
  26. acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
  27. acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
  28. acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
  29. acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
  30. acoular/tests/reference_data/BeamformerGIB.npy +0 -0
  31. acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
  32. acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
  33. acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
  34. acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
  35. acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
  36. acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
  37. acoular/tests/reference_data/FiltOctave__.npy +0 -0
  38. acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
  39. acoular/tests/reference_data/Filter__.npy +0 -0
  40. acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
  41. acoular/tests/reference_data/TimeAverage__.npy +0 -0
  42. acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
  43. acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
  44. acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
  45. acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
  46. acoular/tests/reference_data/TimeInOut__.npy +0 -0
  47. acoular/tests/reference_data/TimePower__.npy +0 -0
  48. acoular/tests/reference_data/TimeReverse__.npy +0 -0
  49. acoular/tests/test_beamformer_results.py +39 -8
  50. acoular/tests/test_grid.py +92 -0
  51. acoular/tests/test_integrate.py +102 -0
  52. acoular/tests/test_tprocess.py +52 -0
  53. acoular/tests/test_traj_beamformer_results.py +2 -2
  54. acoular/tfastfuncs.py +24 -25
  55. acoular/tools.py +144 -2
  56. acoular/tprocess.py +91 -102
  57. acoular/version.py +2 -2
  58. acoular-24.3.dist-info/METADATA +181 -0
  59. {acoular-23.6.dist-info → acoular-24.3.dist-info}/RECORD +62 -25
  60. {acoular-23.6.dist-info → acoular-24.3.dist-info}/WHEEL +1 -1
  61. {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/LICENSE +1 -1
  62. acoular/tests/reference_data/BeamformerCMF.npy +0 -0
  63. acoular-23.6.dist-info/METADATA +0 -82
  64. {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/sources.py CHANGED
@@ -19,12 +19,13 @@
19
19
  MovingLineSource
20
20
  UncorrelatedNoiseSource
21
21
  SourceMixer
22
+ PointSourceConvolve
22
23
  """
23
24
 
24
25
  # imports from other packages
25
26
 
26
27
  from numpy import array, sqrt, ones, empty, newaxis, uint32, arange, dot, int64 ,real, pi, tile,\
27
- cross, zeros, ceil
28
+ cross, zeros, ceil, repeat
28
29
  from numpy import min as npmin
29
30
  from numpy import any as npany
30
31
 
@@ -45,7 +46,7 @@ from .trajectory import Trajectory
45
46
  from .internal import digest, ldigest
46
47
  from .microphones import MicGeom
47
48
  from .environments import Environment
48
- from .tprocess import SamplesGenerator
49
+ from .tprocess import SamplesGenerator, TimeConvolve
49
50
  from .signals import SignalGenerator
50
51
  from .h5files import H5FileBase, _get_h5file_class
51
52
  from .tools import get_modes
@@ -1236,7 +1237,10 @@ class UncorrelatedNoiseSource( SamplesGenerator ):
1236
1237
  yield signal[n-num:n,:]
1237
1238
  n += num
1238
1239
  else:
1239
- yield signal[n-num:,:]
1240
+ if (n-num) < self.numsamples:
1241
+ yield signal[n-num:,:]
1242
+ else:
1243
+ return
1240
1244
 
1241
1245
 
1242
1246
 
@@ -1348,3 +1352,73 @@ class SourceMixer( SamplesGenerator ):
1348
1352
  yield temp
1349
1353
  if sh > temp.shape[0]:
1350
1354
  break
1355
+
1356
+
1357
+ class PointSourceConvolve( PointSource ):
1358
+ """
1359
+ Class to blockwise convolve an arbitrary source signal with a spatial room impulse response
1360
+ """
1361
+
1362
+ #: Convolution kernel in the time domain.
1363
+ #: The second dimension of the kernel array has to be either 1 or match :attr:`~SamplesGenerator.numchannels`.
1364
+ #: If only a single kernel is supplied, it is applied to all channels.
1365
+ kernel = CArray(dtype=float, desc="Convolution kernel.")
1366
+
1367
+ # ------------- overwrite traits that are not supported by this class -------------
1368
+
1369
+ #: Start time of the signal in seconds, defaults to 0 s.
1370
+ start_t = Enum(0.0,
1371
+ desc="signal start time")
1372
+
1373
+ #: Start time of the data aquisition at microphones in seconds,
1374
+ #: defaults to 0 s.
1375
+ start = Enum(0.0,
1376
+ desc="sample start time")
1377
+
1378
+ #: Signal behaviour for negative time indices, i.e. if :attr:`start` < :attr:start_t.
1379
+ #: `loop` take values from the end of :attr:`signal.signal()` array.
1380
+ #: `zeros` set source signal to zero, advisable for deterministic signals.
1381
+ #: defaults to `loop`.
1382
+ prepadding = Enum(None, desc="Behaviour for negative time indices.")
1383
+
1384
+ #: Upsampling factor, internal use, defaults to 16.
1385
+ up = Enum(None, desc="upsampling factor")
1386
+
1387
+ # internal identifier
1388
+ digest = Property(
1389
+ depends_on = ['mics.digest', 'signal.digest', 'loc', 'kernel', '__class__'],
1390
+ )
1391
+
1392
+ @cached_property
1393
+ def _get_digest( self ):
1394
+ return digest(self)
1395
+
1396
+ def result(self, num=128):
1397
+ """
1398
+ Python generator that yields the output at microphones block-wise.
1399
+
1400
+ Parameters
1401
+ ----------
1402
+ num : integer, defaults to 128
1403
+ This parameter defines the size of the blocks to be yielded
1404
+ (i.e. the number of samples per block) .
1405
+
1406
+ Returns
1407
+ -------
1408
+ Samples in blocks of shape (num, numchannels).
1409
+ The last block may be shorter than num.
1410
+ """
1411
+ data = repeat(
1412
+ self.signal.signal()[:,newaxis],self.mics.num_mics,axis=1)
1413
+ source = TimeSamples(
1414
+ data=data,
1415
+ sample_freq=self.sample_freq,
1416
+ numsamples=self.numsamples,
1417
+ numchannels=self.mics.num_mics,
1418
+ )
1419
+ time_convolve = TimeConvolve(
1420
+ source = source,
1421
+ kernel = self.kernel,
1422
+ )
1423
+ for block in time_convolve.result(num):
1424
+ yield block
acoular/spectra.py CHANGED
@@ -18,8 +18,9 @@
18
18
  from warnings import warn
19
19
 
20
20
  from numpy import array, ones, hanning, hamming, bartlett, blackman, \
21
- dot, newaxis, zeros, empty, fft, linalg, sqrt,real, imag,\
21
+ dot, newaxis, zeros, empty, linalg, sqrt,real, imag,\
22
22
  searchsorted, isscalar, fill_diagonal, arange, zeros_like, sum, ndarray
23
+ from scipy import fft
23
24
  from traits.api import HasPrivateTraits, Int, Property, Instance, Trait, \
24
25
  Bool, cached_property, property_depends_on, Delegate, Float, Enum, \
25
26
  CArray
@@ -658,7 +659,6 @@ class PowerSpectraImport( PowerSpectra ):
658
659
 
659
660
  #: The cross spectral matrix,
660
661
  #: (number of frequencies, numchannels, numchannels) array of complex;
661
- #: readonly.
662
662
  csm = Property(
663
663
  desc="cross spectral matrix")
664
664
 
acoular/tbeamform.py CHANGED
@@ -245,13 +245,13 @@ class BeamformerTime( TimeInOut ):
245
245
  n_index = arange(0,num+1)[:,newaxis]
246
246
  c = self.steer.env.c/self.source.sample_freq
247
247
  amp = empty((1,self.grid.size,numMics),dtype=fdtype)
248
- delays = empty((1,self.grid.size,numMics),dtype=fdtype)
248
+ #delays = empty((1,self.grid.size,numMics),dtype=fdtype)
249
249
  d_index = empty((1,self.grid.size,numMics),dtype=idtype)
250
250
  d_interp2 = empty((1,self.grid.size,numMics),dtype=fdtype)
251
251
  _steer_III(self.rm[newaxis,:,:],self.r0[newaxis,:],amp)
252
- _delays(self.rm[newaxis,:,:], delays, c, d_interp2, d_index)
252
+ _delays(self.rm[newaxis,:,:], c, d_interp2, d_index)
253
253
  amp.shape = amp.shape[1:]
254
- delays.shape = delays.shape[1:]
254
+ #delays.shape = delays.shape[1:]
255
255
  d_index.shape = d_index.shape[1:]
256
256
  d_interp2.shape = d_interp2.shape[1:]
257
257
  maxdelay = int((self.rm/c).max())+2 + num # +2 because of interpolation
@@ -295,7 +295,7 @@ class BeamformerTime( TimeInOut ):
295
295
  else:
296
296
  powPhi = (Phi[:num]**2).sum(0)
297
297
  imax = argmax(powPhi)
298
- t_float = delays[imax]+n_index
298
+ t_float = d_interp2[imax] + d_index[imax] + n_index
299
299
  t_ind = t_float.astype(int64)
300
300
  for m in range(numMics):
301
301
  p_res[t_ind[:num+1,m],m] -= self.damp*interp(t_ind[:num+1,m],
@@ -485,7 +485,7 @@ class BeamformerTimeTraj( BeamformerTime ):
485
485
  blockrm = empty((num,self.grid.size,numMics),dtype=fdtype)
486
486
  blockrmconv = empty((num,self.grid.size,numMics),dtype=fdtype)
487
487
  amp = empty((num,self.grid.size,numMics),dtype=fdtype)
488
- delays = empty((num,self.grid.size,numMics),dtype=fdtype)
488
+ #delays = empty((num,self.grid.size,numMics),dtype=fdtype)
489
489
  d_index = empty((num,self.grid.size,numMics),dtype=idtype)
490
490
  d_interp2 = empty((num,self.grid.size,numMics),dtype=fdtype)
491
491
  blockr0 = empty((num,self.grid.size),dtype=fdtype)
@@ -521,7 +521,7 @@ class BeamformerTimeTraj( BeamformerTime ):
521
521
  steer_func(blockrmconv, blockr0, amp)
522
522
  else:
523
523
  steer_func(blockrm, blockr0, amp)
524
- _delays(blockrm, delays, c, d_interp2, d_index)
524
+ _delays(blockrm, c, d_interp2, d_index)
525
525
  #_modf(delays, d_interp2, d_index)
526
526
  maxdelay = (d_index.max((1,2)) + arange(0,num)).max()+2 # + because of interpolation
527
527
  # increase buffer size because of greater delays
@@ -548,6 +548,11 @@ class BeamformerTimeTraj( BeamformerTime ):
548
548
  else:
549
549
  yield Phi[:num]**2
550
550
  else:
551
+ # choose correct distance
552
+ if self.conv_amp:
553
+ blockrm1 = blockrmconv
554
+ else:
555
+ blockrm1 = blockrm
551
556
  Gamma = zeros(Phi.shape,dtype=fdtype)
552
557
  Gamma_autopow = zeros(Phi.shape,dtype=fdtype)
553
558
  J = 0
@@ -562,7 +567,9 @@ class BeamformerTimeTraj( BeamformerTime ):
562
567
  # find index of max power focus point
563
568
  imax = argmax(powPhi)
564
569
  # find backward delays
565
- t_float = (delays[:num,imax,m_index]+n_index).astype(fdtype)
570
+ t_float = (d_interp2[:num,imax,m_index] + \
571
+ d_index[:num,imax,m_index] + \
572
+ n_index).astype(fdtype)
566
573
  # determine max/min delays in sample units
567
574
  # + 2 because we do not want to extrapolate behind the last sample
568
575
  ind_max = t_float.max(0).astype(idtype)+2
@@ -574,7 +581,7 @@ class BeamformerTimeTraj( BeamformerTime ):
574
581
  p_res[ind_min[m]:ind_max[m],m] -= self.damp*interp(
575
582
  t_ind[ind_min[m]:ind_max[m]],
576
583
  t_float[:num,m],
577
- h/blockrm[:num,imax,m],
584
+ h/blockrm1[:num,imax,m],
578
585
  )
579
586
  nextPhi, nextAutopow = self.delay_and_sum(num,p_res,d_interp2,d_index,amp)
580
587
  if self.r_diag:
@@ -21,7 +21,14 @@ RectGrid, BeamformerBase, BeamformerEig, BeamformerOrth, BeamformerCleansc, \
21
21
  MaskedTimeSamples, BeamformerCMF, \
22
22
  BeamformerCapon, BeamformerMusic, BeamformerDamas, BeamformerClean, \
23
23
  BeamformerFunctional, BeamformerDamasPlus, BeamformerGIB, BeamformerGridlessOrth,\
24
- SteeringVector, Environment
24
+ SteeringVector, Environment, BeamformerSODIX
25
+
26
+ # copy CMF classes as workaround so that reference data name is unique later
27
+ class BeamformerCMFLassoLarsBIC (BeamformerCMF):
28
+ pass
29
+
30
+ class BeamformerCMFNNLS (BeamformerCMF):
31
+ pass
25
32
 
26
33
  # if this flag is set to True
27
34
  WRITE_NEW_REFERENCE_DATA = False
@@ -68,14 +75,16 @@ def fbeamformers():
68
75
  bm = BeamformerMusic(freq_data=f, steer=st, n=6, cached = False)
69
76
  bd = BeamformerDamas(beamformer=bb, n_iter=10, cached = False)
70
77
  bdp = BeamformerDamasPlus(beamformer=bb, n_iter=100, cached = False)
71
- bo = BeamformerOrth(beamformer=be, eva_list=list(range(38,54)), cached = False)
78
+ bo = BeamformerOrth(freq_data=f, steer=st, r_diag=True, eva_list=list(range(38,54)), cached = False)
72
79
  bs = BeamformerCleansc(freq_data=f, steer=st, r_diag=True, cached = False)
73
- bcmf = BeamformerCMF(freq_data=f, steer=st, method='LassoLarsBIC', cached = False)
80
+ bcmflassobic = BeamformerCMFLassoLarsBIC(freq_data=f, steer=st, method='LassoLarsBIC', cached = False)
81
+ bcmfnnls = BeamformerCMFNNLS(freq_data=f, steer=st, method='NNLS', cached = False)
74
82
  bl = BeamformerClean(beamformer=bb, n_iter=10, cached = False)
75
83
  bf = BeamformerFunctional(freq_data=f, steer=st, r_diag=False, gamma=3, cached = False)
76
84
  bgib = BeamformerGIB(freq_data=f, steer=st, method= 'LassoLars', n=2, cached = False)
77
85
  bgo = BeamformerGridlessOrth(freq_data=f, steer=st, r_diag=False, n=1, shgo={'n':16}, cached = False)
78
- return (bbase, bc, beig, bm, bl, bo, bs, bd, bcmf, bf, bdp, bgib, bgo)
86
+ bsodix = BeamformerSODIX(freq_data=f, steer=st,max_iter=10, cached = False)
87
+ return (bbase, bc, beig, bm, bl, bo, bs, bd, bcmflassobic, bcmfnnls, bf, bdp, bgib, bgo,bsodix)
79
88
 
80
89
  class acoular_beamformer_test(unittest.TestCase):
81
90
 
@@ -90,7 +99,7 @@ class acoular_beamformer_test(unittest.TestCase):
90
99
  if WRITE_NEW_REFERENCE_DATA:
91
100
  np.save(name,actual_data)
92
101
  ref_data = np.load(name)
93
- np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
102
+ np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
94
103
  # we expect the results to be computed and written to cache
95
104
  acoular.config.global_caching = 'individual'
96
105
  for b in fbeamformers():
@@ -99,7 +108,7 @@ class acoular_beamformer_test(unittest.TestCase):
99
108
  name = join('reference_data',f'{b.__class__.__name__}.npy')
100
109
  actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
101
110
  ref_data = np.load(name)
102
- np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
111
+ np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
103
112
  # we expect the results to be read from cache
104
113
  acoular.config.global_caching = 'all'
105
114
  for b in fbeamformers():
@@ -108,7 +117,7 @@ class acoular_beamformer_test(unittest.TestCase):
108
117
  name = join('reference_data',f'{b.__class__.__name__}.npy')
109
118
  actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
110
119
  ref_data = np.load(name)
111
- np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
120
+ np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
112
121
  # we expect the cached results to be overwritten
113
122
  acoular.config.global_caching = 'overwrite'
114
123
  for b0,b1 in zip(fbeamformers(),fbeamformers()):
@@ -123,7 +132,7 @@ class acoular_beamformer_test(unittest.TestCase):
123
132
  name = join('reference_data',f'{b1.__class__.__name__}.npy')
124
133
  actual_data = np.array([b1.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
125
134
  ref_data = np.load(name)
126
- np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
135
+ np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
127
136
 
128
137
  def test_beamformer_caching(self):
129
138
  # within each subcase, we need new beamformer objects because result is not updated when
@@ -174,6 +183,28 @@ class Test_PowerSpectra(unittest.TestCase):
174
183
  np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
175
184
 
176
185
 
186
+ class TestSteerFormulation(unittest.TestCase):
187
+
188
+ def test_all_steer_formulation(self):
189
+ """ tests all variants of beamformerFreq subroutines
190
+ """
191
+ st = SteeringVector(grid=g, mics=m, env=env)
192
+ b0 = BeamformerBase(freq_data=f, steer=st, cached = False)
193
+ b1 = BeamformerEig(freq_data=f, steer=st, n=54, cached = False)
194
+ for ki,kind in enumerate(('classic', 'inverse','true level', 'true location')):
195
+ st.steer_type = kind
196
+ for b in (b0,b1):
197
+ for dr in (True,False):
198
+ b.r_diag = dr
199
+ with self.subTest(f"{b.__class__.__name__} r_diag:{dr} steer:{kind}"):
200
+ name = join('reference_data',f"{b.__class__.__name__}{dr}{ki+1}.npy")
201
+ actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
202
+ if WRITE_NEW_REFERENCE_DATA:
203
+ np.save(name,actual_data)
204
+ ref_data = np.load(name)
205
+ np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
206
+
207
+
177
208
  if __name__ == '__main__':
178
209
  unittest.main() #exit=False
179
210
 
@@ -0,0 +1,92 @@
1
+ import unittest
2
+ import acoular as ac
3
+ from copy import deepcopy
4
+ class GridTest(unittest.TestCase):
5
+
6
+ @staticmethod
7
+ def get_sector_classes():
8
+ # for later testing condition: sector only includes (0,0,1) point
9
+ sectors = [
10
+ ac.CircSector(x=0,y=0,r=0.2),
11
+ ac.RectSector(x_min=-0.2,x_max=0.2,y_min=-0.2,y_max=0.2),
12
+ ac.RectSector3D(x_min=-0.2,x_max=0.2,y_min=-0.2,y_max=0.2,z_min=1,z_max=1),
13
+ ac.PolySector(edges=[0.2,0.2,-0.2,0.2,-0.2,-0.2,0.2,-0.2]),
14
+ ac.ConvexSector(edges=[0.2,0.2,-0.2,0.2,-0.2,-0.2,0.2,-0.2]),
15
+ ]
16
+ multi_sector = ac.MultiSector(sectors=deepcopy(sectors))
17
+ return sectors + [multi_sector]
18
+
19
+ @staticmethod
20
+ def get_emtpy_sector_classes():
21
+ # for later testing condition: sector should not cover any grid point
22
+ off = 10
23
+ sectors = [
24
+ ac.CircSector(x=off,y=off,r=0.2, include_border=False, default_nearest=False),
25
+ ac.RectSector(
26
+ x_min=-0.2+off,x_max=0.2+off,y_min=-0.2+off,y_max=0.2+off,
27
+ include_border=False, default_nearest=False),
28
+ ac.RectSector3D(
29
+ x_min=-0.2+off,x_max=0.2+off,y_min=-0.2+off,y_max=0.2+off,z_min=1+off,z_max=1+off,
30
+ include_border=False, default_nearest=False),
31
+ ac.PolySector(
32
+ edges=[0.2+off,0.2+off,-0.2+off,0.2+off,-0.2+off,-0.2+off,0.2+off,-0.2+off],
33
+ include_border=False, default_nearest=False),
34
+ ac.ConvexSector(
35
+ edges=[0.2+off,0.2+off,-0.2+off,0.2+off,-0.2+off,-0.2+off,0.2+off,-0.2+off],
36
+ include_border=False, default_nearest=False)]
37
+ multi_sector = ac.MultiSector(
38
+ sectors=deepcopy(sectors))
39
+ return sectors + [multi_sector]
40
+
41
+ @staticmethod
42
+ def get_rectgrid():
43
+ return ac.RectGrid(x_min=-1,x_max=1,y_min=-1,y_max=1,z=1,increment=1)
44
+
45
+ @staticmethod
46
+ def get_rectgrid3D():
47
+ return ac.RectGrid3D(x_min=-1,x_max=1,y_min=-1,y_max=1,z_min=1, z_max=1,increment=1)
48
+
49
+ @staticmethod
50
+ def get_linegrid():
51
+ return ac.LineGrid(loc=(-1,0,1), length=2, numpoints=3)
52
+
53
+ @staticmethod
54
+ def get_importgrid():
55
+ return ac.ImportGrid(gpos_file=GridTest.get_rectgrid().gpos)
56
+
57
+ @staticmethod
58
+ def get_mergegrid():
59
+ return ac.MergeGrid(grids=[GridTest.get_rectgrid(), GridTest.get_linegrid()])
60
+
61
+ @staticmethod
62
+ def get_all_grids():
63
+ for grid in [GridTest.get_rectgrid, GridTest.get_rectgrid3D,
64
+ GridTest.get_linegrid, GridTest.get_importgrid, GridTest.get_mergegrid]:
65
+ yield grid()
66
+
67
+ def test_size(self):
68
+ for grid in self.get_all_grids():
69
+ with self.subTest(grid.__class__.__name__):
70
+ self.assertEqual(grid.size, grid.gpos.shape[-1])
71
+
72
+ def test_existing_subdomain(self):
73
+ for grid in self.get_all_grids():
74
+ for sector in self.get_sector_classes():
75
+ with self.subTest(f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
76
+ indices = grid.subdomain(sector)
77
+ self.assertEqual(indices[0].shape[0], 1)
78
+
79
+ def test_empty_subdomain(self):
80
+ for grid in self.get_all_grids():
81
+ for sector in self.get_emtpy_sector_classes():
82
+ with self.subTest(f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
83
+ indices = grid.subdomain(sector)
84
+ self.assertEqual(indices[0].shape[0], 0)
85
+ if hasattr(sector, 'default_nearest'):
86
+ sector.default_nearest = True
87
+ indices = grid.subdomain(sector)
88
+ self.assertEqual(indices[0].shape[0], 1)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ unittest.main()
@@ -0,0 +1,102 @@
1
+ import unittest
2
+ import acoular as ac
3
+ ac.config.global_caching = "none"
4
+ import numpy as np
5
+ from test_grid import GridTest
6
+ from functools import partial
7
+
8
+ class TestIntegrate(unittest.TestCase):
9
+
10
+ f = [1000,2000]
11
+
12
+ @staticmethod
13
+ def get_sector_args():
14
+ # for later testing condition: sector only includes (0,0,1) point
15
+ return {'RectGrid': np.array([0,0,0.2]),
16
+ 'RectGrid3D' : np.array([0,0,1,0,0,1]),}
17
+
18
+ def get_beamformer(self, grid):
19
+ rng1 = np.random.RandomState(1)
20
+ src_pos = np.array([[0],[0],[1]])
21
+ mics = ac.MicGeom(mpos_tot=rng1.normal(size=3*5).reshape((3,5)))
22
+ steer = ac.SteeringVector(
23
+ grid=ac.ImportGrid(
24
+ gpos_file=src_pos),
25
+ mics=mics)
26
+ H = np.empty((len(self.f),mics.num_mics, 1),dtype=complex)
27
+ for i,_f in enumerate(self.f): # calculate only the indices that are needed
28
+ H[i] = steer.transfer(_f).T # transfer functions
29
+ csm = H@H.swapaxes(2,1).conjugate()
30
+ freq_data = ac.PowerSpectraImport(csm = csm, frequencies=self.f)
31
+ steer.grid = grid
32
+ return ac.BeamformerBase(freq_data=freq_data, steer=steer)
33
+
34
+ def test_sector_class_integration_functional(self):
35
+ for sector in GridTest.get_sector_classes():
36
+ for grid in GridTest.get_all_grids():
37
+ bf = self.get_beamformer(grid)
38
+ with self.subTest(
39
+ f"Grid: {grid.__class__.__name__} Sector: {sector.__class__.__name__}"):
40
+ for i,f in enumerate(self.f):
41
+ bf_res = bf.synthetic(f)
42
+ bf_max = bf_res.max()
43
+ integration_res = ac.integrate(
44
+ data=bf_res,sector=sector,grid=grid)
45
+ self.assertEqual(integration_res.shape, ())
46
+ self.assertEqual(integration_res, bf_max)
47
+
48
+ def test_sector_class_integration_class(self):
49
+ for sector in GridTest.get_sector_classes():
50
+ for grid in GridTest.get_all_grids():
51
+ bf = self.get_beamformer(grid)
52
+ with self.subTest(
53
+ f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
54
+ for i,f in enumerate(self.f):
55
+ bf_res = bf.synthetic(f)
56
+ bf_max = bf_res.max()
57
+ integration_res = bf.integrate(sector)
58
+ self.assertEqual(integration_res.shape, (len(self.f),))
59
+ self.assertEqual(integration_res[i], bf_max)
60
+
61
+ def test_sector_args_integration_functional(self):
62
+ for grid in GridTest.get_all_grids():
63
+ bf = self.get_beamformer(grid)
64
+ with self.subTest(
65
+ f"Grid: {grid.__class__.__name__}"):
66
+ for i,f in enumerate(self.f):
67
+ sector = self.get_sector_args().get(grid.__class__.__name__)
68
+ bf_res = bf.synthetic(f)
69
+ bf_max = bf_res.max()
70
+ if sector is None: # not allowed grid for simple sector args
71
+ sector = np.array([0,0,0.2]) # some random circ sector arguments
72
+ integrate = partial(
73
+ ac.integrate, data=bf_res, grid=grid, sector=sector)
74
+ self.assertRaises(NotImplementedError, integrate)
75
+ else:
76
+ integration_res = ac.integrate(
77
+ data=bf_res,sector=sector,grid=grid)
78
+ self.assertEqual(integration_res.shape, ())
79
+ self.assertEqual(integration_res, bf_max)
80
+
81
+ def test_sector_args_integration_class(self):
82
+ for grid in GridTest.get_all_grids():
83
+ bf = self.get_beamformer(grid)
84
+ with self.subTest(
85
+ f"Grid: {grid.__class__.__name__}"):
86
+ for i,f in enumerate(self.f):
87
+ sector = self.get_sector_args().get(grid.__class__.__name__)
88
+ bf_res = bf.synthetic(f)
89
+ bf_max = bf_res.max()
90
+ if sector is None: # not allowed grid for simple sector args
91
+ sector = np.array([0,0,0.2]) # some random circ sector arguments
92
+ integrate = partial(
93
+ bf.integrate, sector=sector)
94
+ self.assertRaises(NotImplementedError, integrate)
95
+ else:
96
+ integration_res = bf.integrate(sector)
97
+ self.assertEqual(integration_res.shape, (len(self.f),))
98
+ self.assertEqual(integration_res[i], bf_max)
99
+
100
+ if __name__ == "__main__":
101
+
102
+ unittest.main()