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/h5cache.py CHANGED
@@ -4,16 +4,16 @@
4
4
 
5
5
  # imports from other packages
6
6
  import gc
7
- from os import listdir, path
7
+ from pathlib import Path
8
8
  from weakref import WeakValueDictionary
9
9
 
10
- from traits.api import Bool, Delegate, Dict, HasPrivateTraits, Instance
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(HasPrivateTraits):
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(get_basename(cachefile))
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
- print(list(self.open_file_reference.items()))
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
- filename = basename + '_cache.h5'
92
- obj_filename = self.get_filename(obj.h5f)
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 obj_filename:
95
- if obj_filename == filename:
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(obj_filename)
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
- f = self.open_cachefile(filename, mode)
111
- self.open_files[filename] = f
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
- if nodename in group:
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
- if group + nodename in self:
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 errno
15
- from os import path, strerror
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 Bool, CArray, File, HasPrivateTraits, ListInt, Property, cached_property, on_trait_change
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
- class MicGeom(HasPrivateTraits):
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 wich to read the data.
33
- from_file = File(filter=['*.xml'], desc='name of the xml file to import')
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
- #: Validate mic geom from file
36
- validate_file = Bool(True, desc='Validate mic geom from file')
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
- #: Basename of the .xml-file, without the extension; is set automatically / readonly.
39
- basename = Property(depends_on='from_file', desc='basename of xml file')
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 = ListInt(desc='list of 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=['mpos'], desc='number of microphones in the geometry')
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=['mpos'], desc='array center')
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=['mpos'], desc='array aperture')
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=['mpos'])
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 _get_basename(self):
72
- return path.splitext(path.basename(self.from_file))[0]
73
-
74
- @cached_property
75
- def _get_mpos(self):
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.mpos.shape[-1]
84
+ return self.pos.shape[-1]
86
85
 
87
86
  @cached_property
88
87
  def _get_center(self):
89
- if self.mpos.any():
90
- center = average(self.mpos, axis=1)
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.mpos.any():
99
- return cdist(self.mpos.T, self.mpos.T).max()
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('basename')
101
+ @on_trait_change('file')
103
102
  def import_mpos(self):
104
103
  """Import the microphone positions from .xml file.
105
- Called when :attr:`basename` changes.
104
+ Called when :attr:`file` changes.
106
105
  """
107
- if not path.isfile(self.from_file):
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.mpos_tot = array(xyz, 'd').swapaxes(0, 1)
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
- basename = path.splitext(path.basename(filename))[0]
133
- with open(filename, 'w') as f:
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.mpos.shape[-1]):
126
+ for i in range(self.pos.shape[-1]):
136
127
  f.write(
137
- f' <pos Name="Point {i+1}" x="{self.mpos[0, i]}" y="{self.mpos[1, i]}" z="{self.mpos[2, i]}"/>\n',
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>')