acoular 24.5__py3-none-any.whl → 24.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/grids.py CHANGED
@@ -550,8 +550,8 @@ class RectGrid3D(RectGrid):
550
550
  if isscalar(increment):
551
551
  try:
552
552
  self._increment = absolute(float(increment))
553
- except:
554
- raise TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment)
553
+ except ValueError as ve:
554
+ raise TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment) from ve
555
555
  elif len(increment) == 3:
556
556
  self._increment = array(increment, dtype=float)
557
557
  else:
@@ -559,14 +559,14 @@ class RectGrid3D(RectGrid):
559
559
 
560
560
  # Respective increments in x,y, and z-direction (in m).
561
561
  # Deprecated: Use :attr:`~RectGrid.increment` for this functionality
562
- increment3D = Property(desc='3D step sizes')
562
+ increment3D = Property(desc='3D step sizes') # noqa N815
563
563
 
564
- def _get_increment3D(self):
564
+ def _get_increment3D(self): # noqa N802
565
565
  if isscalar(self._increment):
566
566
  return array([self._increment, self._increment, self._increment])
567
567
  return self._increment
568
568
 
569
- def _set_increment3D(self, inc):
569
+ def _set_increment3D(self, inc): # noqa N802
570
570
  if not isscalar(inc) and len(inc) == 3:
571
571
  self._increment = array(inc, dtype=float)
572
572
  else:
acoular/h5cache.py CHANGED
@@ -13,7 +13,7 @@ from .configuration import Config, config
13
13
  from .h5files import _get_cachefile_class
14
14
 
15
15
 
16
- class H5cache_class(HasPrivateTraits):
16
+ class HDF5Cache(HasPrivateTraits):
17
17
  """Cache class that handles opening and closing 'tables.File' objects."""
18
18
 
19
19
  config = Instance(Config)
@@ -24,23 +24,23 @@ class H5cache_class(HasPrivateTraits):
24
24
 
25
25
  open_files = WeakValueDictionary()
26
26
 
27
- openFileReferenceCount = Dict()
27
+ open_file_reference = Dict()
28
28
 
29
29
  def _idle_if_busy(self):
30
30
  while self.busy:
31
31
  pass
32
32
 
33
- def open_cachefile(self, cacheFileName, mode):
34
- File = _get_cachefile_class()
35
- return File(path.join(self.cache_dir, cacheFileName), mode)
33
+ def open_cachefile(self, filename, mode):
34
+ file = _get_cachefile_class()
35
+ return file(path.join(self.cache_dir, filename), mode)
36
36
 
37
37
  def close_cachefile(self, cachefile):
38
- self.openFileReferenceCount.pop(get_basename(cachefile))
38
+ self.open_file_reference.pop(get_basename(cachefile))
39
39
  cachefile.close()
40
40
 
41
41
  def get_filename(self, file):
42
- File = _get_cachefile_class()
43
- if isinstance(file, File):
42
+ file_class = _get_cachefile_class()
43
+ if isinstance(file, file_class):
44
44
  return get_basename(file)
45
45
  return 0
46
46
 
@@ -51,13 +51,13 @@ class H5cache_class(HasPrivateTraits):
51
51
  return iter(self.open_files.values())
52
52
 
53
53
  def close_unreferenced_cachefiles(self):
54
- for openCacheFile in self.get_open_cachefiles():
55
- if not self.is_reference_existent(openCacheFile):
56
- # print("close unreferenced File:",get_basename(openCacheFile))
57
- self.close_cachefile(openCacheFile)
54
+ for cachefile in self.get_open_cachefiles():
55
+ if not self.is_reference_existent(cachefile):
56
+ # print("close unreferenced File:",get_basename(cachefile))
57
+ self.close_cachefile(cachefile)
58
58
 
59
59
  def is_reference_existent(self, file):
60
- existFlag = False
60
+ exist_flag = False
61
61
  # inspect all refererres to the file object
62
62
  gc.collect() # clear garbage before collecting referrers
63
63
  for ref in gc.get_referrers(file):
