acoular 25.3.post1__py3-none-any.whl → 25.7__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 +2 -4
- acoular/aiaa/aiaa.py +9 -4
- acoular/base.py +14 -33
- acoular/calib.py +4 -4
- acoular/configuration.py +1 -1
- acoular/demo/__init__.py +6 -1
- acoular/demo/acoular_demo.py +34 -10
- acoular/deprecation.py +14 -2
- acoular/environments.py +6 -5
- acoular/fastFuncs.py +16 -10
- acoular/fbeamform.py +12 -107
- acoular/fprocess.py +3 -32
- acoular/grids.py +145 -38
- acoular/h5cache.py +5 -3
- acoular/h5files.py +10 -0
- acoular/internal.py +4 -0
- acoular/microphones.py +21 -3
- acoular/process.py +3 -45
- acoular/sdinput.py +12 -4
- acoular/signals.py +2 -2
- acoular/sources.py +28 -25
- acoular/spectra.py +17 -17
- acoular/tbeamform.py +3 -0
- acoular/tools/helpers.py +27 -0
- acoular/tools/utils.py +1 -2
- acoular/tprocess.py +1251 -470
- acoular/traitsviews.py +1 -3
- acoular/version.py +4 -3
- {acoular-25.3.post1.dist-info → acoular-25.7.dist-info}/METADATA +3 -3
- acoular-25.7.dist-info/RECORD +56 -0
- acoular-25.3.post1.dist-info/RECORD +0 -56
- {acoular-25.3.post1.dist-info → acoular-25.7.dist-info}/WHEEL +0 -0
- {acoular-25.3.post1.dist-info → acoular-25.7.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.3.post1.dist-info → acoular-25.7.dist-info}/licenses/LICENSE +0 -0
acoular/fbeamform.py
CHANGED
|
@@ -170,6 +170,7 @@ class SteeringVector(HasStrictTraits):
|
|
|
170
170
|
'true level': lambda x: x / einsum('ij,ij->i', x, x.conj())[:, newaxis],
|
|
171
171
|
'true location': lambda x: x / sqrt(einsum('ij,ij->i', x, x.conj()) * x.shape[-1])[:, newaxis],
|
|
172
172
|
},
|
|
173
|
+
transient=True,
|
|
173
174
|
desc='dictionary of frequency domain steering vector functions',
|
|
174
175
|
)
|
|
175
176
|
|
|
@@ -180,6 +181,7 @@ class SteeringVector(HasStrictTraits):
|
|
|
180
181
|
'true level': _steer_III,
|
|
181
182
|
'true location': _steer_IV,
|
|
182
183
|
},
|
|
184
|
+
transient=True,
|
|
183
185
|
desc='dictionary of time domain steering vector functions',
|
|
184
186
|
)
|
|
185
187
|
|
|
@@ -290,7 +292,7 @@ class LazyBfResult:
|
|
|
290
292
|
self.bf = bf
|
|
291
293
|
|
|
292
294
|
def __getitem__(self, key):
|
|
293
|
-
|
|
295
|
+
"""'intelligent' [] operator, checks if results are available and triggers calculation."""
|
|
294
296
|
sl = index_exp[key][0]
|
|
295
297
|
if isinstance(sl, (int, integer)):
|
|
296
298
|
sl = slice(sl, sl + 1)
|
|
@@ -660,18 +662,15 @@ class BeamformerBase(HasStrictTraits):
|
|
|
660
662
|
msg,
|
|
661
663
|
)
|
|
662
664
|
gshape = self.steer.grid.shape
|
|
665
|
+
num_freqs = self.freq_data.fftfreq().shape[0]
|
|
663
666
|
if num == 0 or frange is None:
|
|
664
667
|
if frange is None:
|
|
665
668
|
ind_low = self.freq_data.ind_low
|
|
666
669
|
ind_high = self.freq_data.ind_high
|
|
667
670
|
if ind_low is None:
|
|
668
671
|
ind_low = 0
|
|
669
|
-
if ind_low < 0:
|
|
670
|
-
ind_low += self._numfreq
|
|
671
672
|
if ind_high is None:
|
|
672
|
-
ind_high =
|
|
673
|
-
if ind_high < 0:
|
|
674
|
-
ind_high += self._numfreq
|
|
673
|
+
ind_high = num_freqs
|
|
675
674
|
irange = (ind_low, ind_high)
|
|
676
675
|
num = 0
|
|
677
676
|
elif len(frange) == 2:
|
|
@@ -681,10 +680,10 @@ class BeamformerBase(HasStrictTraits):
|
|
|
681
680
|
raise TypeError(
|
|
682
681
|
msg,
|
|
683
682
|
)
|
|
684
|
-
h = zeros(
|
|
683
|
+
h = zeros(num_freqs, dtype=float)
|
|
685
684
|
sl = slice(*irange)
|
|
686
685
|
r = self.result[sl]
|
|
687
|
-
for i in range(
|
|
686
|
+
for i in range(num_freqs)[sl]:
|
|
688
687
|
# we do this per frequency because r might not have fancy indexing
|
|
689
688
|
h[i] = r[i - sl.start].reshape(gshape)[ind].sum()
|
|
690
689
|
if frange is None:
|
|
@@ -1146,14 +1145,6 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1146
1145
|
See :cite:`Brooks2006` for details.
|
|
1147
1146
|
"""
|
|
1148
1147
|
|
|
1149
|
-
#: (only for backward compatibility) :class:`BeamformerBase` object
|
|
1150
|
-
#: if set, provides :attr:`freq_data`, :attr:`steer`, :attr:`r_diag`
|
|
1151
|
-
#: if not set, these have to be set explicitly.
|
|
1152
|
-
beamformer = Property()
|
|
1153
|
-
|
|
1154
|
-
# private storage of beamformer instance
|
|
1155
|
-
_beamformer = Instance(BeamformerBase)
|
|
1156
|
-
|
|
1157
1148
|
#: The floating-number-precision of the PSFs. Default is 64 bit.
|
|
1158
1149
|
psf_precision = Enum('float64', 'float32', desc='precision of PSF')
|
|
1159
1150
|
|
|
@@ -1172,32 +1163,10 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1172
1163
|
depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['n_iter', 'damp', 'psf_precision'],
|
|
1173
1164
|
)
|
|
1174
1165
|
|
|
1175
|
-
def _get_beamformer(self):
|
|
1176
|
-
return self._beamformer
|
|
1177
|
-
|
|
1178
|
-
def _set_beamformer(self, beamformer):
|
|
1179
|
-
msg = (
|
|
1180
|
-
f"Deprecated use of 'beamformer' trait in class {self.__class__.__name__}. "
|
|
1181
|
-
'Please set :attr:`freq_data`, :attr:`steer`, :attr:`r_diag` directly. '
|
|
1182
|
-
"Using the 'beamformer' trait will be removed in version 25.07."
|
|
1183
|
-
)
|
|
1184
|
-
warn(
|
|
1185
|
-
msg,
|
|
1186
|
-
DeprecationWarning,
|
|
1187
|
-
stacklevel=2,
|
|
1188
|
-
)
|
|
1189
|
-
self._beamformer = beamformer
|
|
1190
|
-
|
|
1191
1166
|
@cached_property
|
|
1192
1167
|
def _get_digest(self):
|
|
1193
1168
|
return digest(self)
|
|
1194
1169
|
|
|
1195
|
-
@on_trait_change('_beamformer.digest')
|
|
1196
|
-
def delegate_beamformer_traits(self):
|
|
1197
|
-
self.freq_data = self.beamformer.freq_data
|
|
1198
|
-
self.r_diag = self.beamformer.r_diag
|
|
1199
|
-
self.steer = self.beamformer.steer
|
|
1200
|
-
|
|
1201
1170
|
def _calc(self, ind):
|
|
1202
1171
|
"""Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1203
1172
|
|
|
@@ -1240,7 +1209,7 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1240
1209
|
self._fr[i] = 1
|
|
1241
1210
|
|
|
1242
1211
|
|
|
1243
|
-
@deprecated_alias({'max_iter': 'n_iter'})
|
|
1212
|
+
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1244
1213
|
class BeamformerDamasPlus(BeamformerDamas):
|
|
1245
1214
|
"""DAMAS deconvolution :cite:`Brooks2006` for solving the system of equations, instead of the
|
|
1246
1215
|
original Gauss-Seidel iterations, this class employs the NNLS or linear programming solvers from
|
|
@@ -1363,14 +1332,6 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1363
1332
|
New faster implementation without explicit (:class:`BeamformerEig`).
|
|
1364
1333
|
"""
|
|
1365
1334
|
|
|
1366
|
-
#: (only for backward compatibility) :class:`BeamformerEig` object
|
|
1367
|
-
#: if set, provides :attr:`freq_data`, :attr:`steer`, :attr:`r_diag`
|
|
1368
|
-
#: if not set, these have to be set explicitly.
|
|
1369
|
-
beamformer = Property()
|
|
1370
|
-
|
|
1371
|
-
# private storage of beamformer instance
|
|
1372
|
-
_beamformer = Instance(BeamformerEig)
|
|
1373
|
-
|
|
1374
1335
|
#: List of components to consider, use this to directly set the eigenvalues
|
|
1375
1336
|
#: used in the beamformer. Alternatively, set :attr:`n`.
|
|
1376
1337
|
eva_list = CArray(dtype=int, value=array([-1]), desc='components')
|
|
@@ -1386,32 +1347,10 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1386
1347
|
depends_on=BEAMFORMER_BASE_DIGEST_DEPENDENCIES + ['eva_list'],
|
|
1387
1348
|
)
|
|
1388
1349
|
|
|
1389
|
-
def _get_beamformer(self):
|
|
1390
|
-
return self._beamformer
|
|
1391
|
-
|
|
1392
|
-
def _set_beamformer(self, beamformer):
|
|
1393
|
-
msg = (
|
|
1394
|
-
f"Deprecated use of 'beamformer' trait in class {self.__class__.__name__}. "
|
|
1395
|
-
'Please set :attr:`freq_data`, :attr:`steer`, :attr:`r_diag` directly. '
|
|
1396
|
-
"Using the 'beamformer' trait will be removed in version 25.07."
|
|
1397
|
-
)
|
|
1398
|
-
warn(
|
|
1399
|
-
msg,
|
|
1400
|
-
DeprecationWarning,
|
|
1401
|
-
stacklevel=2,
|
|
1402
|
-
)
|
|
1403
|
-
self._beamformer = beamformer
|
|
1404
|
-
|
|
1405
1350
|
@cached_property
|
|
1406
1351
|
def _get_digest(self):
|
|
1407
1352
|
return digest(self)
|
|
1408
1353
|
|
|
1409
|
-
@on_trait_change('_beamformer.digest')
|
|
1410
|
-
def delegate_beamformer_traits(self):
|
|
1411
|
-
self.freq_data = self.beamformer.freq_data
|
|
1412
|
-
self.r_diag = self.beamformer.r_diag
|
|
1413
|
-
self.steer = self.beamformer.steer
|
|
1414
|
-
|
|
1415
1354
|
@on_trait_change('n')
|
|
1416
1355
|
def set_eva_list(self):
|
|
1417
1356
|
"""Sets the list of eigenvalues to consider."""
|
|
@@ -1454,7 +1393,7 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1454
1393
|
self._fr[i] = 1
|
|
1455
1394
|
|
|
1456
1395
|
|
|
1457
|
-
@deprecated_alias({'n': 'n_iter'})
|
|
1396
|
+
@deprecated_alias({'n': 'n_iter'}, removal_version='25.10')
|
|
1458
1397
|
class BeamformerCleansc(BeamformerBase):
|
|
1459
1398
|
"""CLEAN-SC deconvolution algorithm.
|
|
1460
1399
|
|
|
@@ -1552,14 +1491,6 @@ class BeamformerClean(BeamformerBase):
|
|
|
1552
1491
|
See :cite:`Hoegbom1974` for details.
|
|
1553
1492
|
"""
|
|
1554
1493
|
|
|
1555
|
-
#: (only for backward compatibility) :class:`BeamformerBase` object
|
|
1556
|
-
#: if set, provides :attr:`freq_data`, :attr:`steer`, :attr:`r_diag`
|
|
1557
|
-
#: if not set, these have to be set explicitly.
|
|
1558
|
-
beamformer = Property()
|
|
1559
|
-
|
|
1560
|
-
# private storage of beamformer instance
|
|
1561
|
-
_beamformer = Instance(BeamformerBase)
|
|
1562
|
-
|
|
1563
1494
|
#: The floating-number-precision of the PSFs. Default is 64 bit.
|
|
1564
1495
|
psf_precision = Enum('float64', 'float32', desc='precision of PSF.')
|
|
1565
1496
|
|
|
@@ -1582,28 +1513,6 @@ class BeamformerClean(BeamformerBase):
|
|
|
1582
1513
|
def _get_digest(self):
|
|
1583
1514
|
return digest(self)
|
|
1584
1515
|
|
|
1585
|
-
def _get_beamformer(self):
|
|
1586
|
-
return self._beamformer
|
|
1587
|
-
|
|
1588
|
-
def _set_beamformer(self, beamformer):
|
|
1589
|
-
msg = (
|
|
1590
|
-
f"Deprecated use of 'beamformer' trait in class {self.__class__.__name__}. "
|
|
1591
|
-
'Please set :attr:`freq_data`, :attr:`steer`, :attr:`r_diag` directly. '
|
|
1592
|
-
"Using the 'beamformer' trait will be removed in version 25.07."
|
|
1593
|
-
)
|
|
1594
|
-
warn(
|
|
1595
|
-
msg,
|
|
1596
|
-
DeprecationWarning,
|
|
1597
|
-
stacklevel=2,
|
|
1598
|
-
)
|
|
1599
|
-
self._beamformer = beamformer
|
|
1600
|
-
|
|
1601
|
-
@on_trait_change('_beamformer.digest')
|
|
1602
|
-
def delegate_beamformer_traits(self):
|
|
1603
|
-
self.freq_data = self.beamformer.freq_data
|
|
1604
|
-
self.r_diag = self.beamformer.r_diag
|
|
1605
|
-
self.steer = self.beamformer.steer
|
|
1606
|
-
|
|
1607
1516
|
def _calc(self, ind):
|
|
1608
1517
|
"""Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1609
1518
|
|
|
@@ -1666,7 +1575,7 @@ class BeamformerClean(BeamformerBase):
|
|
|
1666
1575
|
self._fr[i] = 1
|
|
1667
1576
|
|
|
1668
1577
|
|
|
1669
|
-
@deprecated_alias({'max_iter': 'n_iter'})
|
|
1578
|
+
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1670
1579
|
class BeamformerCMF(BeamformerBase):
|
|
1671
1580
|
"""Covariance Matrix Fitting algorithm.
|
|
1672
1581
|
|
|
@@ -1870,10 +1779,8 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1870
1779
|
factr=10000000.0,
|
|
1871
1780
|
pgtol=1e-05,
|
|
1872
1781
|
epsilon=1e-08,
|
|
1873
|
-
iprint=-1,
|
|
1874
1782
|
maxfun=15000,
|
|
1875
1783
|
maxiter=self.n_iter,
|
|
1876
|
-
disp=None,
|
|
1877
1784
|
callback=None,
|
|
1878
1785
|
maxls=20,
|
|
1879
1786
|
)
|
|
@@ -1895,7 +1802,7 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1895
1802
|
self._fr[i] = 1
|
|
1896
1803
|
|
|
1897
1804
|
|
|
1898
|
-
@deprecated_alias({'max_iter': 'n_iter'})
|
|
1805
|
+
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1899
1806
|
class BeamformerSODIX(BeamformerBase):
|
|
1900
1807
|
"""Source directivity modeling in the cross-spectral matrix (SODIX) algorithm.
|
|
1901
1808
|
|
|
@@ -2040,10 +1947,8 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
2040
1947
|
factr=100.0,
|
|
2041
1948
|
pgtol=1e-12,
|
|
2042
1949
|
epsilon=1e-08,
|
|
2043
|
-
iprint=-1,
|
|
2044
1950
|
maxfun=1500000,
|
|
2045
1951
|
maxiter=self.n_iter,
|
|
2046
|
-
disp=-1,
|
|
2047
1952
|
callback=None,
|
|
2048
1953
|
maxls=20,
|
|
2049
1954
|
)
|
|
@@ -2054,7 +1959,7 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
2054
1959
|
self._fr[i] = 1
|
|
2055
1960
|
|
|
2056
1961
|
|
|
2057
|
-
@deprecated_alias({'max_iter': 'n_iter'})
|
|
1962
|
+
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
2058
1963
|
class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
2059
1964
|
"""Beamforming GIB methods with different normalizations.
|
|
2060
1965
|
|
acoular/fprocess.py
CHANGED
|
@@ -11,11 +11,8 @@ Implements blockwise processing methods in the frequency domain.
|
|
|
11
11
|
IRFFT
|
|
12
12
|
AutoPowerSpectra
|
|
13
13
|
CrossPowerSpectra
|
|
14
|
-
FFTSpectra
|
|
15
14
|
"""
|
|
16
15
|
|
|
17
|
-
from warnings import warn
|
|
18
|
-
|
|
19
16
|
import numpy as np
|
|
20
17
|
from scipy import fft
|
|
21
18
|
from traits.api import Bool, CArray, Enum, Instance, Int, Property, Union, cached_property
|
|
@@ -29,7 +26,7 @@ from .process import SamplesBuffer
|
|
|
29
26
|
from .spectra import BaseSpectra
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
@deprecated_alias({'numfreqs': 'num_freqs', 'numsamples': 'num_samples'}, read_only=True)
|
|
29
|
+
@deprecated_alias({'numfreqs': 'num_freqs', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
|
|
33
30
|
class RFFT(BaseSpectra, SpectraOut):
|
|
34
31
|
"""
|
|
35
32
|
Compute the one-sided Fast Fourier Transform (FFT) for real-valued multichannel time data.
|
|
@@ -170,7 +167,7 @@ class RFFT(BaseSpectra, SpectraOut):
|
|
|
170
167
|
yield fftdata[: j + 1]
|
|
171
168
|
|
|
172
169
|
|
|
173
|
-
@deprecated_alias({'numsamples': 'num_samples'}, read_only=True)
|
|
170
|
+
@deprecated_alias({'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
|
|
174
171
|
class IRFFT(TimeOut):
|
|
175
172
|
"""
|
|
176
173
|
Perform the inverse Fast Fourier Transform (IFFT) for one-sided multi-channel spectra.
|
|
@@ -350,7 +347,7 @@ class AutoPowerSpectra(SpectraOut):
|
|
|
350
347
|
yield ((temp * temp.conjugate()).real * scale).astype(self.precision)
|
|
351
348
|
|
|
352
349
|
|
|
353
|
-
@deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
|
|
350
|
+
@deprecated_alias({'numchannels': 'num_channels'}, read_only=True, removal_version='25.10')
|
|
354
351
|
class CrossPowerSpectra(AutoPowerSpectra):
|
|
355
352
|
"""
|
|
356
353
|
Compute the complex-valued auto- and cross-power spectra from frequency-domain data.
|
|
@@ -450,29 +447,3 @@ class CrossPowerSpectra(AutoPowerSpectra):
|
|
|
450
447
|
csm_flat[i] = csm_lower[:, :nc].reshape(-1)
|
|
451
448
|
csm_upper[...] = 0 # calcCSM adds cumulative
|
|
452
449
|
yield csm_flat[: i + 1] * scale
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
class FFTSpectra(RFFT):
|
|
456
|
-
"""
|
|
457
|
-
Provide the one-sided Fast Fourier Transform (FFT) for multichannel time data.
|
|
458
|
-
|
|
459
|
-
.. deprecated:: 24.10
|
|
460
|
-
The :class:`~acoular.fprocess.FFTSpectra` class is deprecated and will be removed
|
|
461
|
-
in Acoular version 25.07. Please use :class:`~acoular.fprocess.RFFT` instead.
|
|
462
|
-
|
|
463
|
-
Alias for the :class:`~acoular.fprocess.RFFT` class, which computes the one-sided
|
|
464
|
-
Fast Fourier Transform (FFT) for multichannel time data.
|
|
465
|
-
|
|
466
|
-
Warnings
|
|
467
|
-
--------
|
|
468
|
-
This class remains temporarily available for backward compatibility but should not be used in
|
|
469
|
-
new implementations.
|
|
470
|
-
"""
|
|
471
|
-
|
|
472
|
-
def __init__(self, *args, **kwargs):
|
|
473
|
-
super().__init__(*args, **kwargs)
|
|
474
|
-
warn(
|
|
475
|
-
'Using FFTSpectra is deprecated and will be removed in Acoular version 25.07. Use class RFFT instead.',
|
|
476
|
-
DeprecationWarning,
|
|
477
|
-
stacklevel=2,
|
|
478
|
-
)
|
acoular/grids.py
CHANGED
|
@@ -28,6 +28,7 @@ Implement support for multidimensional grids and integration sectors.
|
|
|
28
28
|
# imports from other packages
|
|
29
29
|
import xml.dom.minidom
|
|
30
30
|
from abc import abstractmethod
|
|
31
|
+
from pathlib import Path
|
|
31
32
|
|
|
32
33
|
from numpy import (
|
|
33
34
|
absolute,
|
|
@@ -301,7 +302,7 @@ class Polygon:
|
|
|
301
302
|
return mindst
|
|
302
303
|
|
|
303
304
|
|
|
304
|
-
@deprecated_alias({'gpos': 'pos'})
|
|
305
|
+
@deprecated_alias({'gpos': 'pos'}, removal_version='25.10')
|
|
305
306
|
class Grid(ABCHasStrictTraits):
|
|
306
307
|
"""
|
|
307
308
|
Abstract base class for grid geometries.
|
|
@@ -309,6 +310,16 @@ class Grid(ABCHasStrictTraits):
|
|
|
309
310
|
This class defines a common interface for all grid geometries and provides tools to query grid
|
|
310
311
|
properties and related data. It is intended to serve as a base class for specialized grid
|
|
311
312
|
implementations and should not be instantiated directly as it lacks concrete functionality.
|
|
313
|
+
|
|
314
|
+
.. _units_note_grids:
|
|
315
|
+
|
|
316
|
+
Unit System
|
|
317
|
+
-----------
|
|
318
|
+
The source code is agnostic to the unit of length. The positions' coordinates are assumed to be
|
|
319
|
+
in meters. This is consistent with the standard :class:`~acoular.environments.Environment` class
|
|
320
|
+
which uses the speed of sound at 20°C at sea level under standard atmosphere pressure in m/s.
|
|
321
|
+
If the positions' coordinates are provided in a unit other than meter, it is advisable to change
|
|
322
|
+
the :attr:`~acoular.environments.Environment.c` attribute to match the given unit.
|
|
312
323
|
"""
|
|
313
324
|
|
|
314
325
|
#: The total number of grid points. This property is automatically calculated based on other
|
|
@@ -321,6 +332,7 @@ class Grid(ABCHasStrictTraits):
|
|
|
321
332
|
|
|
322
333
|
#: The grid positions represented as a (3, :attr:`size`) array of :class:`floats<float>`.
|
|
323
334
|
#: (read-only)
|
|
335
|
+
#: All positions' coordinates are in meters by default (see :ref:`notes <units_note_grids>`).
|
|
324
336
|
pos = Property(desc='x, y, z positions of grid points')
|
|
325
337
|
|
|
326
338
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
@@ -375,8 +387,77 @@ class Grid(ABCHasStrictTraits):
|
|
|
375
387
|
# return indices of "True" entries
|
|
376
388
|
return where(xyi)
|
|
377
389
|
|
|
390
|
+
def export_gpos(self, filename):
|
|
391
|
+
"""
|
|
392
|
+
Export the grid positions to an XML file.
|
|
393
|
+
|
|
394
|
+
This method generates an XML file containing the positions of all grid points.
|
|
395
|
+
Each point is represented by a ``<pos>`` element with ``Name``, ``x``, ``y``, and ``z``
|
|
396
|
+
attributes. The generated XML is formatted to match the structure required for importing
|
|
397
|
+
into the :class:`ImportGrid` class.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
filename : :class:`str`
|
|
402
|
+
The path to the file to which the grid positions will be written. The file
|
|
403
|
+
extension must be ``.xml``.
|
|
404
|
+
|
|
405
|
+
Raises
|
|
406
|
+
------
|
|
407
|
+
:obj:`OSError`
|
|
408
|
+
If the file cannot be written due to permissions issues or invalid file paths.
|
|
409
|
+
|
|
410
|
+
Notes
|
|
411
|
+
-----
|
|
412
|
+
- The file will be saved in UTF-8 encoding.
|
|
413
|
+
- The ``Name`` attribute for each point is set as ``"Point {i+1}"``, where ``i`` is the
|
|
414
|
+
index of the grid point.
|
|
415
|
+
- If subgrids are defined, they will be included as the ``subgrid`` attribute.
|
|
416
|
+
|
|
417
|
+
Examples
|
|
418
|
+
--------
|
|
419
|
+
Export a grid with 100 points to an XML file:
|
|
420
|
+
|
|
421
|
+
>>> import acoular as ac
|
|
422
|
+
>>> import numpy as np
|
|
423
|
+
>>> grid = ac.ImportGrid()
|
|
424
|
+
>>> # Create some grid points
|
|
425
|
+
>>> points = np.arange(9).reshape(3, 3)
|
|
426
|
+
>>> grid.pos = points
|
|
427
|
+
>>> grid.export_gpos('grid_points.xml') # doctest: +SKIP
|
|
428
|
+
|
|
429
|
+
The generated ``grid_points.xml`` file will look like this:
|
|
430
|
+
|
|
431
|
+
.. code-block:: xml
|
|
378
432
|
|
|
379
|
-
|
|
433
|
+
<?xml version="1.1" encoding="utf-8"?><Grid name="grid_points">
|
|
434
|
+
<pos Name="Point 1" x="0" y="1" z="2"/>
|
|
435
|
+
<pos Name="Point 2" x="3" y="4" z="5"/>
|
|
436
|
+
<pos Name="Point 3" x="6" y="7" z="8"/>
|
|
437
|
+
</Grid>
|
|
438
|
+
"""
|
|
439
|
+
filepath = Path(filename)
|
|
440
|
+
basename = filepath.stem
|
|
441
|
+
with filepath.open('w', encoding='utf-8') as f:
|
|
442
|
+
f.write(f'<?xml version="1.1" encoding="utf-8"?><Grid name="{basename}">\n')
|
|
443
|
+
for i in range(self.pos.shape[-1]):
|
|
444
|
+
has_subgrids = hasattr(self, 'subgrids') and len(self.subgrids) > i
|
|
445
|
+
subgrid_attr = f'subgrid="{self.subgrids[i]}"' if has_subgrids else ''
|
|
446
|
+
pos_str = ' '.join(
|
|
447
|
+
[
|
|
448
|
+
' <pos',
|
|
449
|
+
f'Name="Point {i+1}"',
|
|
450
|
+
f'x="{self.pos[0, i]}"',
|
|
451
|
+
f'y="{self.pos[1, i]}"',
|
|
452
|
+
f'z="{self.pos[2, i]}"',
|
|
453
|
+
f'{subgrid_attr}/>\n',
|
|
454
|
+
]
|
|
455
|
+
)
|
|
456
|
+
f.write(pos_str)
|
|
457
|
+
f.write('</Grid>')
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@deprecated_alias({'gpos': 'pos'}, read_only=True, removal_version='25.10')
|
|
380
461
|
class RectGrid(Grid):
|
|
381
462
|
"""
|
|
382
463
|
Provides a 2D Cartesian grid for beamforming results.
|
|
@@ -409,6 +490,9 @@ class RectGrid(Grid):
|
|
|
409
490
|
#: Number of grid points along y-axis. (read-only)
|
|
410
491
|
nysteps = Property(desc='number of grid points along y-axis')
|
|
411
492
|
|
|
493
|
+
#: The grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form. (read-only)
|
|
494
|
+
extent = Property(desc='grid extent as (x_min, x_max, y_min, y_max)')
|
|
495
|
+
|
|
412
496
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
413
497
|
digest = Property(
|
|
414
498
|
depends_on=['x_min', 'x_max', 'y_min', 'y_max', 'z', 'increment'],
|
|
@@ -450,6 +534,56 @@ class RectGrid(Grid):
|
|
|
450
534
|
bpos.resize((3, self.size))
|
|
451
535
|
return bpos
|
|
452
536
|
|
|
537
|
+
@property_depends_on(['x_min', 'x_max', 'y_min', 'y_max'])
|
|
538
|
+
def _get_extent(self):
|
|
539
|
+
# Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
540
|
+
#
|
|
541
|
+
# Returns
|
|
542
|
+
# -------
|
|
543
|
+
# :class:`tuple` of :class:`floats<float>`
|
|
544
|
+
# (:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
545
|
+
# extent.
|
|
546
|
+
#
|
|
547
|
+
# Notes
|
|
548
|
+
# -----
|
|
549
|
+
# This property is intended for use with the ``extent`` parameter of
|
|
550
|
+
# :obj:`matplotlib.pyplot.imshow`.
|
|
551
|
+
#
|
|
552
|
+
# Examples
|
|
553
|
+
# --------
|
|
554
|
+
# >>> from acoular import RectGrid
|
|
555
|
+
# >>> grid = RectGrid()
|
|
556
|
+
# >>> grid.y_min = -5
|
|
557
|
+
# >>> grid.y_max = 5
|
|
558
|
+
# >>> grid.extent
|
|
559
|
+
# (-1.0, 1.0, -5.0, 5.0)
|
|
560
|
+
return (self.x_min, self.x_max, self.y_min, self.y_max)
|
|
561
|
+
|
|
562
|
+
def extend(self):
|
|
563
|
+
"""
|
|
564
|
+
Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
565
|
+
|
|
566
|
+
Returns
|
|
567
|
+
-------
|
|
568
|
+
:class:`tuple` of :class:`floats<float>`
|
|
569
|
+
(:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
570
|
+
extent.
|
|
571
|
+
|
|
572
|
+
Notes
|
|
573
|
+
-----
|
|
574
|
+
This method is deprecated. Use the :attr:`extent` property instead.
|
|
575
|
+
"""
|
|
576
|
+
import warnings
|
|
577
|
+
|
|
578
|
+
msg = ' '.join(
|
|
579
|
+
[
|
|
580
|
+
"Deprecated use of 'extend' method (will be removed in version 26.04).",
|
|
581
|
+
"Please use the 'extent' trait instead.",
|
|
582
|
+
]
|
|
583
|
+
)
|
|
584
|
+
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
585
|
+
return self.extent
|
|
586
|
+
|
|
453
587
|
def index(self, x, y):
|
|
454
588
|
"""
|
|
455
589
|
Find the indices of a grid point near a given coordinate.
|
|
@@ -539,34 +673,6 @@ class RectGrid(Grid):
|
|
|
539
673
|
return array(xis), array(yis)
|
|
540
674
|
# return arange(self.size)[inds]
|
|
541
675
|
|
|
542
|
-
def extend(self):
|
|
543
|
-
"""
|
|
544
|
-
Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
545
|
-
|
|
546
|
-
Returns
|
|
547
|
-
-------
|
|
548
|
-
:class:`tuple` of :class:`floats<float>`
|
|
549
|
-
(:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
550
|
-
extent.
|
|
551
|
-
|
|
552
|
-
Notes
|
|
553
|
-
-----
|
|
554
|
-
- ``pylab.imhow`` is the same as :obj:`matplotlib.pyplot.imshow`. It's only using a
|
|
555
|
-
different namespace.
|
|
556
|
-
- The return of the method is ment for the ``extent`` parameter of
|
|
557
|
-
:obj:`matplotlib.pyplot.imshow`.
|
|
558
|
-
|
|
559
|
-
Examples
|
|
560
|
-
--------
|
|
561
|
-
>>> from acoular import RectGrid
|
|
562
|
-
>>> grid = RectGrid()
|
|
563
|
-
>>> grid.y_min = -5
|
|
564
|
-
>>> grid.y_max = 5
|
|
565
|
-
>>> grid.extend()
|
|
566
|
-
(-1.0, 1.0, -5.0, 5.0)
|
|
567
|
-
"""
|
|
568
|
-
return (self.x_min, self.x_max, self.y_min, self.y_max)
|
|
569
|
-
|
|
570
676
|
|
|
571
677
|
class RectGrid3D(RectGrid):
|
|
572
678
|
"""
|
|
@@ -575,10 +681,10 @@ class RectGrid3D(RectGrid):
|
|
|
575
681
|
The grid has cubic or nearly cubic cells. It is defined by lower and upper x-, y- and z-limits.
|
|
576
682
|
"""
|
|
577
683
|
|
|
578
|
-
#: The lower z-limit that defines the grid. Default is ``-1``.
|
|
684
|
+
#: The lower z-limit that defines the grid. Default is ``-1.0``.
|
|
579
685
|
z_min = Float(-1.0, desc='minimum z-value')
|
|
580
686
|
|
|
581
|
-
#: The upper z-limit that defines the grid. Default is ``1``.
|
|
687
|
+
#: The upper z-limit that defines the grid. Default is ``1.0``.
|
|
582
688
|
z_max = Float(1.0, desc='maximum z-value')
|
|
583
689
|
|
|
584
690
|
#: Number of grid points along x-axis. (read-only)
|
|
@@ -760,7 +866,7 @@ class RectGrid3D(RectGrid):
|
|
|
760
866
|
return s_[xi1 : xi2 + 1], s_[yi1 : yi2 + 1], s_[zi1 : zi2 + 1]
|
|
761
867
|
|
|
762
868
|
|
|
763
|
-
@deprecated_alias({'from_file': 'file', 'gpos_file': 'pos'})
|
|
869
|
+
@deprecated_alias({'from_file': 'file', 'gpos_file': 'pos'}, removal_version='25.10')
|
|
764
870
|
class ImportGrid(Grid):
|
|
765
871
|
"""
|
|
766
872
|
Load a 3D grid from an XML file.
|
|
@@ -770,7 +876,7 @@ class ImportGrid(Grid):
|
|
|
770
876
|
"""
|
|
771
877
|
|
|
772
878
|
#: Name of the .xml-file from which to read the data.
|
|
773
|
-
file = File(filter=['*.xml'], exists=True, desc='name of the xml file to import')
|
|
879
|
+
file = Union(None, File(filter=['*.xml'], exists=True), desc='name of the xml file to import')
|
|
774
880
|
|
|
775
881
|
_gpos = CArray(dtype=float, desc='x, y, z position of all Grid Points')
|
|
776
882
|
|
|
@@ -928,7 +1034,7 @@ class ImportGrid(Grid):
|
|
|
928
1034
|
self.subgrids = array(names)
|
|
929
1035
|
|
|
930
1036
|
|
|
931
|
-
@deprecated_alias({'gpos': 'pos', 'numpoints': 'num_points'}, read_only=['gpos'])
|
|
1037
|
+
@deprecated_alias({'gpos': 'pos', 'numpoints': 'num_points'}, read_only=['gpos'], removal_version='25.10')
|
|
932
1038
|
class LineGrid(Grid):
|
|
933
1039
|
"""
|
|
934
1040
|
Define a 3D grid for a line geometry.
|
|
@@ -976,8 +1082,9 @@ class LineGrid(Grid):
|
|
|
976
1082
|
#: are set. (read-only)
|
|
977
1083
|
size = Property(desc='overall number of grid points')
|
|
978
1084
|
|
|
979
|
-
#: A (3, :attr:`size`) array containing the x, y, and z positions
|
|
980
|
-
#:
|
|
1085
|
+
#: A (3, :attr:`size`) array containing the x, y, and z positions of the grid points.
|
|
1086
|
+
#: (read-only)
|
|
1087
|
+
#: All positions' coordinates are in meters by default (see :ref:`notes <units_note_grids>`).
|
|
981
1088
|
pos = Property(desc='x, y, z positions of grid points')
|
|
982
1089
|
|
|
983
1090
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
@@ -1008,7 +1115,7 @@ class LineGrid(Grid):
|
|
|
1008
1115
|
return pos.T
|
|
1009
1116
|
|
|
1010
1117
|
|
|
1011
|
-
@deprecated_alias({'gpos': 'pos'}, read_only=True)
|
|
1118
|
+
@deprecated_alias({'gpos': 'pos'}, read_only=True, removal_version='25.10')
|
|
1012
1119
|
class MergeGrid(Grid):
|
|
1013
1120
|
"""
|
|
1014
1121
|
Base class for merging multiple grid geometries.
|
acoular/h5cache.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
"""Implements a cache for HDF5 files used in Acoular."""
|
|
6
|
+
|
|
6
7
|
import gc
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from weakref import WeakValueDictionary
|
|
@@ -92,10 +93,11 @@ class HDF5Cache(HasStrictTraits):
|
|
|
92
93
|
return # cachefile is not created in readonly mode
|
|
93
94
|
|
|
94
95
|
if isinstance(obj.h5f, file_cls):
|
|
95
|
-
|
|
96
|
+
h5filename = Path(obj.h5f.filename).resolve()
|
|
97
|
+
if h5filename == filename:
|
|
96
98
|
self.busy = False
|
|
97
99
|
return
|
|
98
|
-
self._decrease_file_reference_counter(
|
|
100
|
+
self._decrease_file_reference_counter(h5filename)
|
|
99
101
|
|
|
100
102
|
if filename not in self.open_files: # or tables.file._open_files.filenames
|
|
101
103
|
if config.global_caching == 'readonly':
|
acoular/h5files.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
+
"""Implements base classes for handling HDF5 files."""
|
|
6
|
+
|
|
5
7
|
from .configuration import config
|
|
6
8
|
|
|
7
9
|
|
|
@@ -57,6 +59,8 @@ if config.have_tables:
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
class H5FileTables(H5FileBase, tables.File):
|
|
62
|
+
"""Hdf5 File based on PyTables."""
|
|
63
|
+
|
|
60
64
|
def create_extendable_array(self, nodename, shape, precision, group=None):
|
|
61
65
|
if not group:
|
|
62
66
|
group = self.root
|
|
@@ -106,6 +110,8 @@ if config.have_tables:
|
|
|
106
110
|
return result
|
|
107
111
|
|
|
108
112
|
class H5CacheFileTables(H5FileTables, H5CacheFileBase):
|
|
113
|
+
"""Hdf5 Cache File based on PyTables."""
|
|
114
|
+
|
|
109
115
|
compression_filter = tables.Filters(complevel=5, complib='blosc')
|
|
110
116
|
|
|
111
117
|
def is_cached(self, nodename, group=None):
|
|
@@ -124,6 +130,8 @@ if config.have_h5py:
|
|
|
124
130
|
import h5py
|
|
125
131
|
|
|
126
132
|
class H5FileH5py(H5FileBase, h5py.File):
|
|
133
|
+
"""Hdf5 File based on h5py."""
|
|
134
|
+
|
|
127
135
|
def _get_in_file_path(self, nodename, group=None):
|
|
128
136
|
if not group:
|
|
129
137
|
return '/' + nodename
|
|
@@ -182,6 +190,8 @@ if config.have_h5py:
|
|
|
182
190
|
return result
|
|
183
191
|
|
|
184
192
|
class H5CacheFileH5py(H5CacheFileBase, H5FileH5py):
|
|
193
|
+
"""Hdf5 Cache File based on h5py."""
|
|
194
|
+
|
|
185
195
|
compression_filter = 'lzf'
|
|
186
196
|
# compression_filter = 'blosc' # unavailable...
|
|
187
197
|
|