acoular 24.10__py3-none-any.whl → 25.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.
- acoular/__init__.py +5 -2
- acoular/aiaa/__init__.py +12 -0
- acoular/{tools → aiaa}/aiaa.py +23 -28
- acoular/base.py +75 -55
- acoular/calib.py +129 -34
- acoular/configuration.py +11 -9
- acoular/demo/__init__.py +1 -0
- acoular/demo/acoular_demo.py +31 -18
- acoular/deprecation.py +85 -0
- acoular/environments.py +481 -229
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +203 -411
- acoular/fprocess.py +233 -123
- acoular/grids.py +793 -424
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +197 -74
- acoular/process.py +660 -149
- acoular/sdinput.py +23 -20
- acoular/signals.py +461 -159
- acoular/sources.py +1311 -489
- acoular/spectra.py +328 -352
- acoular/tbeamform.py +79 -202
- acoular/tfastfuncs.py +21 -21
- acoular/tools/__init__.py +2 -8
- acoular/tools/helpers.py +216 -2
- acoular/tools/metrics.py +4 -4
- acoular/tools/utils.py +106 -200
- acoular/tprocess.py +348 -309
- acoular/traitsviews.py +10 -10
- acoular/trajectory.py +126 -53
- acoular/version.py +2 -2
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/METADATA +39 -17
- acoular-25.3.dist-info/RECORD +56 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/WHEEL +1 -1
- acoular-24.10.dist-info/RECORD +0 -54
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/LICENSE +0 -0
acoular/tools/utils.py
CHANGED
|
@@ -3,208 +3,114 @@
|
|
|
3
3
|
.. autosummary::
|
|
4
4
|
:toctree: generated/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
get_file_basename
|
|
7
|
+
find_basename
|
|
8
|
+
mole_fraction_of_water_vapor
|
|
7
9
|
"""
|
|
8
10
|
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
9
13
|
import numpy as np
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_file_basename(file, alternative_basename='void'):
|
|
17
|
+
"""Return the basename of the file.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
file : str
|
|
22
|
+
File path.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
str
|
|
27
|
+
Basename of the file.
|
|
67
28
|
"""
|
|
29
|
+
basename = Path(file).stem
|
|
30
|
+
return basename if basename else alternative_basename
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def find_basename(source, alternative_basename='void'):
|
|
34
|
+
"""Return the basename of the original source.
|
|
35
|
+
|
|
36
|
+
Traverses the source chain of the object and returns the basename of the original source.
|
|
37
|
+
If the source object does not have a basename, uses the alternative basename.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
source : instance
|
|
42
|
+
:class:`~acoular.base.Generator` derived object
|
|
43
|
+
alternative_basename : str
|
|
44
|
+
Alternative basename to use if the source object does not have a basename.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
str
|
|
50
|
+
Basename of the original source.
|
|
51
|
+
"""
|
|
52
|
+
while source:
|
|
53
|
+
basename = getattr(source, 'basename', None)
|
|
54
|
+
if basename is not None:
|
|
55
|
+
return basename
|
|
56
|
+
source = getattr(source, 'source', None)
|
|
57
|
+
return alternative_basename
|
|
58
|
+
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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)
|
|
60
|
+
def mole_fraction_of_water_vapor(h, t, p=101325):
|
|
61
|
+
r"""Mole fraction of water vapor in the air for real gases.
|
|
62
|
+
|
|
63
|
+
Calculates the mole fraction of water vapor in air from the relative humidity,
|
|
64
|
+
based on the equations provided in the appendix of :cite:`Cramer1993` and
|
|
65
|
+
the enhancement factors from :cite:`Davis1992`.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
h : float
|
|
70
|
+
Relative humidity as a fraction [0,1].
|
|
71
|
+
t : float
|
|
72
|
+
Thermodynamic temperature in K.
|
|
73
|
+
p : float
|
|
74
|
+
Atmospheric pressure in Pa (default is the standard pressure 101325 Pa).
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
float
|
|
79
|
+
Mole fraction of water vapor.
|
|
80
|
+
|
|
81
|
+
Notes
|
|
82
|
+
-----
|
|
83
|
+
The mole fraction is calculated as:
|
|
84
|
+
|
|
85
|
+
.. math::
|
|
86
|
+
x_w = h \cdot f \cdot \frac{p_{sv}}{p},
|
|
87
|
+
|
|
88
|
+
where:
|
|
89
|
+
- :math:`h` is the relative humidity as a fraction [0,1].
|
|
90
|
+
- :math:`f` is the enhancement factor:
|
|
91
|
+
|
|
92
|
+
.. math::
|
|
93
|
+
f = 1.00062 + 3.14 \times 10^{-8} \cdot p + 5.6 \times 10^{-7} \cdot t^2.
|
|
94
|
+
|
|
95
|
+
- :math:`p_{sv}` is the saturation vapor pressure of water vapor in air:
|
|
96
|
+
|
|
97
|
+
.. math::
|
|
98
|
+
p_{sv} = \exp(A \cdot t^2 + B \cdot t + C + \frac{D}{t}),
|
|
99
|
+
|
|
100
|
+
with the updated coefficients from :cite:`Davis1992`:
|
|
101
|
+
|
|
102
|
+
.. math::
|
|
103
|
+
A = 1.2378847 \times 10^{-5}, \\
|
|
104
|
+
B = -1.9121316 \times 10^{-2}, \\
|
|
105
|
+
C = 33.93711047, \\
|
|
106
|
+
D = -6.3431645 \times 10^3.
|
|
107
|
+
|
|
108
|
+
"""
|
|
109
|
+
f = 1.00062 + 3.14 * 10 ** (-8) * p + 5.6 * 10 ** (-7) * t**2 # enhancement factor
|
|
110
|
+
# Saturation vapor pressure using updated coefficients from Davis (1992)
|
|
111
|
+
A = 1.2378847 * 10 ** (-5) # noqa: N806
|
|
112
|
+
B = -1.9121316 * 10 ** (-2) # noqa: N806
|
|
113
|
+
C = 33.93711047 # noqa: N806
|
|
114
|
+
D = -6.3431645 * 10**3 # noqa: N806
|
|
115
|
+
p_sv = np.exp(A * t**2 + B * t + C + D / t) # p_sv in Pa
|
|
116
|
+
return h * f * p_sv / p
|