@@ -65,41 +65,41 @@ class H5cache_class(HasPrivateTraits):
65
65
  # attribute?
66
66
  if isinstance(ref, dict) and 'h5f' in ref:
67
67
  # file is still referred, must not be closed
68
- existFlag = True
68
+ exist_flag = True
69
69
  break
70
- return existFlag
70
+ return exist_flag
71
71
 
72
- def is_cachefile_existent(self, cacheFileName):
73
- if cacheFileName in listdir(self.cache_dir):
72
+ def is_cachefile_existent(self, filename):
73
+ if filename in listdir(self.cache_dir):
74
74
  return True
75
75
  return False
76
76
 
77
- def _increase_file_reference_counter(self, cacheFileName):
78
- self.openFileReferenceCount[cacheFileName] = self.openFileReferenceCount.get(cacheFileName, 0) + 1
77
+ def _increase_file_reference_counter(self, filename):
78
+ self.open_file_reference[filename] = self.open_file_reference.get(filename, 0) + 1
79
79
 
80
- def _decrease_file_reference_counter(self, cacheFileName):
81
- self.openFileReferenceCount[cacheFileName] = self.openFileReferenceCount[cacheFileName] - 1
80
+ def _decrease_file_reference_counter(self, filename):
81
+ self.open_file_reference[filename] = self.open_file_reference[filename] - 1
82
82
 
83
83
  def _print_open_files(self):
84
- print(list(self.openFileReferenceCount.items()))
84
+ print(list(self.open_file_reference.items()))
85
85
 
86
86
  def get_cache_file(self, obj, basename, mode='a'):
87
87
  """Returns pytables .h5 file to h5f trait of calling object for caching."""
88
88
  self._idle_if_busy() #
89
89
  self.busy = True
90
90
 
91
- cacheFileName = basename + '_cache.h5'
92
- objFileName = self.get_filename(obj.h5f)
91
+ filename = basename + '_cache.h5'
92
+ obj_filename = self.get_filename(obj.h5f)
93
93
 
94
- if objFileName:
95
- if objFileName == cacheFileName:
94
+ if obj_filename:
95
+ if obj_filename == filename:
96
96
  self.busy = False
97
97
  return
98
- self._decrease_file_reference_counter(objFileName)
98
+ self._decrease_file_reference_counter(obj_filename)
99
99
 
100
- if cacheFileName not in self.open_files: # or tables.file._open_files.filenames
100
+ if filename not in self.open_files: # or tables.file._open_files.filenames
101
101
  if config.global_caching == 'readonly' and not self.is_cachefile_existent(
102
- cacheFileName,
102
+ filename,
103
103
  ): # condition ensures that cachefile is not created in readonly mode
104
104
  obj.h5f = None
105
105
  self.busy = False
@@ -107,11 +107,11 @@ class H5cache_class(HasPrivateTraits):
107
107
  return
108
108
  if config.global_caching == 'readonly':
109
109
  mode = 'r'
110
- f = self.open_cachefile(cacheFileName, mode)
111
- self.open_files[cacheFileName] = f
110
+ f = self.open_cachefile(filename, mode)
111
+ self.open_files[filename] = f
112
112
 
113
- obj.h5f = self.open_files[cacheFileName]
114
- self._increase_file_reference_counter(cacheFileName)
113
+ obj.h5f = self.open_files[filename]
114
+ self._increase_file_reference_counter(filename)
115
115
 
116
116
  # garbage collection
117
117
  self.close_unreferenced_cachefiles()
@@ -120,7 +120,7 @@ class H5cache_class(HasPrivateTraits):
120
120
  self._print_open_files()
121
121
 
122
122
 
123
- H5cache = H5cache_class(config=config)
123
+ H5cache = HDF5Cache(config=config)
124
124
 
125
125
 
126
126
  def get_basename(file):
acoular/h5files.py CHANGED
@@ -33,7 +33,7 @@ class H5FileBase:
33
33
  class H5CacheFileBase:
34
34
  """Base class for File objects that handle writing and reading of .h5 cache files."""
