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.
- acoular/__init__.py +2 -2
- acoular/configuration.py +37 -1
- acoular/environments.py +15 -9
- acoular/fastFuncs.py +199 -472
- acoular/fbeamform.py +168 -109
- acoular/grids.py +33 -114
- acoular/sources.py +77 -3
- acoular/spectra.py +2 -2
- acoular/tbeamform.py +15 -8
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/test_beamformer_results.py +39 -8
- acoular/tests/test_grid.py +92 -0
- acoular/tests/test_integrate.py +102 -0
- acoular/tests/test_tprocess.py +52 -0
- acoular/tests/test_traj_beamformer_results.py +2 -2
- acoular/tfastfuncs.py +24 -25
- acoular/tools.py +144 -2
- acoular/tprocess.py +91 -102
- acoular/version.py +2 -2
- acoular-24.3.dist-info/METADATA +181 -0
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/RECORD +62 -25
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/WHEEL +1 -1
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/LICENSE +1 -1
- acoular/tests/reference_data/BeamformerCMF.npy +0 -0
- acoular-23.6.dist-info/METADATA +0 -82
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/fbeamform.py
CHANGED
|
@@ -43,12 +43,19 @@ invert, dot, newaxis, zeros, linalg, \
|
|
|
43
43
|
searchsorted, pi, sign, diag, arange, sqrt, log10, \
|
|
44
44
|
reshape, hstack, vstack, eye, tril, size, clip, tile, round, delete, \
|
|
45
45
|
absolute, argsort, sum, hsplit, fill_diagonal, zeros_like, \
|
|
46
|
-
einsum, ndarray, isscalar, inf, real, unique
|
|
46
|
+
einsum, ndarray, isscalar, inf, real, unique, atleast_2d, einsum_path,trace
|
|
47
47
|
|
|
48
48
|
from numpy.linalg import norm
|
|
49
49
|
|
|
50
50
|
from sklearn.linear_model import LassoLars, LassoLarsCV, LassoLarsIC,\
|
|
51
|
-
OrthogonalMatchingPursuitCV
|
|
51
|
+
OrthogonalMatchingPursuitCV, LinearRegression
|
|
52
|
+
|
|
53
|
+
#check for sklearn version to account for incompatible behavior
|
|
54
|
+
import sklearn
|
|
55
|
+
from packaging.version import parse
|
|
56
|
+
sklearn_ndict = {}
|
|
57
|
+
if parse(sklearn.__version__)<parse('1.4'):
|
|
58
|
+
sklearn_ndict['normalize'] = False
|
|
52
59
|
|
|
53
60
|
from scipy.optimize import nnls, linprog, fmin_l_bfgs_b, shgo
|
|
54
61
|
from scipy.linalg import inv, eigh, eigvals, fractional_matrix_power
|
|
@@ -170,7 +177,7 @@ class SteeringVector( HasPrivateTraits ):
|
|
|
170
177
|
|
|
171
178
|
@property_depends_on('grid.digest, mics.digest, env.digest')
|
|
172
179
|
def _get_rm ( self ):
|
|
173
|
-
return self.env._r(self.grid.pos(), self.mics.mpos)
|
|
180
|
+
return atleast_2d(self.env._r(self.grid.pos(), self.mics.mpos))
|
|
174
181
|
|
|
175
182
|
@cached_property
|
|
176
183
|
def _get_digest( self ):
|
|
@@ -681,8 +688,14 @@ class BeamformerBase( HasPrivateTraits ):
|
|
|
681
688
|
# return h.reshape(h.shape[0], prod(h.shape[1:])).sum(axis=1)
|
|
682
689
|
if isinstance(sector, Sector):
|
|
683
690
|
ind = self.steer.grid.subdomain(sector)
|
|
684
|
-
|
|
691
|
+
elif hasattr(self.steer.grid, 'indices'):
|
|
685
692
|
ind = self.steer.grid.indices(*sector)
|
|
693
|
+
else:
|
|
694
|
+
raise NotImplementedError(
|
|
695
|
+
f'Grid of type {self.steer.grid.__class__.__name__} does not have an indices method! '
|
|
696
|
+
f'Please use a sector derived instance of type :class:`~acoular.grids.Sector` '
|
|
697
|
+
'instead of type numpy.array.'
|
|
698
|
+
)
|
|
686
699
|
gshape = self.steer.grid.shape
|
|
687
700
|
r = self.result
|
|
688
701
|
h = zeros(r.shape[0])
|
|
@@ -1415,51 +1428,56 @@ class BeamformerDamasPlus (BeamformerDamas):
|
|
|
1415
1428
|
p.freq = f[i]
|
|
1416
1429
|
psf = p.psf[:]
|
|
1417
1430
|
|
|
1418
|
-
if self.method ==
|
|
1419
|
-
|
|
1420
|
-
elif self.method ==
|
|
1431
|
+
if self.method == "NNLS":
|
|
1432
|
+
ac[i] = nnls(psf, y)[0] / unit
|
|
1433
|
+
elif self.method == "LP": # linear programming (Dougherty)
|
|
1421
1434
|
if self.r_diag:
|
|
1422
|
-
warn(
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1435
|
+
warn(
|
|
1436
|
+
"Linear programming solver may fail when CSM main "
|
|
1437
|
+
"diagonal is removed for delay-and-sum beamforming.",
|
|
1438
|
+
Warning,
|
|
1439
|
+
stacklevel=5,
|
|
1440
|
+
)
|
|
1441
|
+
cT = -1 * psf.sum(1) # turn the minimization into a maximization
|
|
1442
|
+
ac[i] = (
|
|
1443
|
+
linprog(c=cT, A_ub=psf, b_ub=y).x / unit
|
|
1444
|
+
) # defaults to simplex method and non-negative x
|
|
1445
|
+
else:
|
|
1446
|
+
if self.method == "LassoLars":
|
|
1447
|
+
model = LassoLars(
|
|
1448
|
+
alpha=self.alpha * unit, max_iter=self.max_iter
|
|
1449
|
+
)
|
|
1450
|
+
elif self.method == "OMPCV":
|
|
1451
|
+
model = OrthogonalMatchingPursuitCV()
|
|
1452
|
+
else:
|
|
1453
|
+
raise NotImplementedError(f"%model solver not implemented")
|
|
1454
|
+
model.normalize = False
|
|
1455
|
+
# from sklearn 1.2, normalize=True does not work the same way anymore and the pipeline approach
|
|
1456
|
+
# with StandardScaler does scale in a different way, thus we monkeypatch the code and normalize
|
|
1457
|
+
# ourselves to make results the same over different sklearn versions
|
|
1458
|
+
norms = norm(psf, axis=0)
|
|
1459
|
+
# get rid of annoying sklearn warnings that appear
|
|
1460
|
+
# for sklearn<1.2 despite any settings
|
|
1461
|
+
with warnings.catch_warnings():
|
|
1462
|
+
warnings.simplefilter("ignore", category=FutureWarning)
|
|
1463
|
+
# normalized psf
|
|
1464
|
+
model.fit(psf / norms, y)
|
|
1465
|
+
# recover normalization in the coef's
|
|
1466
|
+
ac[i] = model.coef_[:] / norms / unit
|
|
1439
1467
|
|
|
1440
1468
|
fr[i] = 1
|
|
1441
1469
|
|
|
1442
|
-
|
|
1470
|
+
|
|
1471
|
+
class BeamformerOrth( BeamformerBase ):
|
|
1443
1472
|
"""
|
|
1444
|
-
Orthogonal
|
|
1445
|
-
|
|
1473
|
+
Orthogonal deconvolution, see :ref:`Sarradj, 2010<Sarradj2010>`.
|
|
1474
|
+
New faster implementation without explicit (:class:`BeamformerEig`).
|
|
1446
1475
|
"""
|
|
1447
|
-
|
|
1448
|
-
#:
|
|
1476
|
+
#: (only for backward compatibility) :class:`BeamformerEig` object
|
|
1477
|
+
#: if set, provides :attr:`freq_data`, :attr:`steer`, :attr:`r_diag`
|
|
1478
|
+
#: if not set, these have to be set explicitly
|
|
1449
1479
|
beamformer = Trait(BeamformerEig)
|
|
1450
1480
|
|
|
1451
|
-
#: :class:`~acoular.spectra.PowerSpectra` object that provides the cross spectral matrix
|
|
1452
|
-
#: and eigenvalues, is set automatically.
|
|
1453
|
-
freq_data = Delegate('beamformer')
|
|
1454
|
-
|
|
1455
|
-
#: Flag, if 'True' (default), the main diagonal is removed before beamforming,
|
|
1456
|
-
#: is set automatically.
|
|
1457
|
-
r_diag = Delegate('beamformer')
|
|
1458
|
-
|
|
1459
|
-
#: instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes,
|
|
1460
|
-
#: that contains information about the steering vector. Is set automatically.
|
|
1461
|
-
steer = Delegate('beamformer')
|
|
1462
|
-
|
|
1463
1481
|
#: List of components to consider, use this to directly set the eigenvalues
|
|
1464
1482
|
#: used in the beamformer. Alternatively, set :attr:`n`.
|
|
1465
1483
|
eva_list = CArray(dtype=int,
|
|
@@ -1473,14 +1491,10 @@ class BeamformerOrth (BeamformerBase):
|
|
|
1473
1491
|
|
|
1474
1492
|
# internal identifier
|
|
1475
1493
|
digest = Property(
|
|
1476
|
-
depends_on = ['
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
# internal identifier
|
|
1480
|
-
ext_digest = Property(
|
|
1481
|
-
depends_on = ['digest', 'beamformer.ext_digest'],
|
|
1494
|
+
depends_on = ['freq_data.digest', '_steer_obj.digest', 'r_diag',
|
|
1495
|
+
'eva_list'],
|
|
1482
1496
|
)
|
|
1483
|
-
|
|
1497
|
+
|
|
1484
1498
|
@cached_property
|
|
1485
1499
|
def _get_digest( self ):
|
|
1486
1500
|
return digest( self )
|
|
@@ -1488,7 +1502,13 @@ class BeamformerOrth (BeamformerBase):
|
|
|
1488
1502
|
@cached_property
|
|
1489
1503
|
def _get_ext_digest( self ):
|
|
1490
1504
|
return digest( self, 'ext_digest' )
|
|
1491
|
-
|
|
1505
|
+
|
|
1506
|
+
@on_trait_change('beamformer.digest')
|
|
1507
|
+
def delegate_beamformer_traits(self):
|
|
1508
|
+
self.freq_data = self.beamformer.freq_data
|
|
1509
|
+
self.r_diag = self.beamformer.r_diag
|
|
1510
|
+
self.steer = self.beamformer.steer
|
|
1511
|
+
|
|
1492
1512
|
@on_trait_change('n')
|
|
1493
1513
|
def set_eva_list(self):
|
|
1494
1514
|
""" sets the list of eigenvalues to consider """
|
|
@@ -1521,19 +1541,23 @@ class BeamformerOrth (BeamformerBase):
|
|
|
1521
1541
|
This method only returns values through the *ac* and *fr* parameters
|
|
1522
1542
|
"""
|
|
1523
1543
|
# prepare calculation
|
|
1524
|
-
|
|
1544
|
+
f = self.freq_data.fftfreq()
|
|
1545
|
+
numchannels = self.freq_data.numchannels
|
|
1546
|
+
normFactor = self.sig_loss_norm()
|
|
1547
|
+
param_steer_type, steer_vector = self._beamformer_params()
|
|
1525
1548
|
for i in self.freq_data.indices:
|
|
1526
1549
|
if not fr[i]:
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1550
|
+
eva = array(self.freq_data.eva[i], dtype='float64')
|
|
1551
|
+
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
1552
|
+
for n in self.eva_list:
|
|
1553
|
+
beamformerOutput = beamformerFreq(param_steer_type,
|
|
1554
|
+
self.r_diag,
|
|
1555
|
+
normFactor,
|
|
1556
|
+
steer_vector(f[i]),
|
|
1557
|
+
(ones(1), eve[:, n].reshape((-1,1))))[0]
|
|
1558
|
+
ac[i, beamformerOutput.argmax()]+=eva[n]/numchannels
|
|
1559
|
+
fr[i] = 1
|
|
1560
|
+
|
|
1537
1561
|
class BeamformerCleansc( BeamformerBase ):
|
|
1538
1562
|
"""
|
|
1539
1563
|
CLEAN-SC deconvolution, see :ref:`Sijtsma, 2007<Sijtsma2007>`.
|
|
@@ -1865,13 +1889,7 @@ class BeamformerCMF ( BeamformerBase ):
|
|
|
1865
1889
|
else:
|
|
1866
1890
|
# take all real parts -- also main diagonal
|
|
1867
1891
|
ind_reim = hstack([ones(size(ind_im0),)>0,ind_im0])
|
|
1868
|
-
ind_reim[0]=True #
|
|
1869
|
-
# if sigma2:
|
|
1870
|
-
# # identity matrix, needed when noise term sigma is used
|
|
1871
|
-
# I = eye(nc).reshape(nc*nc,1)
|
|
1872
|
-
# A = realify( hstack([Ac, I])[ind,:] )[ind_reim,:]
|
|
1873
|
-
# # ... ac[i] = model.coef_[:-1]
|
|
1874
|
-
# else:
|
|
1892
|
+
ind_reim[0]=True # why this ?
|
|
1875
1893
|
|
|
1876
1894
|
A = realify( Ac [ind,:] )[ind_reim,:]
|
|
1877
1895
|
# use csm.T for column stacking reshape!
|
|
@@ -1879,18 +1897,18 @@ class BeamformerCMF ( BeamformerBase ):
|
|
|
1879
1897
|
# choose method
|
|
1880
1898
|
if self.method == 'LassoLars':
|
|
1881
1899
|
model = LassoLars(alpha = self.alpha * unit,
|
|
1882
|
-
max_iter = self.max_iter
|
|
1900
|
+
max_iter = self.max_iter,
|
|
1901
|
+
**sklearn_ndict)
|
|
1883
1902
|
elif self.method == 'LassoLarsBIC':
|
|
1884
1903
|
model = LassoLarsIC(criterion = 'bic',
|
|
1885
|
-
max_iter = self.max_iter
|
|
1904
|
+
max_iter = self.max_iter,
|
|
1905
|
+
**sklearn_ndict)
|
|
1886
1906
|
elif self.method == 'OMPCV':
|
|
1887
|
-
model = OrthogonalMatchingPursuitCV()
|
|
1907
|
+
model = OrthogonalMatchingPursuitCV(**sklearn_ndict)
|
|
1908
|
+
elif self.method == 'NNLS':
|
|
1909
|
+
model = LinearRegression(positive=True)
|
|
1888
1910
|
|
|
1889
|
-
|
|
1890
|
-
if self.method == 'NNLS':
|
|
1891
|
-
ac[i] , x = nnls(A,R.flat)
|
|
1892
|
-
ac[i] /= unit
|
|
1893
|
-
elif self.method == 'Split_Bregman' and PYLOPS_TRUE:
|
|
1911
|
+
if self.method == 'Split_Bregman' and PYLOPS_TRUE:
|
|
1894
1912
|
Oop = MatrixMult(A) #tranfer operator
|
|
1895
1913
|
Iop = self.alpha*Identity(numpoints) # regularisation
|
|
1896
1914
|
ac[i],iterations = SplitBregman(Oop, [Iop] , R[:,0],
|
|
@@ -1932,11 +1950,17 @@ class BeamformerCMF ( BeamformerBase ):
|
|
|
1932
1950
|
|
|
1933
1951
|
ac[i] /= unit
|
|
1934
1952
|
else:
|
|
1935
|
-
#
|
|
1953
|
+
# from sklearn 1.2, normalize=True does not work the same way anymore and the pipeline
|
|
1954
|
+
# approach with StandardScaler does scale in a different way, thus we monkeypatch the
|
|
1955
|
+
# code and normalize ourselves to make results the same over different sklearn versions
|
|
1956
|
+
norms = norm(A, axis=0)
|
|
1957
|
+
# get rid of annoying sklearn warnings that appear for sklearn<1.2 despite any settings
|
|
1936
1958
|
with warnings.catch_warnings():
|
|
1937
1959
|
warnings.simplefilter("ignore", category=FutureWarning)
|
|
1938
|
-
|
|
1939
|
-
|
|
1960
|
+
# normalized A
|
|
1961
|
+
model.fit(A/norms,R[:,0])
|
|
1962
|
+
# recover normalization in the coef's
|
|
1963
|
+
ac[i] = model.coef_[:] / norms / unit
|
|
1940
1964
|
fr[i] = 1
|
|
1941
1965
|
|
|
1942
1966
|
|
|
@@ -2024,7 +2048,8 @@ class BeamformerSODIX( BeamformerBase ):
|
|
|
2024
2048
|
group)
|
|
2025
2049
|
ac = self.h5f.get_data_by_reference('result','/'+nodename)
|
|
2026
2050
|
fr = self.h5f.get_data_by_reference('freqs','/'+nodename)
|
|
2027
|
-
|
|
2051
|
+
gpos = None
|
|
2052
|
+
return (ac,fr,gpos)
|
|
2028
2053
|
|
|
2029
2054
|
@property_depends_on('ext_digest')
|
|
2030
2055
|
def _get_sodix_result ( self ):
|
|
@@ -2042,7 +2067,7 @@ class BeamformerSODIX( BeamformerBase ):
|
|
|
2042
2067
|
config.global_caching == 'none' or
|
|
2043
2068
|
(config.global_caching == 'individual' and self.cached == False)
|
|
2044
2069
|
):
|
|
2045
|
-
(ac,fr) = self._get_filecache()
|
|
2070
|
+
(ac,fr,gpos) = self._get_filecache()
|
|
2046
2071
|
if ac and fr:
|
|
2047
2072
|
if not fr[f.ind_low:f.ind_high].all():
|
|
2048
2073
|
if config.global_caching == 'readonly':
|
|
@@ -2199,21 +2224,28 @@ class BeamformerSODIX( BeamformerBase ):
|
|
|
2199
2224
|
|
|
2200
2225
|
'''
|
|
2201
2226
|
#### the sodix function ####
|
|
2202
|
-
Djm = D.reshape([numpoints,num_mics])
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2227
|
+
Djm = D.reshape([numpoints,num_mics])
|
|
2228
|
+
p = h.T * Djm
|
|
2229
|
+
csm_mod = dot(p.T, p.conj())
|
|
2230
|
+
Q = csm - csm_mod
|
|
2231
|
+
func = sum((absolute(Q))**2)
|
|
2232
|
+
|
|
2233
|
+
# subscripts and operands for numpy einsum and einsum_path
|
|
2234
|
+
subscripts = 'rl,rm,ml->rl'
|
|
2235
|
+
operands = (h.T,h.T.conj()*Djm,Q)
|
|
2236
|
+
es_path = einsum_path(subscripts, *operands, optimize='greedy')[0]
|
|
2237
|
+
|
|
2238
|
+
#### the sodix derivative ####
|
|
2239
|
+
derdrl = einsum(subscripts, *operands, optimize=es_path)
|
|
2240
|
+
derdrl = -4 * real(derdrl)
|
|
2241
|
+
return func, derdrl.ravel()
|
|
2242
|
+
|
|
2212
2243
|
##### initial guess ####
|
|
2213
2244
|
if all(ac[(i-1)]==0):
|
|
2214
2245
|
D0 = ones([numpoints,num_mics])
|
|
2215
2246
|
else:
|
|
2216
|
-
D0 = ac[(i-1)]
|
|
2247
|
+
D0 = sqrt(ac[(i-1)]*
|
|
2248
|
+
real((trace(csm)/trace(array(self.freq_data.csm[i-1], dtype='complex128',copy=1)))))
|
|
2217
2249
|
|
|
2218
2250
|
#boundarys - set to non negative [2*(numpoints*num_mics)]
|
|
2219
2251
|
boundarys = tile((0, +inf), (numpoints*num_mics,1))
|
|
@@ -2222,11 +2254,11 @@ class BeamformerSODIX( BeamformerBase ):
|
|
|
2222
2254
|
# see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html
|
|
2223
2255
|
|
|
2224
2256
|
qi = ones([numpoints,num_mics])
|
|
2225
|
-
qi, yval, dicts = fmin_l_bfgs_b(function, D0, fprime=None, args=(),
|
|
2226
|
-
approx_grad=0, bounds=boundarys,
|
|
2227
|
-
factr=100.0, pgtol=1e-
|
|
2228
|
-
iprint
|
|
2229
|
-
disp
|
|
2257
|
+
qi, yval, dicts = fmin_l_bfgs_b(function, D0, fprime=None, args=(),
|
|
2258
|
+
approx_grad=0, bounds=boundarys,
|
|
2259
|
+
factr=100.0, pgtol=1e-12, epsilon=1e-08,
|
|
2260
|
+
iprint=-1, maxfun=1500000, maxiter=self.max_iter,
|
|
2261
|
+
disp=-1, callback=None, maxls=20)
|
|
2230
2262
|
#squared pressure
|
|
2231
2263
|
ac[i]=qi**2
|
|
2232
2264
|
else:
|
|
@@ -2411,21 +2443,34 @@ class BeamformerGIB(BeamformerEig): #BeamformerEig #BeamformerBase
|
|
|
2411
2443
|
AB = vstack([hstack([A.real,-A.imag]),hstack([A.imag,A.real])])
|
|
2412
2444
|
R = hstack([emode.real.T,emode.imag.T]) * unit
|
|
2413
2445
|
if self.method == 'LassoLars':
|
|
2414
|
-
model = LassoLars(alpha=self.alpha * unit,
|
|
2446
|
+
model = LassoLars(alpha=self.alpha * unit,
|
|
2447
|
+
max_iter=self.max_iter)
|
|
2415
2448
|
elif self.method == 'LassoLarsBIC':
|
|
2416
|
-
model = LassoLarsIC(criterion='bic',
|
|
2449
|
+
model = LassoLarsIC(criterion='bic',
|
|
2450
|
+
max_iter=self.max_iter)
|
|
2417
2451
|
elif self.method == 'OMPCV':
|
|
2418
2452
|
model = OrthogonalMatchingPursuitCV()
|
|
2419
2453
|
elif self.method == 'LassoLarsCV':
|
|
2420
|
-
model = LassoLarsCV()
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2454
|
+
model = LassoLarsCV()
|
|
2455
|
+
elif self.method == 'NNLS':
|
|
2456
|
+
model = LinearRegression(positive=True)
|
|
2457
|
+
model.normalize = False
|
|
2458
|
+
# from sklearn 1.2, normalize=True does not work
|
|
2459
|
+
# the same way anymore and the pipeline approach
|
|
2460
|
+
# with StandardScaler does scale in a different
|
|
2461
|
+
# way, thus we monkeypatch the code and normalize
|
|
2462
|
+
# ourselves to make results the same over different
|
|
2463
|
+
# sklearn versions
|
|
2464
|
+
norms = norm(AB, axis=0)
|
|
2465
|
+
# get rid of annoying sklearn warnings that appear
|
|
2466
|
+
# for sklearn<1.2 despite any settings
|
|
2467
|
+
with warnings.catch_warnings():
|
|
2468
|
+
warnings.simplefilter("ignore",
|
|
2469
|
+
category=FutureWarning)
|
|
2470
|
+
# normalized A
|
|
2471
|
+
model.fit(AB/norms,R)
|
|
2472
|
+
# recover normalization in the coef's
|
|
2473
|
+
qi_real,qi_imag = hsplit(model.coef_[:]/norms/unit, 2)
|
|
2429
2474
|
#print(s,qi.size)
|
|
2430
2475
|
qi[s,locpoints] = qi_real+qi_imag*1j
|
|
2431
2476
|
else:
|
|
@@ -2464,6 +2509,12 @@ class BeamformerAdaptiveGrid(BeamformerBase,Grid):
|
|
|
2464
2509
|
array of floats
|
|
2465
2510
|
The spectrum (all calculated frequency bands) for the integrated sector.
|
|
2466
2511
|
"""
|
|
2512
|
+
if not isinstance(sector, Sector):
|
|
2513
|
+
raise NotImplementedError(
|
|
2514
|
+
f'Please use a sector derived instance of type :class:`~acoular.grids.Sector` '
|
|
2515
|
+
f'instead of type {type(sector)}.'
|
|
2516
|
+
)
|
|
2517
|
+
|
|
2467
2518
|
ind = self.subdomain(sector)
|
|
2468
2519
|
r = self.result
|
|
2469
2520
|
h = zeros(r.shape[0])
|
|
@@ -2648,13 +2699,15 @@ def integrate(data, grid, sector):
|
|
|
2648
2699
|
grid: Grid object
|
|
2649
2700
|
Object of a :class:`~acoular.grids.Grid`-derived class
|
|
2650
2701
|
that provides the grid locations.
|
|
2651
|
-
sector: array of floats
|
|
2702
|
+
sector: array of floats or :class:`~acoular.grids.Sector`-derived object
|
|
2652
2703
|
Tuple with arguments for the `indices` method
|
|
2653
2704
|
of a :class:`~acoular.grids.Grid`-derived class
|
|
2654
2705
|
(e.g. :meth:`RectGrid.indices<acoular.grids.RectGrid.indices>`
|
|
2655
2706
|
or :meth:`RectGrid3D.indices<acoular.grids.RectGrid3D.indices>`).
|
|
2656
2707
|
Possible sectors would be `array([xmin, ymin, xmax, ymax])`
|
|
2657
2708
|
or `array([x, y, radius])`.
|
|
2709
|
+
Alternatively, a :class:`~acoular.grids.Sector`-derived object
|
|
2710
|
+
can be used.
|
|
2658
2711
|
|
|
2659
2712
|
Returns
|
|
2660
2713
|
-------
|
|
@@ -2664,8 +2717,14 @@ def integrate(data, grid, sector):
|
|
|
2664
2717
|
|
|
2665
2718
|
if isinstance(sector, Sector):
|
|
2666
2719
|
ind = grid.subdomain(sector)
|
|
2667
|
-
|
|
2720
|
+
elif hasattr(grid, 'indices'):
|
|
2668
2721
|
ind = grid.indices(*sector)
|
|
2722
|
+
else:
|
|
2723
|
+
raise NotImplementedError(
|
|
2724
|
+
f'Grid of type {grid.__class__.__name__} does not have an indices method! '
|
|
2725
|
+
f'Please use a sector derived instance of type :class:`~acoular.grids.Sector` '
|
|
2726
|
+
'instead of type numpy.array.'
|
|
2727
|
+
)
|
|
2669
2728
|
|
|
2670
2729
|
gshape = grid.shape
|
|
2671
2730
|
gsize = grid.size
|
acoular/grids.py
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
LineGrid
|
|
16
16
|
MergeGrid
|
|
17
17
|
Sector
|
|
18
|
+
SingleSector
|
|
18
19
|
RectSector
|
|
19
20
|
RectSector3D
|
|
20
21
|
CircSector
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
# imports from other packages
|
|
28
29
|
from numpy import mgrid, s_, array, arange, isscalar, absolute, ones, argmin,\
|
|
29
30
|
zeros, where, asfarray,concatenate,sum,ma,ones_like,inf,copysign,fabs ,append,\
|
|
30
|
-
tile,newaxis
|
|
31
|
+
tile,newaxis, unique
|
|
31
32
|
from numpy.linalg import norm
|
|
32
33
|
from traits.api import HasPrivateTraits, Float, Property, Any, \
|
|
33
34
|
property_depends_on, cached_property, Bool, List, Instance, File ,on_trait_change,\
|
|
@@ -733,7 +734,7 @@ class ImportGrid( Grid ):
|
|
|
733
734
|
@on_trait_change('basename')
|
|
734
735
|
def import_gpos( self ):
|
|
735
736
|
"""
|
|
736
|
-
Import the
|
|
737
|
+
Import the the grid point locations from .xml file.
|
|
737
738
|
Called when :attr:`basename` changes.
|
|
738
739
|
"""
|
|
739
740
|
if not path.isfile(self.from_file):
|
|
@@ -749,98 +750,6 @@ class ImportGrid( Grid ):
|
|
|
749
750
|
xyz.append(list(map(lambda a : float(el.getAttribute(a)), 'xyz')))
|
|
750
751
|
self.gpos_file = array(xyz, 'd').swapaxes(0, 1)
|
|
751
752
|
self.subgrids = array(names)
|
|
752
|
-
|
|
753
|
-
def index ( self, x, y ):
|
|
754
|
-
"""
|
|
755
|
-
Queries the indices for a grid point near a certain co-ordinate.
|
|
756
|
-
|
|
757
|
-
This can be used to query results or co-ordinates at/near a certain
|
|
758
|
-
co-ordinate.
|
|
759
|
-
|
|
760
|
-
Parameters
|
|
761
|
-
----------
|
|
762
|
-
x, y : float
|
|
763
|
-
The co-ordinates for which the indices are queried.
|
|
764
|
-
|
|
765
|
-
Returns
|
|
766
|
-
-------
|
|
767
|
-
2-tuple of integers
|
|
768
|
-
The indices that give the grid point nearest to the given x, y
|
|
769
|
-
co-ordinates from an array with the same shape as the grid.
|
|
770
|
-
"""
|
|
771
|
-
if x < self.x_min or x > self.x_max:
|
|
772
|
-
raise ValueError("x-value out of range")
|
|
773
|
-
if y < self.y_min or y > self.y_max:
|
|
774
|
-
raise ValueError("y-value out of range")
|
|
775
|
-
xi = int((x-self.x_min)/self.increment+0.5)
|
|
776
|
-
yi = int((y-self.y_min)/self.increment+0.5)
|
|
777
|
-
return xi, yi
|
|
778
|
-
|
|
779
|
-
def indices ( self, *r):
|
|
780
|
-
"""
|
|
781
|
-
Queries the indices for a subdomain in the grid.
|
|
782
|
-
|
|
783
|
-
Allows either rectangular, circular or polygonial subdomains.
|
|
784
|
-
This can be used to mask or to query results from a certain
|
|
785
|
-
sector or subdomain.
|
|
786
|
-
|
|
787
|
-
Parameters
|
|
788
|
-
----------
|
|
789
|
-
x1, y1, x2, y2, ... : float
|
|
790
|
-
If three parameters are given, then a circular sector is assumed
|
|
791
|
-
that is given by its center (x1, y1) and the radius x2.
|
|
792
|
-
If four paramters are given, then a rectangular sector is
|
|
793
|
-
assumed that is given by two corners (x1, y1) and (x2, y2).
|
|
794
|
-
If more parameters are given, the subdomain is assumed to have
|
|
795
|
-
polygonial shape with corners at (x_n, y_n).
|
|
796
|
-
|
|
797
|
-
Returns
|
|
798
|
-
-------
|
|
799
|
-
2-tuple of arrays of integers or of numpy slice objects
|
|
800
|
-
The indices that can be used to mask/select the grid subdomain from
|
|
801
|
-
an array with the same shape as the grid.
|
|
802
|
-
"""
|
|
803
|
-
|
|
804
|
-
if len(r) == 3: # only 3 values given -> use x,y,radius method
|
|
805
|
-
xpos = self.gpos
|
|
806
|
-
xis = []
|
|
807
|
-
yis = []
|
|
808
|
-
dr2 = (xpos[0, :]-r[0])**2 + (xpos[1, :]-r[1])**2
|
|
809
|
-
# array with true/false entries
|
|
810
|
-
inds = dr2 <= r[2]**2
|
|
811
|
-
for np in arange(self.size)[inds]: # np -- points in x2-circle
|
|
812
|
-
xi, yi = self.index(xpos[0, np], xpos[1, np])
|
|
813
|
-
xis += [xi]
|
|
814
|
-
yis += [yi]
|
|
815
|
-
if not (xis and yis): # if no points in circle, take nearest one
|
|
816
|
-
return self.index(r[0], r[1])
|
|
817
|
-
else:
|
|
818
|
-
return array(xis), array(yis)
|
|
819
|
-
elif len(r) == 4: # rectangular subdomain - old functionality
|
|
820
|
-
xi1, yi1 = self.index(min(r[0], r[2]), min(r[1], r[3]))
|
|
821
|
-
xi2, yi2 = self.index(max(r[0], r[2]), max(r[1], r[3]))
|
|
822
|
-
return s_[xi1:xi2+1], s_[yi1:yi2+1]
|
|
823
|
-
else: # use enveloping polygon
|
|
824
|
-
xpos = self.gpos
|
|
825
|
-
xis = []
|
|
826
|
-
yis = []
|
|
827
|
-
#replaced matplotlib Path by numpy
|
|
828
|
-
#p = Path(array(r).reshape(-1,2))
|
|
829
|
-
#inds = p.contains_points()
|
|
830
|
-
#inds = in_poly(xpos[:2,:].T,array(r).reshape(-1,2))
|
|
831
|
-
poly = Polygon(array(r).reshape(-1,2)[:,0],array(r).reshape(-1,2)[:,1])
|
|
832
|
-
dists = poly.is_inside(xpos[0,:],xpos[1,:])
|
|
833
|
-
inds = dists >= 0
|
|
834
|
-
for np in arange(self.size)[inds]: # np -- points in x2-circle
|
|
835
|
-
xi, yi = self.index(xpos[0, np], xpos[1, np])
|
|
836
|
-
xis += [xi]
|
|
837
|
-
yis += [yi]
|
|
838
|
-
if not (xis and yis): # if no points inside, take nearest to center
|
|
839
|
-
center = array(r).reshape(-1,2).mean(0)
|
|
840
|
-
return self.index(center[0], center[1])
|
|
841
|
-
else:
|
|
842
|
-
return array(xis), array(yis)
|
|
843
|
-
#return arange(self.size)[inds]
|
|
844
753
|
|
|
845
754
|
class LineGrid( Grid ):
|
|
846
755
|
"""
|
|
@@ -948,7 +857,6 @@ class MergeGrid( Grid ):
|
|
|
948
857
|
subgrids = append(subgrids,tile(grid.__class__.__name__+grid.digest,grid.size))
|
|
949
858
|
return subgrids[:,newaxis].T
|
|
950
859
|
|
|
951
|
-
|
|
952
860
|
@property_depends_on('digest')
|
|
953
861
|
def _get_gpos( self ):
|
|
954
862
|
bpos = zeros((3,0))
|
|
@@ -956,29 +864,18 @@ class MergeGrid( Grid ):
|
|
|
956
864
|
for grid in self.grids:
|
|
957
865
|
bpos = append(bpos,grid.gpos, axis = 1)
|
|
958
866
|
#subgrids = append(subgrids,str(grid))
|
|
867
|
+
bpos = unique(bpos,axis=1)
|
|
959
868
|
return bpos
|
|
960
869
|
|
|
961
870
|
class Sector( HasPrivateTraits ):
|
|
962
871
|
"""
|
|
963
|
-
Base class for sector types.
|
|
872
|
+
Base class for all sector types.
|
|
964
873
|
|
|
965
|
-
Defines the common interface for all
|
|
874
|
+
Defines the common interface for all tbdsector classes. This class
|
|
966
875
|
may be used as a base for diverse sector implementaions. If used
|
|
967
876
|
directly, it implements a sector encompassing the whole grid.
|
|
968
877
|
"""
|
|
969
878
|
|
|
970
|
-
#: Boolean flag, if 'True' (default), grid points lying on the sector border are included.
|
|
971
|
-
include_border = Bool(True,
|
|
972
|
-
desc="include points on the border")
|
|
973
|
-
|
|
974
|
-
#: Absolute tolerance for sector border
|
|
975
|
-
abs_tol = Float(1e-12,
|
|
976
|
-
desc="absolute tolerance for sector border")
|
|
977
|
-
|
|
978
|
-
#: Boolean flag, if 'True' (default), the nearest grid point is returned if none is inside the sector.
|
|
979
|
-
default_nearest = Bool(True,
|
|
980
|
-
desc="return nearest grid point to center of none inside sector")
|
|
981
|
-
|
|
982
879
|
def contains ( self, pos ):
|
|
983
880
|
"""
|
|
984
881
|
Queries whether the coordinates in a given array lie within the
|
|
@@ -1000,7 +897,30 @@ class Sector( HasPrivateTraits ):
|
|
|
1000
897
|
return ones(pos.shape[1], dtype=bool)
|
|
1001
898
|
|
|
1002
899
|
|
|
1003
|
-
class
|
|
900
|
+
class SingleSector( Sector ):
|
|
901
|
+
"""
|
|
902
|
+
Base class for single sector types.
|
|
903
|
+
|
|
904
|
+
Defines the common interface for all single sector classes. This class
|
|
905
|
+
may be used as a base for diverse single sector implementaions. If used
|
|
906
|
+
directly, it implements a sector encompassing the whole grid.
|
|
907
|
+
"""
|
|
908
|
+
|
|
909
|
+
#: Boolean flag, if 'True' (default), grid points lying on the sector border are included.
|
|
910
|
+
include_border = Bool(True,
|
|
911
|
+
desc="include points on the border")
|
|
912
|
+
|
|
913
|
+
#: Absolute tolerance for sector border
|
|
914
|
+
abs_tol = Float(1e-12,
|
|
915
|
+
desc="absolute tolerance for sector border")
|
|
916
|
+
|
|
917
|
+
#: Boolean flag, if 'True' (default), the nearest grid point is returned if none is inside the sector.
|
|
918
|
+
default_nearest = Bool(True,
|
|
919
|
+
desc="return nearest grid point to center of none inside sector")
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
class RectSector( SingleSector ):
|
|
1004
924
|
"""
|
|
1005
925
|
Class for defining a rectangular sector.
|
|
1006
926
|
|
|
@@ -1142,7 +1062,7 @@ class RectSector3D( RectSector ):
|
|
|
1142
1062
|
return inds.astype(bool)
|
|
1143
1063
|
|
|
1144
1064
|
|
|
1145
|
-
class CircSector(
|
|
1065
|
+
class CircSector( SingleSector ):
|
|
1146
1066
|
"""
|
|
1147
1067
|
Class for defining a circular sector.
|
|
1148
1068
|
|
|
@@ -1197,7 +1117,7 @@ class CircSector( Sector ):
|
|
|
1197
1117
|
return inds
|
|
1198
1118
|
|
|
1199
1119
|
|
|
1200
|
-
class PolySector(
|
|
1120
|
+
class PolySector( SingleSector ):
|
|
1201
1121
|
"""
|
|
1202
1122
|
Class for defining a polygon sector.
|
|
1203
1123
|
|
|
@@ -1242,7 +1162,7 @@ class PolySector( Sector ):
|
|
|
1242
1162
|
|
|
1243
1163
|
return inds
|
|
1244
1164
|
|
|
1245
|
-
class ConvexSector(
|
|
1165
|
+
class ConvexSector( SingleSector ):
|
|
1246
1166
|
"""
|
|
1247
1167
|
Class for defining a convex hull sector.
|
|
1248
1168
|
|
|
@@ -1297,7 +1217,6 @@ class MultiSector(Sector):
|
|
|
1297
1217
|
#: to be mixed.
|
|
1298
1218
|
sectors = List(Instance(Sector))
|
|
1299
1219
|
|
|
1300
|
-
|
|
1301
1220
|
def contains ( self, pos ):
|
|
1302
1221
|
"""
|
|
1303
1222
|
Queries whether the coordinates in a given array lie within any
|