acoular 24.5__py3-none-any.whl → 24.10__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/tfastfuncs.py CHANGED
@@ -6,9 +6,6 @@
6
6
  import numba as nb
7
7
  import numpy as np
8
8
 
9
- cachedOption = True # if True: saves the numba func as compiled func in sub directory
10
- fastOption = True # fastmath options
11
-
12
9
 
13
10
  @nb.njit(
14
11
  [
@@ -46,11 +43,11 @@ def _delayandsum4(data, offsets, ifactor2, steeramp, out, autopower):
46
43
  """
47
44
  gridsize, numchannels = offsets.shape
48
45
  num = out.shape[0]
49
- ZERO = data.dtype.type(0.0)
46
+ zero_constant = data.dtype.type(0.0)
50
47
  for n in nb.prange(num):
51
48
  for gi in nb.prange(gridsize):
52
- out[n, gi] = ZERO
53
- autopower[n, gi] = ZERO
49
+ out[n, gi] = zero_constant
50
+ autopower[n, gi] = zero_constant
54
51
  for mi in range(numchannels):
55
52
  ind = (gi, mi)
56
53
  r = (
@@ -105,14 +102,16 @@ def _delayandsum5(data, offsets, ifactor2, steeramp, out, autopower):
105
102
  num, gridsize, numchannels = offsets.shape
106
103
  num = out.shape[0]
107
104
  # ZERO = data.dtype.type(0.)
108
- ONE = data.dtype.type(1.0)
105
+ one_constant = data.dtype.type(1.0)
109
106
  for n in nb.prange(num):
110
107
  for gi in nb.prange(gridsize):
111
108
  out[n, gi] = 0
112
109
  autopower[n, gi] = 0
113
110
  for mi in range(numchannels):
114
111
  ind = offsets[n, gi, mi] + n
115
- r = (data[ind, mi] * (ONE - ifactor2[n, gi, mi]) + data[ind + 1, mi] * ifactor2[n, gi, mi]) * steeramp[
112
+ r = (
113
+ data[ind, mi] * (one_constant - ifactor2[n, gi, mi]) + data[ind + 1, mi] * ifactor2[n, gi, mi]
114
+ ) * steeramp[
116
115
  n,
117
116
  gi,
118
117
  mi,
@@ -130,14 +129,14 @@ def _delayandsum5(data, offsets, ifactor2, steeramp, out, autopower):
130
129
  parallel=True,
131
130
  fastmath=True,
132
131
  )
133
- def _steer_I(rm, r0, amp): # noqa: ARG001
132
+ def _steer_I(rm, r0, amp): # noqa: ARG001, N802
134
133
  num, gridsize, numchannels = rm.shape
135
134
  amp[0, 0, 0] = 1.0 / numchannels # to get the same type for rm2 as for rm
136
- Nr = amp[0, 0, 0]
135
+ nr = amp[0, 0, 0]
137
136
  for n in nb.prange(num):
138
137
  for gi in nb.prange(gridsize):
139
138
  for mi in nb.prange(numchannels):
140
- amp[n, gi, mi] = Nr
139
+ amp[n, gi, mi] = nr
141
140
 
142
141
 
143
142
  @nb.njit(
@@ -149,13 +148,13 @@ def _steer_I(rm, r0, amp): # noqa: ARG001
149
148
  parallel=True,
150
149
  fastmath=True,
151
150
  )
152
- def _steer_II(rm, r0, amp):
151
+ def _steer_II(rm, r0, amp): # noqa: N802
153
152
  num, gridsize, numchannels = rm.shape
154
153
  amp[0, 0, 0] = 1.0 / numchannels # to get the same type for rm2 as for rm
155
- Nr = amp[0, 0, 0]
154
+ nr = amp[0, 0, 0]
156
155
  for n in nb.prange(num):
157
156
  for gi in nb.prange(gridsize):
158
- rm2 = np.divide(Nr, r0[n, gi])
157
+ rm2 = np.divide(nr, r0[n, gi])
159
158
  for mi in nb.prange(numchannels):
160
159
  amp[n, gi, mi] = rm[n, gi, mi] * rm2
161
160
 
@@ -169,7 +168,7 @@ def _steer_II(rm, r0, amp):
169
168
  parallel=True,
170
169
  fastmath=True,
171
170
  )
172
- def _steer_III(rm, r0, amp):
171
+ def _steer_III(rm, r0, amp): # noqa: N802
173
172
  num, gridsize, numchannels = rm.shape
174
173
  rm20 = rm[0, 0, 0] - rm[0, 0, 0] # to get the same type for rm2 as for rm
175
174
  rm1 = rm[0, 0, 0] / rm[0, 0, 0]
@@ -192,10 +191,10 @@ def _steer_III(rm, r0, amp):
192
191
  parallel=True,
193
192
  fastmath=True,
194
193
  )
195
- def _steer_IV(rm, r0, amp): # noqa: ARG001
194
+ def _steer_IV(rm, r0, amp): # noqa: ARG001, N802
196
195
  num, gridsize, numchannels = rm.shape
197
196
  amp[0, 0, 0] = np.sqrt(1.0 / numchannels) # to get the same type for rm2 as for rm
198
- Nr = amp[0, 0, 0]
197
+ nr = amp[0, 0, 0]
199
198
  rm1 = rm[0, 0, 0] / rm[0, 0, 0]
200
199
  rm20 = rm[0, 0, 0] - rm[0, 0, 0] # to get the same type for rm2 as for rm
201
200
  for n in nb.prange(num):
@@ -205,7 +204,7 @@ def _steer_IV(rm, r0, amp): # noqa: ARG001
205
204
  rm2 += np.divide(rm1, np.square(rm[n, gi, mi]))
206
205
  rm2 = np.sqrt(rm2)
207
206
  for mi in nb.prange(numchannels):
208
- amp[n, gi, mi] = np.divide(Nr, rm[n, gi, mi] * rm2)
207
+ amp[n, gi, mi] = np.divide(nr, rm[n, gi, mi] * rm2)
209
208
 
210
209
 
211
210
  @nb.njit(
acoular/tools/__init__.py CHANGED
@@ -9,6 +9,7 @@
9
9
  aiaa
10
10
  helpers
11
11
  metrics
12
+ utils
12
13
  """
13
14
 
14
15
  from .aiaa import (
@@ -23,3 +24,4 @@ from .helpers import (
23
24
  return_result,
24
25
  )
25
26
  from .metrics import MetricEvaluator
27
+ from .utils import SamplesBuffer
acoular/tools/aiaa.py CHANGED
@@ -12,8 +12,8 @@ the framework.
12
12
 
13
13
  Examples
14
14
  --------
15
- >>> micgeom = MicAIAABenchmark(name='some_benchmarkdata.h5')
16
- >>> timedata = TimeSamplesAIAABenchmark(name='some_benchmarkdata.h5')
15
+ >>> micgeom = MicAIAABenchmark(name='some_benchmarkdata.h5') # doctest: +SKIP
16
+ >>> timedata = TimeSamplesAIAABenchmark(name='some_benchmarkdata.h5') # doctest: +SKIP
17
17
 
18
18
 
19
19
  .. autosummary::
@@ -25,6 +25,7 @@ Examples
25
25
  MicAIAABenchmark
26
26
  """
27
27
 
28
+ import contextlib
28
29
  from os import path
29
30
 
30
31
  from numpy import array
@@ -53,13 +54,13 @@ class TimeSamplesAIAABenchmark(TimeSamples):
53
54
  objects.
54
55
  """
55
56
 
56
- def load_timedata(self):
57
+ def _load_timedata(self):
57
58
  """Loads timedata from .h5 file. Only for internal use."""
58
59
  self.data = self.h5f.get_data_by_reference('MicrophoneData/microphoneDataPa')
59
60
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
60
61
  (self.numsamples, self.numchannels) = self.data.shape
61
62
 
62
- def load_metadata(self):
63
+ def _load_metadata(self):
63
64
  """Loads metadata from .h5 file. Only for internal use."""
64
65
  self.metadata = {}
65
66
  if '/MetaData' in self.h5f:
@@ -75,7 +76,7 @@ class TriggerAIAABenchmark(TimeSamplesAIAABenchmark):
75
76
  and and provides information about this data.
76
77
  """
77
78
 
78
- def load_timedata(self):
79
+ def _load_timedata(self):
79
80
  """Loads timedata from .h5 file. Only for internal use."""
80
81
  self.data = self.h5f.get_data_by_reference('TachoData/tachoDataV')
81
82
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
@@ -118,10 +119,8 @@ class CsmAIAABenchmark(PowerSpectraImport):
118
119
  # no file there
119
120
  raise OSError('No such file: %s' % self.name)
120
121
  if self.h5f is not None:
121
- try:
122
+ with contextlib.suppress(OSError):
122
123
  self.h5f.close()
123
- except OSError:
124
- pass
125
124
  file = _get_h5file_class()
126
125
  self.h5f = file(self.name)
127
126
 
acoular/tools/helpers.py CHANGED
@@ -24,13 +24,13 @@ from acoular.spectra import synthetic
24
24
 
25
25
  def return_result(source, nmax=-1, num=128):
26
26
  """Collects the output from a
27
- :meth:`SamplesGenerator.result()<acoular.tprocess.SamplesGenerator.result>`
27
+ :meth:`SamplesGenerator.result()<acoular.base.SamplesGenerator.result>`
28
28
  generator and returns an assembled array with all the data.
29
29
 
30
30
  Parameters
31
31
  ----------
32
32
  source: SamplesGenerator or derived object.
33
- This is the :class:`SamplesGenerator<acoular.tprocess.SamplesGenerator>` data source.
33
+ This is the :class:`SamplesGenerator<acoular.base.SamplesGenerator>` data source.
34
34
  nmax: integer
35
35
  With this parameter, a maximum number of output samples can be set
36
36
  (first dimension of array). If set to -1 (default), samples are
acoular/tools/metrics.py CHANGED
@@ -28,7 +28,7 @@ class MetricEvaluator(HasPrivateTraits):
28
28
  """Evaluate the reconstruction performance of source mapping methods.
29
29
 
30
30
  This class can be used to calculate the following performance metrics
31
- according to Herold and Sarradj (2017):
31
+ according :cite:`Herold2017`:
32
32
  * Specific level error
33
33
  * Overall level error
34
34
  * Inverse level error
acoular/tools/utils.py ADDED
@@ -0,0 +1,210 @@
1
+ """Utility classes intended for internal use in Acoular.
2
+
3
+ .. autosummary::
4
+ :toctree: generated/
5
+
6
+ SamplesBuffer
7
+ """
8
+
9
+ import numpy as np
10
+ from traits.api import Any, Array, Bool, Enum, Int, Property, Union
11
+
12
+ from acoular.process import InOut
13
+
14
+
15
+ class SamplesBuffer(InOut):
16
+ """Handles buffering of samples from a source.
17
+
18
+ This class is used to buffer samples from a source and provide them in blocks
19
+ of a specified size. There are several usecases for this class, as demonstrated in
20
+ the following.
21
+
22
+ Examples
23
+ --------
24
+ Let us assume we want to draw blocks of 16 samples from our source, but we want to make sure
25
+ that we always have twice the number of samples in the buffer. We can achieve this simple behaviour
26
+ by using the following code:
27
+
28
+ >>> import acoular as ac
29
+ >>> import numpy as np
30
+ >>> # create a white noise source with 512 samples
31
+ >>> source = ac.TimeSamples(
32
+ ... data=ac.WNoiseGenerator(
33
+ ... sample_freq=64,
34
+ ... numsamples=512,
35
+ ... ).signal()[:, np.newaxis],
36
+ ... sample_freq=64,
37
+ ... )
38
+ >>> # create a buffer with a size of 32 samples
39
+ >>> buffer = ac.tools.SamplesBuffer(source=source, length=32)
40
+ >>> # get the first block of 16 samples
41
+ >>> block = next(buffer.result(num=16))
42
+ >>> np.testing.assert_array_equal(block, source.data[:16])
43
+
44
+ Here, on the first call to the result method, the buffer will fill up by collecting blocks with same size
45
+ from the source. The buffer will then return the first block of 16 samples. On the next call to the result
46
+ method, the buffer will be filled again and returns the next block of 16 samples.
47
+
48
+ In some cases, we might want to draw a different number of samples from the source than we want to return.
49
+ This can be achieved by setting the `source_num` trait of the buffer. A special case is the return of a variable
50
+ number of samples. This is the case, for example, in the class :class:`~acoular.tbeamform.BeamformerTimeTraj`,
51
+ in which a different number of time samples is required from the buffer for further delay-and-sum processing
52
+ depending on the expected delay, which can be vary for moving sources. At the same time, however, only 'num'
53
+ samples should be written to and removed from the buffer. This behavior can be achieved by setting the
54
+ `shift_index_by` trait to 'num' and by setting the `result_num` trait to the number of samples that should be
55
+ returned by the result function.
56
+
57
+ >>> buffer = ac.tools.SamplesBuffer(source=source, length=32, result_num=20, shift_index_by='num')
58
+ >>> block_sizes = []
59
+ >>> block_sizes.append(
60
+ ... next(buffer.result(num=16)).shape[0]
61
+ ... ) # this time, the buffer will return 20 samples, but the buffer will only forget the first 16 samples
62
+ >>> buffer.result_num = 24
63
+ >>> block_sizes.append(
64
+ ... next(buffer.result(num=16)).shape[0]
65
+ ... ) # this time, the buffer will return 24 samples, but the buffer will only forget the first 16 samples
66
+ >>> np.testing.assert_array_equal(block_sizes, [20, 24])
67
+ """
68
+
69
+ #: number of samples that fit in the buffer
70
+ length = Int(desc='number of samples that fit in the buffer')
71
+
72
+ #: number of samples per block to obtain from the source. If 'None', use 'num' argument of result method
73
+ source_num = Union(
74
+ None,
75
+ Int(),
76
+ default_value=None,
77
+ desc='number of samples to return from the source. If "None", use "num" argument of result method',
78
+ )
79
+
80
+ #: number of samples to return from the buffer. If 'None', use 'num' argument of result method
81
+ result_num = Union(
82
+ None,
83
+ Int(),
84
+ default_value=None,
85
+ desc="number of samples to return from the buffer. If 'None', use 'num' argument of result method",
86
+ )
87
+
88
+ #: index shift value for the buffer. If "result_num", buffer will return and forget 'result_num' samples.
89
+ #: If "num", buffer will return 'result_num' samples but will forget 'num' samples
90
+ shift_index_by = Enum(
91
+ ('result_num', 'num'),
92
+ desc=(
93
+ 'index shift value for the buffer. If "result_num", use "result_num" trait.'
94
+ ' If "num", use "num" argument of result method'
95
+ ),
96
+ )
97
+
98
+ #: current filling level of buffer
99
+ level = Property(desc='current filling level of buffer')
100
+
101
+ #: data type of the buffer elements
102
+ dtype = Any(desc='data type of the buffer')
103
+
104
+ # flag to indicate that the source is empty, for internal use
105
+ _empty_source = Bool(False, desc='flag to indicate that the source is empty')
106
+
107
+ # the buffer for processing
108
+ _buffer = Array(shape=(None, None), desc='buffer for block processing')
109
+
110
+ # current index in buffer
111
+ _index = Int(desc='current index in buffer')
112
+
113
+ def _get_level(self):
114
+ return self._buffer.shape[0] - self._index
115
+
116
+ def _create_new_buffer(self):
117
+ self._buffer = np.zeros((self.length, self.numchannels), dtype=self.dtype)
118
+ self._index = self.length
119
+ self._empty_source = False
120
+
121
+ def _write_to_buffer(self, data):
122
+ ns = data.shape[0]
123
+ self._buffer[0 : (self.length - ns)] = self._buffer[-(self.length - ns) :]
124
+ self._buffer[-ns:, :] = data.astype(self.dtype)
125
+ self._index -= ns
126
+
127
+ def increase_buffer(self, num):
128
+ """Increase the buffer by 'num' samples.
129
+
130
+ Returns
131
+ -------
132
+ None
133
+ """
134
+ ar = np.zeros((num, self.numchannels), dtype=self._buffer.dtype)
135
+ self._buffer = np.concatenate((ar, self._buffer), axis=0)
136
+ self._index += num
137
+ self.length += num
138
+
139
+ def read_from_buffer(self, num):
140
+ """Read samples from the buffer.
141
+
142
+ Parameters
143
+ ----------
144
+ num : int
145
+ number of samples to read from the buffer.
146
+
147
+ Returns
148
+ -------
149
+ numpy.ndarray
150
+ block of samples from the buffer
151
+
152
+ """
153
+ rnum = num if self.result_num is None else self.result_num
154
+ rnum = rnum if self.level >= rnum else self.level
155
+ data = self._buffer[self._index : self._index + rnum]
156
+ if self.shift_index_by == 'result_num':
157
+ self._index += rnum
158
+ else:
159
+ self._index += num
160
+ return data
161
+
162
+ def fill_buffer(self, snum):
163
+ """Fill the buffer with samples from the source.
164
+
165
+ Parameters
166
+ ----------
167
+ snum : int
168
+ number of samples to return from the source.
169
+
170
+ Yields
171
+ ------
172
+ None
173
+ """
174
+ source_generator = self.source.result(snum)
175
+ while not self._empty_source:
176
+ while self._index >= snum:
177
+ if self.result_num is not None:
178
+ while self.result_num > self.length:
179
+ self.increase_buffer(snum)
180
+ try:
181
+ self._write_to_buffer(next(source_generator))
182
+ except StopIteration:
183
+ self._empty_source = True
184
+ break
185
+ yield
186
+
187
+ def result(self, num):
188
+ """Return blocks of samples from the buffer.
189
+
190
+ Parameters
191
+ ----------
192
+ num : int
193
+ number of samples to return.
194
+
195
+ Yields
196
+ ------
197
+ numpy.ndarray
198
+ block of samples from the buffer
199
+ """
200
+ self._create_new_buffer()
201
+ snum = num
202
+ if self.source_num is not None:
203
+ snum = self.source_num
204
+ for _ in self.fill_buffer(snum):
205
+ if self.level > 0:
206
+ yield self.read_from_buffer(num)
207
+ else:
208
+ break
209
+ while self.level > 0:
210
+ yield self.read_from_buffer(num)