35
35
 
36
- compressionFilter = None
36
+ compression_filter = None
37
37
 
38
38
  def is_cached(self, nodename, group=None):
39
39
  pass
@@ -72,7 +72,7 @@ if config.have_tables:
72
72
  node.set_attr(attrname, value)
73
73
 
74
74
  def get_node_attribute(self, node, attrname):
75
- return node._v_attrs[attrname]
75
+ return node._v_attrs[attrname] # noqa: SLF001
76
76
 
77
77
  def append_data(self, node, data):
78
78
  node.append(data)
@@ -93,10 +93,10 @@ if config.have_tables:
93
93
  """Recursively convert an HDF5 node to a dictionary."""
94
94
  node = self.get_node(nodename)
95
95
  # initialize node-dict with node's own attributes
96
- result = {attr: node._v_attrs[attr] for attr in node._v_attrs._f_list()}
96
+ result = {attr: node._v_attrs[attr] for attr in node._v_attrs._f_list()} # noqa: SLF001
97
97
  if isinstance(node, tables.Group):
98
98
  # if node is a group, recursively add its children
99
- for childname in node._v_children:
99
+ for childname in node._v_children: # noqa: SLF001
100
100
  result[childname] = self.node_to_dict(f'{nodename}/{childname}')
101
101
  elif isinstance(node, tables.Leaf):
102
102
  # if node contains only data, add it
@@ -106,7 +106,7 @@ if config.have_tables:
106
106
  return result
107
107
 
108
108
  class H5CacheFileTables(H5FileTables, H5CacheFileBase):
109
- compressionFilter = tables.Filters(complevel=5, complib='blosc')
109
+ compression_filter = tables.Filters(complevel=5, complib='blosc')
110
110
 
111
111
  def is_cached(self, nodename, group=None):
112
112
  if not group:
@@ -119,7 +119,7 @@ if config.have_tables:
119
119
  if not group:
120
120
  group = self.root
121
121
  atom = precision_to_atom[precision]
122
- self.create_carray(group, nodename, atom, shape, filters=self.compressionFilter)
122
+ self.create_carray(group, nodename, atom, shape, filters=self.compression_filter)
123
123
 
124
124
 
125
125
  if config.have_h5py:
@@ -149,10 +149,10 @@ if config.have_h5py:
149
149
  return node.attrs[attrname]
150
150
 
151
151
  def append_data(self, node, data):
152
- oldShape = node.shape
153
- newShape = (oldShape[0] + data.shape[0], data.shape[1])
154
- node.resize(newShape)
155
- node[oldShape[0] : newShape[0], :] = data
152
+ old_shape = node.shape
153
+ new_shape = (old_shape[0] + data.shape[0], data.shape[1])
154
+ node.resize(new_shape)
155
+ node[old_shape[0] : new_shape[0], :] = data
156
156
 
157
157
  def remove_data(self, nodename, group=None):
158
158
  in_file_path = self._get_in_file_path(nodename, group)
@@ -184,8 +184,8 @@ if config.have_h5py:
184
184
  return result
185
185
 
186
186
  class H5CacheFileH5py(H5CacheFileBase, H5FileH5py):
187
- compressionFilter = 'lzf'
188
- # compressionFilter = 'blosc' # unavailable...
187
+ compression_filter = 'lzf'
188
+ # compression_filter = 'blosc' # unavailable...
189
189
 
190
190
  def is_cached(self, nodename, group=None):
191
191
  if not group:
@@ -200,7 +200,7 @@ if config.have_h5py:
200
200
  in_file_path,
201
201
  dtype=precision,
202
202
  shape=shape,
203
- compression=self.compressionFilter,
203
+ compression=self.compression_filter,
204
204
  chunks=True,
205
205
  )
206
206
 
acoular/internal.py CHANGED
@@ -13,13 +13,13 @@ def digest(obj, name='digest'):
13
13
  for i in do_.split('.'):
14
14
  vobj = list(vobj.trait_get(i.rstrip('[]')).values())[0]
