acoular 24.7__py3-none-any.whl → 25.1__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 +21 -9
- acoular/aiaa/__init__.py +12 -0
- acoular/{tools → aiaa}/aiaa.py +26 -31
- acoular/base.py +332 -0
- acoular/calib.py +129 -34
- acoular/configuration.py +13 -11
- acoular/demo/__init__.py +1 -0
- acoular/demo/acoular_demo.py +30 -17
- acoular/deprecation.py +85 -0
- acoular/environments.py +38 -24
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +342 -387
- acoular/fprocess.py +376 -0
- acoular/grids.py +122 -150
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +50 -59
- acoular/process.py +771 -0
- acoular/sdinput.py +35 -21
- acoular/signals.py +120 -113
- acoular/sources.py +208 -234
- acoular/spectra.py +59 -254
- acoular/tbeamform.py +280 -280
- acoular/tfastfuncs.py +21 -21
- acoular/tools/__init__.py +3 -7
- acoular/tools/helpers.py +218 -4
- acoular/tools/metrics.py +5 -5
- acoular/tools/utils.py +116 -0
- acoular/tprocess.py +416 -741
- acoular/traitsviews.py +15 -13
- acoular/trajectory.py +7 -10
- acoular/version.py +2 -2
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/METADATA +63 -21
- acoular-25.1.dist-info/RECORD +56 -0
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/WHEEL +1 -1
- acoular-24.7.dist-info/RECORD +0 -50
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/licenses/LICENSE +0 -0
acoular/h5cache.py
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
# imports from other packages
|
|
6
6
|
import gc
|
|
7
|
-
from
|
|
7
|
+
from pathlib import Path
|
|
8
8
|
from weakref import WeakValueDictionary
|
|
9
9
|
|
|
10
|
-
from traits.api import Bool, Delegate, Dict,
|
|
10
|
+
from traits.api import Bool, Delegate, Dict, HasStrictTraits, Instance
|
|
11
11
|
|
|
12
12
|
from .configuration import Config, config
|
|
13
13
|
from .h5files import _get_cachefile_class
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class HDF5Cache(
|
|
16
|
+
class HDF5Cache(HasStrictTraits):
|
|
17
17
|
"""Cache class that handles opening and closing 'tables.File' objects."""
|
|
18
18
|
|
|
19
19
|
config = Instance(Config)
|
|
@@ -24,26 +24,16 @@ class HDF5Cache(HasPrivateTraits):
|
|
|
24
24
|
|
|
25
25
|
open_files = WeakValueDictionary()
|
|
26
26
|
|
|
27
|
-
open_file_reference = Dict()
|
|
27
|
+
open_file_reference = Dict(key_trait=Path, value_trait=int)
|
|
28
28
|
|
|
29
29
|
def _idle_if_busy(self):
|
|
30
30
|
while self.busy:
|
|
31
31
|
pass
|
|
32
32
|
|
|
33
|
-
def open_cachefile(self, filename, mode):
|
|
34
|
-
file = _get_cachefile_class()
|
|
35
|
-
return file(path.join(self.cache_dir, filename), mode)
|
|
36
|
-
|
|
37
33
|
def close_cachefile(self, cachefile):
|
|
38
|
-
self.open_file_reference.pop(
|
|
34
|
+
self.open_file_reference.pop(Path(cachefile.filename))
|
|
39
35
|
cachefile.close()
|
|
40
36
|
|
|
41
|
-
def get_filename(self, file):
|
|
42
|
-
file_class = _get_cachefile_class()
|
|
43
|
-
if isinstance(file, file_class):
|
|
44
|
-
return get_basename(file)
|
|
45
|
-
return 0
|
|
46
|
-
|
|
47
37
|
def get_open_cachefiles(self):
|
|
48
38
|
try:
|
|
49
39
|
return self.open_files.itervalues()
|
|
@@ -53,7 +43,6 @@ class HDF5Cache(HasPrivateTraits):
|
|
|
53
43
|
def close_unreferenced_cachefiles(self):
|
|
54
44
|
for cachefile in self.get_open_cachefiles():
|
|
55
45
|
if not self.is_reference_existent(cachefile):
|
|
56
|
-
# print("close unreferenced File:",get_basename(cachefile))
|
|
57
46
|
self.close_cachefile(cachefile)
|
|
58
47
|
|
|
59
48
|
def is_reference_existent(self, file):
|
|
@@ -69,46 +58,50 @@ class HDF5Cache(HasPrivateTraits):
|
|
|
69
58
|
break
|
|
70
59
|
return exist_flag
|
|
71
60
|
|
|
72
|
-
def is_cachefile_existent(self, filename):
|
|
73
|
-
if filename in listdir(self.cache_dir):
|
|
74
|
-
return True
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
61
|
def _increase_file_reference_counter(self, filename):
|
|
78
62
|
self.open_file_reference[filename] = self.open_file_reference.get(filename, 0) + 1
|
|
79
63
|
|
|
80
64
|
def _decrease_file_reference_counter(self, filename):
|
|
81
65
|
self.open_file_reference[filename] = self.open_file_reference[filename] - 1
|
|
82
66
|
|
|
67
|
+
def get_cache_directories(self):
|
|
68
|
+
"""Return a list of all used cache directories (if multiple paths exist)."""
|
|
69
|
+
return list({str(k.parent) for k in self.open_file_reference})
|
|
70
|
+
|
|
83
71
|
def _print_open_files(self):
|
|
84
|
-
|
|
72
|
+
"""Prints open cache files and the number of objects referencing a cache file.
|
|
73
|
+
|
|
74
|
+
If multiple cache files are open at different paths, the full path is printed.
|
|
75
|
+
Otherwise, only the filename is logged.
|
|
76
|
+
"""
|
|
77
|
+
if len(self.open_file_reference.values()) > 1:
|
|
78
|
+
print(list({str(k): v for k, v in self.open_file_reference.items()}.items()))
|
|
79
|
+
else:
|
|
80
|
+
print(list({str(k.name): v for k, v in self.open_file_reference.items()}.items()))
|
|
85
81
|
|
|
86
82
|
def get_cache_file(self, obj, basename, mode='a'):
|
|
87
83
|
"""Returns pytables .h5 file to h5f trait of calling object for caching."""
|
|
88
84
|
self._idle_if_busy() #
|
|
89
85
|
self.busy = True
|
|
86
|
+
file_cls = _get_cachefile_class()
|
|
87
|
+
filename = (Path(self.cache_dir) / (basename + '_cache.h5')).resolve()
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
if config.global_caching == 'readonly' and not filename.exists():
|
|
90
|
+
obj.h5f = None
|
|
91
|
+
self.busy = False
|
|
92
|
+
return # cachefile is not created in readonly mode
|
|
93
93
|
|
|
94
|
-
if
|
|
95
|
-
if
|
|
94
|
+
if isinstance(obj.h5f, file_cls):
|
|
95
|
+
if Path(obj.h5f.filename).resolve() == filename:
|
|
96
96
|
self.busy = False
|
|
97
97
|
return
|
|
98
|
-
self._decrease_file_reference_counter(
|
|
98
|
+
self._decrease_file_reference_counter(obj.h5f.filename)
|
|
99
99
|
|
|
100
100
|
if filename not in self.open_files: # or tables.file._open_files.filenames
|
|
101
|
-
if config.global_caching == 'readonly' and not self.is_cachefile_existent(
|
|
102
|
-
filename,
|
|
103
|
-
): # condition ensures that cachefile is not created in readonly mode
|
|
104
|
-
obj.h5f = None
|
|
105
|
-
self.busy = False
|
|
106
|
-
# self._print_open_files()
|
|
107
|
-
return
|
|
108
101
|
if config.global_caching == 'readonly':
|
|
109
102
|
mode = 'r'
|
|
110
|
-
|
|
111
|
-
self.open_files[filename] =
|
|
103
|
+
file = file_cls(filename, mode)
|
|
104
|
+
self.open_files[filename] = file
|
|
112
105
|
|
|
113
106
|
obj.h5f = self.open_files[filename]
|
|
114
107
|
self._increase_file_reference_counter(filename)
|
|
@@ -121,7 +114,3 @@ class HDF5Cache(HasPrivateTraits):
|
|
|
121
114
|
|
|
122
115
|
|
|
123
116
|
H5cache = HDF5Cache(config=config)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def get_basename(file):
|
|
127
|
-
return path.basename(file.filename)
|
acoular/h5files.py
CHANGED
|
@@ -111,9 +111,7 @@ if config.have_tables:
|
|
|
111
111
|
def is_cached(self, nodename, group=None):
|
|
112
112
|
if not group:
|
|
113
113
|
group = self.root
|
|
114
|
-
|
|
115
|
-
return True
|
|
116
|
-
return False
|
|
114
|
+
return nodename in group
|
|
117
115
|
|
|
118
116
|
def create_compressible_array(self, nodename, shape, precision, group=None):
|
|
119
117
|
if not group:
|
|
@@ -190,9 +188,7 @@ if config.have_h5py:
|
|
|
190
188
|
def is_cached(self, nodename, group=None):
|
|
191
189
|
if not group:
|
|
192
190
|
group = '/'
|
|
193
|
-
|
|
194
|
-
return True
|
|
195
|
-
return False
|
|
191
|
+
return group + nodename in self
|
|
196
192
|
|
|
197
193
|
def create_compressible_array(self, nodename, shape, precision, group=None):
|
|
198
194
|
in_file_path = self._get_in_file_path(nodename, group)
|
acoular/microphones.py
CHANGED
|
@@ -11,17 +11,28 @@
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
# imports from other packages
|
|
14
|
-
import
|
|
15
|
-
from
|
|
14
|
+
import xml.dom.minidom
|
|
15
|
+
from pathlib import Path
|
|
16
16
|
|
|
17
17
|
from numpy import array, average
|
|
18
18
|
from scipy.spatial.distance import cdist
|
|
19
|
-
from traits.api import
|
|
20
|
-
|
|
19
|
+
from traits.api import (
|
|
20
|
+
CArray,
|
|
21
|
+
File,
|
|
22
|
+
HasStrictTraits,
|
|
23
|
+
List,
|
|
24
|
+
Property,
|
|
25
|
+
cached_property,
|
|
26
|
+
on_trait_change,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# acoular imports
|
|
30
|
+
from .deprecation import deprecated_alias
|
|
21
31
|
from .internal import digest
|
|
22
32
|
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
@deprecated_alias({'mpos_tot': 'pos_total', 'mpos': 'pos', 'from_file': 'file'}, read_only=['mpos'])
|
|
35
|
+
class MicGeom(HasStrictTraits):
|
|
25
36
|
"""Provides the geometric arrangement of microphones in the array.
|
|
26
37
|
|
|
27
38
|
The geometric arrangement of microphones is read in from an
|
|
@@ -29,65 +40,53 @@ class MicGeom(HasPrivateTraits):
|
|
|
29
40
|
Can also be used with programmatically generated arrangements.
|
|
30
41
|
"""
|
|
31
42
|
|
|
32
|
-
#: Name of the .xml-file from
|
|
33
|
-
|
|
43
|
+
#: Name of the .xml-file from which to read the data.
|
|
44
|
+
file = File(filter=['*.xml'], exists=True, desc='name of the xml file to import')
|
|
34
45
|
|
|
35
|
-
#:
|
|
36
|
-
|
|
46
|
+
#: Positions as (3, :attr:`num_mics`) array of floats, may include also invalid
|
|
47
|
+
#: microphones (if any). Set either automatically on change of the
|
|
48
|
+
#: :attr:`file` argument or explicitly by assigning an array of floats.
|
|
49
|
+
pos_total = CArray(dtype=float, shape=(3, None), desc='x, y, z position of all microphones')
|
|
37
50
|
|
|
38
|
-
#:
|
|
39
|
-
|
|
51
|
+
#: Positions as (3, :attr:`num_mics`) array of floats, without invalid
|
|
52
|
+
#: microphones; readonly.
|
|
53
|
+
pos = Property(depends_on=['pos_total', 'invalid_channels'], desc='x, y, z position of used microphones')
|
|
40
54
|
|
|
41
55
|
#: List that gives the indices of channels that should not be considered.
|
|
42
56
|
#: Defaults to a blank list.
|
|
43
|
-
invalid_channels =
|
|
57
|
+
invalid_channels = List(int, desc='list of invalid channels')
|
|
44
58
|
|
|
45
|
-
#: Number of microphones in the array; readonly.
|
|
46
|
-
num_mics = Property(depends_on=['
|
|
59
|
+
#: Number of used microphones in the array; readonly.
|
|
60
|
+
num_mics = Property(depends_on=['pos'], desc='number of microphones in the geometry')
|
|
47
61
|
|
|
48
62
|
#: Center of the array (arithmetic mean of all used array positions); readonly.
|
|
49
|
-
center = Property(depends_on=['
|
|
63
|
+
center = Property(depends_on=['pos'], desc='array center')
|
|
50
64
|
|
|
51
65
|
#: Aperture of the array (greatest extent between two microphones); readonly.
|
|
52
|
-
aperture = Property(depends_on=['
|
|
53
|
-
|
|
54
|
-
#: Positions as (3, :attr:`num_mics`) array of floats, may include also invalid
|
|
55
|
-
#: microphones (if any). Set either automatically on change of the
|
|
56
|
-
#: :attr:`from_file` argument or explicitely by assigning an array of floats.
|
|
57
|
-
mpos_tot = CArray(dtype=float, desc='x, y, z position of all microphones')
|
|
58
|
-
|
|
59
|
-
#: Positions as (3, :attr:`num_mics`) array of floats, without invalid
|
|
60
|
-
#: microphones; readonly.
|
|
61
|
-
mpos = Property(depends_on=['mpos_tot', 'invalid_channels'], desc='x, y, z position of microphones')
|
|
66
|
+
aperture = Property(depends_on=['pos'], desc='array aperture')
|
|
62
67
|
|
|
63
68
|
# internal identifier
|
|
64
|
-
digest = Property(depends_on=['
|
|
69
|
+
digest = Property(depends_on=['pos'])
|
|
65
70
|
|
|
66
71
|
@cached_property
|
|
67
72
|
def _get_digest(self):
|
|
68
73
|
return digest(self)
|
|
69
74
|
|
|
70
75
|
@cached_property
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if self.validate_file:
|
|
77
|
-
if len(self.invalid_channels) == 0:
|
|
78
|
-
return self.mpos_tot
|
|
79
|
-
allr = [i for i in range(self.mpos_tot.shape[-1]) if i not in self.invalid_channels]
|
|
80
|
-
return self.mpos_tot[:, array(allr)]
|
|
81
|
-
raise FileNotFoundError(errno.ENOENT, strerror(errno.ENOENT), self.from_file)
|
|
76
|
+
def _get_pos(self):
|
|
77
|
+
if len(self.invalid_channels) == 0:
|
|
78
|
+
return self.pos_total
|
|
79
|
+
allr = [i for i in range(self.pos_total.shape[-1]) if i not in self.invalid_channels]
|
|
80
|
+
return self.pos_total[:, array(allr)]
|
|
82
81
|
|
|
83
82
|
@cached_property
|
|
84
83
|
def _get_num_mics(self):
|
|
85
|
-
return self.
|
|
84
|
+
return self.pos.shape[-1]
|
|
86
85
|
|
|
87
86
|
@cached_property
|
|
88
87
|
def _get_center(self):
|
|
89
|
-
if self.
|
|
90
|
-
center = average(self.
|
|
88
|
+
if self.pos.any():
|
|
89
|
+
center = average(self.pos, axis=1)
|
|
91
90
|
# set very small values to zero
|
|
92
91
|
center[abs(center) < 1e-16] = 0.0
|
|
93
92
|
return center
|
|
@@ -95,31 +94,22 @@ class MicGeom(HasPrivateTraits):
|
|
|
95
94
|
|
|
96
95
|
@cached_property
|
|
97
96
|
def _get_aperture(self):
|
|
98
|
-
if self.
|
|
99
|
-
return cdist(self.
|
|
97
|
+
if self.pos.any():
|
|
98
|
+
return cdist(self.pos.T, self.pos.T).max()
|
|
100
99
|
return None
|
|
101
100
|
|
|
102
|
-
@on_trait_change('
|
|
101
|
+
@on_trait_change('file')
|
|
103
102
|
def import_mpos(self):
|
|
104
103
|
"""Import the microphone positions from .xml file.
|
|
105
|
-
Called when :attr:`
|
|
104
|
+
Called when :attr:`file` changes.
|
|
106
105
|
"""
|
|
107
|
-
|
|
108
|
-
# no file there
|
|
109
|
-
self.mpos_tot = array([], 'd')
|
|
110
|
-
# raise error: File not found on _get functions
|
|
111
|
-
self.validate_file = False
|
|
112
|
-
|
|
113
|
-
import xml.dom.minidom
|
|
114
|
-
|
|
115
|
-
doc = xml.dom.minidom.parse(self.from_file)
|
|
106
|
+
doc = xml.dom.minidom.parse(self.file)
|
|
116
107
|
names = []
|
|
117
108
|
xyz = []
|
|
118
109
|
for el in doc.getElementsByTagName('pos'):
|
|
119
110
|
names.append(el.getAttribute('Name'))
|
|
120
111
|
xyz.append([float(el.getAttribute(a)) for a in 'xyz'])
|
|
121
|
-
self.
|
|
122
|
-
self.validate_file = True
|
|
112
|
+
self.pos_total = array(xyz, 'd').swapaxes(0, 1)
|
|
123
113
|
|
|
124
114
|
def export_mpos(self, filename):
|
|
125
115
|
"""Export the microphone positions to .xml file.
|
|
@@ -129,11 +119,12 @@ class MicGeom(HasPrivateTraits):
|
|
|
129
119
|
filename : str
|
|
130
120
|
Name of the file to which the microphone positions are written.
|
|
131
121
|
"""
|
|
132
|
-
|
|
133
|
-
|
|
122
|
+
filepath = Path(filename)
|
|
123
|
+
basename = filepath.stem
|
|
124
|
+
with filepath.open('w', encoding='utf-8') as f:
|
|
134
125
|
f.write(f'<?xml version="1.1" encoding="utf-8"?><MicArray name="{basename}">\n')
|
|
135
|
-
for i in range(self.
|
|
126
|
+
for i in range(self.pos.shape[-1]):
|
|
136
127
|
f.write(
|
|
137
|
-
f' <pos Name="Point {i+1}" x="{self.
|
|
128
|
+
f' <pos Name="Point {i+1}" x="{self.pos[0, i]}" y="{self.pos[1, i]}" z="{self.pos[2, i]}"/>\n',
|
|
138
129
|
)
|
|
139
130
|
f.write('</MicArray>')
|