15
15
  str_.append(str(vobj).encode('UTF-8'))
16
- except:
16
+ except: # noqa: E722
17
17
  pass
18
18
  return '_' + md5(b''.join(str_)).hexdigest()
19
19
 
20
20
 
21
- def ldigest(l):
21
+ def ldigest(obj_list):
22
22
  str_ = []
23
- for i in l:
23
+ for i in obj_list:
24
24
  str_.append(str(i.digest).encode('UTF-8'))
25
25
  return '_' + md5(b''.join(str_)).hexdigest()
acoular/sdinput.py CHANGED
@@ -9,12 +9,15 @@
9
9
  SoundDeviceSamplesGenerator
10
10
  """
11
11
 
12
- import sounddevice as sd
13
12
  from traits.api import Any, Bool, Int, Long, Property, cached_property, observe
14
13
 
14
+ from .configuration import config
15
15
  from .internal import digest
16
16
  from .tprocess import SamplesGenerator
17
17
 
18
+ if config.have_sounddevice:
19
+ import sounddevice as sd
20
+
18
21
 
19
22
  class SoundDeviceSamplesGenerator(SamplesGenerator):
20
23
  """Controller for sound card hardware using sounddevice library.
@@ -24,6 +27,12 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
24
27
  :meth:`result`.
25
28
  """
26
29
 
30
+ def __init__(self, *args, **kwargs):
31
+ super().__init__(*args, **kwargs)
32
+ if config.have_sounddevice is False:
33
+ msg = 'SoundDevice library not found but is required for using the SoundDeviceSamplesGenerator class.'
34
+ raise ImportError(msg)
35
+
27
36
  #: input device index, refers to sounddevice list
28
37
  device = Int(0, desc='input device index')
29
38
 
acoular/signals.py CHANGED
@@ -279,7 +279,21 @@ class SineGenerator(SignalGenerator):
279
279
 
280
280
 
281
281
  class GenericSignalGenerator(SignalGenerator):
282
- """Generate signal from output of :class:`~acoular.tprocess.SamplesGenerator` object."""
282
+ """Generate signal from output of :class:`~acoular.tprocess.SamplesGenerator` object.
283
+
284
+ This class can be used to inject arbitrary signals into Acoular processing
285
+ chains. For example, it can be used to read signals from a HDF5 file or create any signal
286
+ by using the :class:`acoular.sources.TimeSamples` class.
287
+
288
+ Example
289
+ -------
290
+ >>> import numpy as np
291
+ >>> from acoular import TimeSamples, GenericSignalGenerator
292
+ >>> data = np.random.rand(1000, 1)
293
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
294
+ >>> sig = GenericSignalGenerator(source=ts)
295
+
296
+ """
283
297
 
284
298
  #: Data source; :class:`~acoular.tprocess.SamplesGenerator` or derived object.
285
299
  source = Trait(SamplesGenerator)
acoular/sources.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Measured multichannel data managment and simulation of acoustic sources.
4
+ """Measured multichannel data management and simulation of acoustic sources.
5
5
 
6
6
  .. autosummary::
7
7
  :toctree: generated/
@@ -22,6 +22,7 @@
22
22
 
23
23
  # imports from other packages
24
24
 
25
+ import contextlib
25
26
  from os import path
26
27
  from warnings import warn
27
28
 
@@ -104,7 +105,7 @@ def _fill_mic_signal_block(out, signal, rm, ind, blocksize, numchannels, up, pre
104
105
  return out
105
106
 
106
107
 
107
- def spherical_hn1(n, z, derivativearccos=False):
108
+ def spherical_hn1(n, z):
108
109
  """Spherical Hankel Function of the First Kind."""
109
110
  return spherical_jn(n, z, derivative=False) + 1j * spherical_yn(n, z, derivative=False)
110
111
 
@@ -146,7 +147,7 @@ def get_radiation_angles(direction, mpos, sourceposition):
146
147
  return azi, ele
147
148
 
148
149
 
149
- def get_modes(lOrder, direction, mpos, sourceposition=None):
150
+ def get_modes(lOrder, direction, mpos, sourceposition=None): # noqa: N803
150
151
  """Returns Spherical Harmonic Radiation Pattern at the Microphones.
151
152
 
152
153
  Parameters
@@ -170,9 +171,9 @@ def get_modes(lOrder, direction, mpos, sourceposition=None):
170
171
  azi, ele = get_radiation_angles(direction, mpos, sourceposition) # angles between source and mics
171
172
  modes = zeros((azi.shape[0], (lOrder + 1) ** 2), dtype=complex128)
172
173
  i = 0
173
- for l in range(lOrder + 1):
174
- for m in range(-l, l + 1):
175
- modes[:, i] = sph_harm(m, l, azi, ele)
174
+ for lidx in range(lOrder + 1):
175
+ for m in range(-lidx, lidx + 1):
176
+ modes[:, i] = sph_harm(m, lidx, azi, ele)
176
177
  if m < 0:
177
178
  modes[:, i] = modes[:, i].conj() * 1j
178
179
  i += 1
@@ -180,12 +181,44 @@ def get_modes(lOrder, direction, mpos, sourceposition=None):
180
181
 
181
182
 
182
183
  class TimeSamples(SamplesGenerator):
183
- """Container for time data in `*.h5` format.
184
-
185
- This class loads measured data from h5 files and
186
- and provides information about this data.
187
- It also serves as an interface where the data can be accessed
188
- (e.g. for use in a block chain) via the :meth:`result` generator.
184
+ """Container for processing time data in `*.h5` or NumPy array format.
185
+
186
+ This class loads measured data from HDF5 files and provides information about this data.
187
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
188
+ :meth:`result` generator.
189
+
190
+ Examples
191
+ --------
192
+ Data can be loaded from a HDF5 file as follows:
193
+
194
+ >>> from acoular import TimeSamples
195
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
196
+ >>> ts = TimeSamples(name=name) # doctest: +SKIP
197
+ >>> print(f'number of channels: {ts.numchannels}') # doctest: +SKIP
198
+ number of channels: 56 # doctest: +SKIP
199
+
200
+ Alternatively, the time data can be specified directly as a numpy array.
201
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
202
+
203
+ >>> import numpy as np
204
+ >>> data = np.random.rand(1000, 4)
205
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
206
+
207
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator.
208
+ The last block will be shorter than the block size if the number of samples is not a multiple of the block size.
209
+
210
+ >>> blocksize = 512
211
+ >>> generator = ts.result(num=blocksize)
212
+ >>> for block in generator:
213
+ ... print(block.shape)
214
+ (512, 4)
215
+ (488, 4)
216
+
217
+ See Also
218
+ --------
219
+ acoular.sources.MaskedTimeSamples :
220
+ Extends the functionality of class :class:`TimeSamples` by enabling the definition of start and stop samples
221
+ as well as the specification of invalid channels.
189
222
  """
190
223
 
191
224
  #: Full name of the .h5 file with data.
@@ -233,31 +266,31 @@ class TimeSamples(SamplesGenerator):
233
266
  return path.splitext(path.basename(self.name))[0]
234
267
 
235
268
  @on_trait_change('basename')
236
- def load_data(self):
269
+ def _load_data(self):
237
270
  """Open the .h5 file and set attributes."""
238
271
  if not path.isfile(self.name):
239
- # no file there
240
- self.numsamples = 0
241
- self.numchannels = 0
242
272
  self.sample_freq = 0
243
273
  raise OSError('No such file: %s' % self.name)
244
274
  if self.h5f is not None:
245
- try:
275
+ with contextlib.suppress(OSError):
246
276
  self.h5f.close()
247
- except OSError:
248
- pass
249
277
  file = _get_h5file_class()
250
278
  self.h5f = file(self.name)
251
- self.load_timedata()
252
- self.load_metadata()
279
+ self._load_timedata()
280
+ self._load_metadata()
253
281
 
254
- def load_timedata(self):
282
+ @on_trait_change('data')
283
+ def _load_shapes(self):
284
+ """Set numchannels and numsamples from data."""
285
+ if self.data is not None:
286
+ self.numsamples, self.numchannels = self.data.shape
287
+
288
+ def _load_timedata(self):
255
289
  """Loads timedata from .h5 file. Only for internal use."""
256
290
  self.data = self.h5f.get_data_by_reference('time_data')
257
291
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
258
- (self.numsamples, self.numchannels) = self.data.shape
259
292
 
260
- def load_metadata(self):
293
+ def _load_metadata(self):
261
294
  """Loads metadata from .h5 file. Only for internal use."""
262
295
  self.metadata = {}
263
296
  if '/metadata' in self.h5f:
@@ -266,15 +299,20 @@ class TimeSamples(SamplesGenerator):
266
299
  def result(self, num=128):
267
300
  """Python generator that yields the output block-wise.
268
301
 
302
+ Reads the time data either from a HDF5 file or from a numpy array given
303
+ by :attr:`data` and iteratively returns a block of size `num` samples.
304
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
305
+
269
306
  Parameters
270
307
  ----------
271
308
  num : integer, defaults to 128
272
309
  This parameter defines the size of the blocks to be yielded
273
- (i.e. the number of samples per block) .
310
+ (i.e. the number of samples per block).
274
311
 
275
- Returns
276
- -------
277
- Samples in blocks of shape (num, numchannels).
312
+ Yields
313
+ ------
314
+ numpy.ndarray
315
+ Samples in blocks of shape (num, numchannels).
278
316
  The last block may be shorter than num.
279
317
 
280
318
  """
@@ -298,14 +336,40 @@ class TimeSamples(SamplesGenerator):
298
336
 
299
337
 
300
338
  class MaskedTimeSamples(TimeSamples):
301
- """Container for time data in `*.h5` format.
302
-
303
- This class loads measured data from h5 files
304
- and provides information about this data.
305
- It supports storing information about (in)valid samples and (in)valid channels
306
- It also serves as an interface where the data can be accessed
307
- (e.g. for use in a block chain) via the :meth:`result` generator.
308
-
339
+ """Container for processing time data in `*.h5` or NumPy array format.
340
+
341
+ This class loads measured data from HDF5 files and provides information about this data.
342
+ It supports storing information about (in)valid samples and (in)valid channels and allows
343
+ to specify a start and stop index for the valid samples.
344
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
345
+ :meth:`result` generator.
346
+
347
+ Examples
348
+ --------
349
+ Data can be loaded from a HDF5 file and invalid channels can be specified as follows:
350
+
351
+ >>> from acoular import MaskedTimeSamples
352
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
353
+ >>> ts = MaskedTimeSamples(name=name, invalid_channels=[0, 1]) # doctest: +SKIP
354
+ >>> print(f'number of valid channels: {ts.numchannels}') # doctest: +SKIP
355
+ number of valid channels: 54 # doctest: +SKIP
356
+
357
+ Alternatively, the time data can be specified directly as a numpy array.
358
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
359
+
360
+ >>> from acoular import MaskedTimeSamples
361
+ >>> import numpy as np
362
+ >>> data = np.random.rand(1000, 4)
363
+ >>> ts = MaskedTimeSamples(data=data, sample_freq=51200)
364
+
365
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator:
366
+
367
+ >>> blocksize = 512
368
+ >>> generator = ts.result(num=blocksize)
369
+ >>> for block in generator:
370
+ ... print(block.shape)
371
+ (512, 4)
372
+ (488, 4)
309
373
  """
310
374
 
311
375
  #: Index of the first sample to be considered valid.
@@ -362,26 +426,28 @@ class MaskedTimeSamples(TimeSamples):
362
426
  return sli[1] - sli[0]
363
427
 
364
428
  @on_trait_change('basename')
365
- def load_data(self):
429
+ def _load_data(self):
366
430
  # """ open the .h5 file and set attributes
367
431
  # """
368
432
  if not path.isfile(self.name):
369
433
  # no file there
370
- self.numsamples_total = 0
371
- self.numchannels_total = 0
372
434
  self.sample_freq = 0
373
435
  raise OSError('No such file: %s' % self.name)
374
436
  if self.h5f is not None:
375
- try:
437
+ with contextlib.suppress(OSError):
376
438
  self.h5f.close()
377
- except OSError:
378
- pass
379
439
  file = _get_h5file_class()
380
440
  self.h5f = file(self.name)
381
- self.load_timedata()
382
- self.load_metadata()
441
+ self._load_timedata()
442
+ self._load_metadata()
443
+
444
+ @on_trait_change('data')
445
+ def _load_shapes(self):
446
+ """Set numchannels and numsamples from data."""
447
+ if self.data is not None:
448
+ self.numsamples_total, self.numchannels_total = self.data.shape
383
449
 
384
- def load_timedata(self):
450
+ def _load_timedata(self):
385
451
  """Loads timedata from .h5 file. Only for internal use."""
386
452
  self.data = self.h5f.get_data_by_reference('time_data')
387
453
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
@@ -390,15 +456,20 @@ class MaskedTimeSamples(TimeSamples):
390
456
  def result(self, num=128):
391
457
  """Python generator that yields the output block-wise.
392
458
 
459
+ Reads the time data either from a HDF5 file or from a numpy array given
460
+ by :attr:`data` and iteratively returns a block of size `num` samples.
461
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
462
+
393
463
  Parameters
394
464
  ----------
395
465
  num : integer, defaults to 128
396
466
  This parameter defines the size of the blocks to be yielded
397
467
  (i.e. the number of samples per block).
398
468
 
399
- Returns
400
- -------
401
- Samples in blocks of shape (num, numchannels).
469
+ Yields
470
+ ------
471
+ numpy.ndarray
472
+ Samples in blocks of shape (num, numchannels).
402
473
  The last block may be shorter than num.
403
474
 
404
475
  """
@@ -588,7 +659,7 @@ class SphericalHarmonicSource(PointSource):
588
659
  """
589
660
 
590
661
  #: Order of spherical harmonic source
591
- lOrder = Int(0, desc='Order of spherical harmonic')
662
+ lOrder = Int(0, desc='Order of spherical harmonic') # noqa: N815
592
663
 
593
664
  alpha = CArray(desc='coefficients of the (lOrder,) spherical harmonic mode')
594
665
 
acoular/spectra.py CHANGED
@@ -383,7 +383,7 @@ class PowerSpectra(BaseSpectra):
383
383
  wind = wind[newaxis, :].swapaxes(0, 1)
384
384
  numfreq = int(self.block_size / 2 + 1)
385
385
  csm_shape = (numfreq, t.numchannels, t.numchannels)
386
- csmUpper = zeros(csm_shape, dtype=self.precision)
386
+ csm_upper = zeros(csm_shape, dtype=self.precision)
387
387
  # print "num blocks", self.num_blocks
388
388
  # for backward compatibility
389
389
  if self.calib and self.calib.num_mics > 0:
@@ -394,11 +394,11 @@ class PowerSpectra(BaseSpectra):
394
394
  # get time data blockwise
395
395
  for data in self.get_source_data():
396
396
  ft = fft.rfft(data * wind, None, 0).astype(self.precision)
397
- calcCSM(csmUpper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
397
+ calcCSM(csm_upper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
398
398
  # create the full csm matrix via transposing and complex conj.
399
- csmLower = csmUpper.conj().transpose(0, 2, 1)
400
- [fill_diagonal(csmLower[cntFreq, :, :], 0) for cntFreq in range(csmLower.shape[0])]
401
- csm = csmLower + csmUpper
399
+ csm_lower = csm_upper.conj().transpose(0, 2, 1)
400
+ [fill_diagonal(csm_lower[cntFreq, :, :], 0) for cntFreq in range(csm_lower.shape[0])]
401
+ csm = csm_lower + csm_upper
402
402
  # onesided spectrum: multiplication by 2.0=sqrt(2)^2
403
403
  return csm * (2.0 / self.block_size / weight / self.num_blocks)
404
404