acoular 24.5__py3-none-any.whl → 24.10__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/fprocess.py ADDED
@@ -0,0 +1,368 @@
1
+ # ------------------------------------------------------------------------------
2
+ # Copyright (c) Acoular Development Team.
3
+ # ------------------------------------------------------------------------------
4
+ """Implements blockwise processing methods in the frequency domain.
5
+
6
+ .. autosummary::
7
+ :toctree: generated/
8
+
9
+ RFFT
10
+ IRFFT
11
+ AutoPowerSpectra
12
+ CrossPowerSpectra
13
+ FFTSpectra
14
+ """
15
+
16
+ from warnings import warn
17
+
18
+ import numpy as np
19
+ from scipy import fft
20
+ from traits.api import Bool, CArray, Enum, Instance, Int, Property, Trait, Union, cached_property
21
+
22
+ from .base import SamplesGenerator, SpectraGenerator, SpectraOut, TimeOut
23
+ from .fastFuncs import calcCSM
24
+ from .internal import digest
25
+ from .spectra import BaseSpectra
26
+ from .tools.utils import SamplesBuffer
27
+
28
+
29
+ class RFFT(BaseSpectra, SpectraOut):
30
+ """Provides the one-sided Fast Fourier Transform (FFT) for real-valued multichannel time data.
31
+
32
+ The FFT is calculated block-wise, i.e. the input data is divided into blocks of length
33
+ :attr:`block_size` and the FFT is calculated for each block. Optionally, a window function
34
+ can be applied to the data before the FFT calculation via the :attr:`window` attribute.
35
+ """
36
+
37
+ #: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
38
+ source = Instance(SamplesGenerator)
39
+
40
+ #: Number of workers to use for the FFT calculation. If negative values are used,
41
+ #: all available logical CPUs will be considered (``scipy.fft.rfft`` implementation wraps around from ``os.cpu_count()``).
42
+ #: Default is `None` (handled by scipy)
43
+ workers = Union(Int(), None, default_value=None, desc='number of workers to use')
44
+
45
+ #: Scaling method, either 'amplitude', 'energy' or :code:`none`.
46
+ #: Default is :code:`none`.
47
+ #: 'energy': compensates for the energy loss due to truncation of the FFT result. The resulting
48
+ #: one-sided spectrum is multiplied by 2.0, except for the DC and Nyquist frequency.
49
+ #: 'amplitude': scales the one-sided spectrum so that the amplitude of discrete tones does not depend
50
+ #: on the block size.
51
+ scaling = Enum('none', 'energy', 'amplitude')
52
+
53
+ #: block size of the FFT. Default is 1024.
54
+ block_size = Property()
55
+
56
+ #: Number of frequencies in the output.
57
+ numfreqs = Property(depends_on='_block_size')
58
+
59
+ #: Number of snapshots in the output.
60
+ numsamples = Property(depends_on='source.numsamples, _block_size')
61
+
62
+ #: 1-D array of FFT sample frequencies.
63
+ freqs = Property()
64
+
65
+ # internal block size variable
66
+ _block_size = Int(1024, desc='block size of the FFT')
67
+
68
+ # internal identifier
69
+ digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
70
+
71
+ @cached_property
72
+ def _get_digest(self):
73
+ return digest(self)
74
+
75
+ @cached_property
76
+ def _get_numfreqs(self):
77
+ return int(self.block_size / 2 + 1)
78
+
79
+ @cached_property
80
+ def _get_numsamples(self):
81
+ if self.source.numsamples >= 0:
82
+ return int(np.floor(self.source.numsamples / self.block_size))
83
+ return -1
84
+
85
+ def _get_block_size(self):
86
+ return self._block_size
87
+
88
+ def _set_block_size(self, value):
89
+ if value % 2 != 0:
90
+ msg = 'Block size must be even'
91
+ raise ValueError(msg)
92
+ self._block_size = value
93
+
94
+ def _scale(self, data, scaling_value):
95
+ """Corrects the energy of the one-sided FFT data."""
96
+ if self.scaling == 'amplitude' or self.scaling == 'energy':
97
+ data[1:-1] *= 2.0
98
+ data *= scaling_value
99
+ return data
100
+
101
+ def _get_freqs(self):
102
+ """Return the Discrete Fourier Transform sample frequencies.
103
+
104
+ Returns
105
+ -------
106
+ f : ndarray
107
+ 1-D Array of length *block_size/2+1* containing the sample frequencies.
108
+
109
+ """
110
+ if self.source is not None:
111
+ return abs(fft.fftfreq(self.block_size, 1.0 / self.source.sample_freq)[: int(self.block_size / 2 + 1)])
112
+ return np.array([])
113
+
114
+ def result(self, num=1):
115
+ """Python generator that yields the output block-wise.
116
+
117
+ Parameters
118
+ ----------
119
+ num : integer
120
+ This parameter defines the number of multi-channel spectra (i.e. snapshots) per block
121
+ returned by the generator.
122
+
123
+ Returns
124
+ -------
125
+ Spectra block of shape (num, :attr:`numchannels`*:attr:`numfreqs`).
126
+ The last block may be shorter than num.
127
+
128
+ """
129
+ wind = self.window_(self.block_size)
130
+ if self.scaling == 'none' or self.scaling == 'energy': # only compensate for the window
131
+ svalue = 1 / np.sqrt(np.dot(wind, wind) / self.block_size)
132
+ elif self.scaling == 'amplitude': # compensates for the window and the energy loss
133
+ svalue = 1 / wind.sum()
134
+ wind = wind[:, np.newaxis]
135
+ fftdata = np.zeros((num, self.numchannels * self.numfreqs), dtype=self.precision)
136
+ j = 0
137
+ for i, data in enumerate(self._get_source_data()): # yields one block of time data
138
+ j = i % num
139
+ fftdata[j] = self._scale(
140
+ fft.rfft(data * wind, n=self.block_size, axis=0, workers=self.workers).astype(self.precision),
141
+ scaling_value=svalue,
142
+ ).reshape(-1)
143
+ if j == num - 1:
144
+ yield fftdata
145
+ if j < num - 1: # yield remaining fft spectra
146
+ yield fftdata[: j + 1]
147
+
148
+
149
+ class IRFFT(TimeOut):
150
+ """Calculates the inverse Fast Fourier Transform (IFFT) for one-sided multi-channel spectra."""
151
+
152
+ #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
153
+ source = Instance(SpectraGenerator)
154
+
155
+ #: Number of workers to use for the FFT calculation. If negative values are used,
156
+ #: all available logical CPUs will be considered (``scipy.fft.rfft`` implementation wraps around from ``os.cpu_count()``).
157
+ #: Default is `None` (handled by scipy)
158
+ workers = Union(Int(), None, default_value=None, desc='number of workers to use')
159
+
160
+ #: The floating-number-precision of the resulting time signals, corresponding to numpy dtypes.
161
+ #: Default is 64 bit.
162
+ precision = Trait('float64', 'float32', desc='precision of the time signal after the ifft')
163
+
164
+ #: Number of time samples in the output.
165
+ numsamples = Property(depends_on='source.numsamples, source._block_size')
166
+
167
+ # internal time signal buffer to handle arbitrary output block sizes
168
+ _buffer = CArray(desc='signal buffer')
169
+
170
+ # internal identifier
171
+ digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
172
+
173
+ def _get_numsamples(self):
174
+ if self.source.numsamples >= 0:
175
+ return int(self.source.numsamples * self.source.block_size)
176
+ return -1
177
+
178
+ @cached_property
179
+ def _get_digest(self):
180
+ return digest(self)
181
+
182
+ def _validate(self):
183
+ if not self.source.block_size or self.source.block_size < 0:
184
+ msg = (
185
+ f'Source of class {self.__class__.__name__} has an unknown blocksize: {self.source.block_size}.'
186
+ 'This is likely due to incomplete spectral data from which the inverse FFT cannot be calculated.'
187
+ )
188
+ raise ValueError(msg)
189
+ if (self.source.numfreqs - 1) * 2 != self.source.block_size:
190
+ msg = (
191
+ f'Block size must be 2*(numfreqs-1) but is {self.source.block_size}.'
192
+ 'This is likely due to incomplete spectral data from which the inverse FFT cannot be calculated.'
193
+ )
194
+ raise ValueError(msg)
195
+ if self.source.block_size % 2 != 0:
196
+ msg = f'Block size must be even but is {self.source.block_size}.'
197
+ raise ValueError(msg)
198
+
199
+ def result(self, num):
200
+ """Python generator that yields the output block-wise.
201
+
202
+ Parameters
203
+ ----------
204
+ num : integer
205
+ This parameter defines the size of the blocks to be yielded
206
+ (i.e. the number of samples per block). The last block may be shorter than num.
207
+
208
+ Yields
209
+ ------
210
+ numpy.ndarray
211
+ Yields blocks of shape (num, numchannels).
212
+ """
213
+ self._validate()
214
+ bs = self.source.block_size
215
+ if num != bs:
216
+ buffer_length = (int(np.ceil(num / bs)) + 1) * bs
217
+ buffer = SamplesBuffer(source=self, source_num=bs, length=buffer_length, dtype=self.precision)
218
+ yield from buffer.result(num)
219
+ else:
220
+ for spectra in self.source.result(1):
221
+ yield fft.irfft(
222
+ spectra.reshape(self.source.numfreqs, self.numchannels), n=num, axis=0, workers=self.workers
223
+ )
224
+
225
+
226
+ class AutoPowerSpectra(SpectraOut):
227
+ """Calculates the real-valued auto-power spectra."""
228
+
229
+ #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
230
+ source = Instance(SpectraGenerator)
231
+
232
+ #: Scaling method, either 'power' or 'psd' (Power Spectral Density).
233
+ #: Only relevant if the source is a :class:`~acoular.fprocess.FreqInOut` object.
234
+ scaling = Enum('power', 'psd')
235
+
236
+ #: Determines if the spectra yielded by the source are single-sided spectra.
237
+ single_sided = Bool(True, desc='single sided spectrum')
238
+
239
+ #: The floating-number-precision of entries, corresponding to numpy dtypes. Default is 64 bit.
240
+ precision = Trait('float64', 'float32', desc='floating-number-precision')
241
+
242
+ # internal identifier
243
+ digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided'])
244
+
245
+ @cached_property
246
+ def _get_digest(self):
247
+ return digest(self)
248
+
249
+ def _get_scaling_value(self):
250
+ scale = 1 / self.block_size**2
251
+ if self.single_sided:
252
+ scale *= 2
253
+ if self.scaling == 'psd':
254
+ scale *= self.block_size * self.source.sample_freq
255
+ return scale
256
+
257
+ def result(self, num=1):
258
+ """Python generator that yields the real-valued auto-power spectra.
259
+
260
+ Parameters
261
+ ----------
262
+ num : integer
263
+ This parameter defines the number of snapshots within each output data block.
264
+
265
+ Yields
266
+ ------
267
+ numpy.ndarray
268
+ Yields blocks of shape (num, numchannels*numfreqs).
269
+ The last block may be shorter than num.
270
+
271
+ """
272
+ scale = self._get_scaling_value()
273
+ for temp in self.source.result(num):
274
+ yield ((temp * temp.conjugate()).real * scale).astype(self.precision)
275
+
276
+
277
+ class CrossPowerSpectra(AutoPowerSpectra):
278
+ """Calculates the complex-valued auto- and cross-power spectra.
279
+
280
+ Receives the complex-valued spectra from the source and returns the cross-spectral matrix (CSM) in a flattened
281
+ representation (i.e. the auto- and cross-power spectra are concatenated along the last axis).
282
+ If :attr:`calc_mode` is 'full', the full CSM is calculated, if 'upper', only the upper triangle is calculated.
283
+ """
284
+
285
+ #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
286
+ source = Trait(SpectraGenerator)
287
+
288
+ #: The floating-number-precision of entries of csm, eigenvalues and
289
+ #: eigenvectors, corresponding to numpy dtypes. Default is 64 bit.
290
+ precision = Trait('complex128', 'complex64', desc='precision of the fft')
291
+
292
+ #: Calculation mode, either 'full' or 'upper'.
293
+ #: 'full' calculates the full cross-spectral matrix, 'upper' calculates
294
+ # only the upper triangle. Default is 'full'.
295
+ calc_mode = Trait('full', 'upper', 'lower', desc='calculation mode')
296
+
297
+ #: Number of channels in output. If :attr:`calc_mode` is 'full', then
298
+ #: :attr:`numchannels` is :math:`n^2`, where :math:`n` is the number of
299
+ #: channels in the input. If :attr:`calc_mode` is 'upper', then
300
+ #: :attr:`numchannels` is :math:`n + n(n-1)/2`.
301
+ numchannels = Property(depends_on='source.numchannels')
302
+
303
+ # internal identifier
304
+ digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided', 'calc_mode'])
305
+
306
+ @cached_property
307
+ def _get_numchannels(self):
308
+ n = self.source.numchannels
309
+ return n**2 if self.calc_mode == 'full' else int(n + n * (n - 1) / 2)
310
+
311
+ @cached_property
312
+ def _get_digest(self):
313
+ return digest(self)
314
+
315
+ def result(self, num=1):
316
+ """Python generator that yields the output block-wise.
317
+
318
+ Parameters
319
+ ----------
320
+ num : integer
321
+ This parameter defines the size of the blocks to be yielded
322
+ (i.e. the number of samples per block).
323
+
324
+ Yields
325
+ ------
326
+ numpy.ndarray
327
+ Yields blocks of shape (num, numchannels*numfreq).
328
+ """
329
+ nc_src = self.source.numchannels
330
+ nc = self.numchannels
331
+ nf = self.numfreqs
332
+ scale = self._get_scaling_value()
333
+
334
+ csm_flat = np.zeros((num, nc * nf), dtype=self.precision)
335
+ csm_upper = np.zeros((nf, nc_src, nc_src), dtype=self.precision)
336
+ for data in self.source.result(num):
337
+ for i in range(data.shape[0]):
338
+ calcCSM(csm_upper, data[i].astype(self.precision).reshape(nf, nc_src))
339
+ if self.calc_mode == 'full':
340
+ csm_lower = csm_upper.conj().transpose(0, 2, 1)
341
+ [np.fill_diagonal(csm_lower[cntFreq, :, :], 0) for cntFreq in range(csm_lower.shape[0])]
342
+ csm_flat[i] = (csm_lower + csm_upper).reshape(-1)
343
+ elif self.calc_mode == 'upper':
344
+ csm_flat[i] = csm_upper[:, :nc].reshape(-1)
345
+ else: # lower
346
+ csm_lower = csm_upper.conj().transpose(0, 2, 1)
347
+ csm_flat[i] = csm_lower[:, :nc].reshape(-1)
348
+ csm_upper[...] = 0 # calcCSM adds cummulative
349
+ yield csm_flat[: i + 1] * scale
350
+
351
+
352
+ class FFTSpectra(RFFT):
353
+ """Provides the one-sided Fast Fourier Transform (FFT) for multichannel time data.
354
+
355
+ Alias for :class:`~acoular.fprocess.RFFT`.
356
+
357
+ .. deprecated:: 24.10
358
+ Using :class:`~acoular.fprocess.FFTSpectra` is deprecated and will be removed in Acoular
359
+ version 25.07. Use :class:`~acoular.fprocess.RFFT` instead.
360
+ """
361
+
362
+ def __init__(self, *args, **kwargs):
363
+ super().__init__(*args, **kwargs)
364
+ warn(
365
+ 'Using FFTSpectra is deprecated and will be removed in Acoular version 25.07. Use class RFFT instead.',
366
+ DeprecationWarning,
367
+ stacklevel=2,
368
+ )
acoular/grids.py CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  # imports from other packages
27
27
  from os import path
28
+ from warnings import warn
28
29
 
29
30
  from numpy import (
30
31
  absolute,
@@ -32,7 +33,7 @@ from numpy import (
32
33
  arange,
33
34
  argmin,
34
35
  array,
35
- asfarray,
36
+ asarray,
36
37
  concatenate,
37
38
  copysign,
38
39
  fabs,
@@ -50,12 +51,11 @@ from numpy import (
50
51
  where,
51
52
  zeros,
52
53
  )
53
- from numpy.linalg import norm
54
+ from scipy.linalg import norm
54
55
 
55
56
  # from matplotlib.path import Path
56
57
  from scipy.spatial import Delaunay
57
58
  from traits.api import (
58
- Any,
59
59
  Bool,
60
60
  CArray,
61
61
  File,
@@ -66,6 +66,7 @@ from traits.api import (
66
66
  List,
67
67
  Property,
68
68
  Tuple,
69
+ Union,
69
70
  cached_property,
70
71
  on_trait_change,
71
72
  property_depends_on,
@@ -110,8 +111,8 @@ def _det(xvert, yvert):
110
111
  all points are collinear.
111
112
 
112
113
  """
113
- xvert = asfarray(xvert)
114
- yvert = asfarray(yvert)
114
+ xvert = asarray(xvert, dtype=float)
115
+ yvert = asarray(yvert, dtype=float)
115
116
  x_prev = concatenate(([xvert[-1]], xvert[:-1]))
116
117
  y_prev = concatenate(([yvert[-1]], yvert[:-1]))
117
118
  return sum(yvert * x_prev - xvert * y_prev, axis=0)
@@ -128,8 +129,8 @@ class Polygon:
128
129
  if len(x) != len(y):
129
130
  msg = 'x and y must be equally sized.'
130
131
  raise IndexError(msg)
131
- self.x = asfarray(x)
132
- self.y = asfarray(y)
132
+ self.x = asarray(x, dtype=float)
133
+ self.y = asarray(y, dtype=float)
133
134
  # Closes the polygon if were open
134
135
  x1, y1 = x[0], y[0]
135
136
  xn, yn = x[-1], y[-1]
@@ -166,8 +167,8 @@ class Polygon:
166
167
  Software, Vol 7, No. 1, pp 45-47.
167
168
 
168
169
  """
169
- xpoint = asfarray(xpoint)
170
- ypoint = asfarray(ypoint)
170
+ xpoint = asarray(xpoint, dtype=float)
171
+ ypoint = asarray(ypoint, dtype=float)
171
172
  # Scalar to array
172
173
  if xpoint.shape == ():
173
174
  xpoint = array([xpoint], dtype=float)
@@ -296,6 +297,7 @@ class Grid(HasPrivateTraits):
296
297
  def pos(self):
297
298
  """Calculates grid co-ordinates.
298
299
  Deprecated; use :attr:`gpos` attribute instead.
300
+ The :meth:`pos` method will be removed in version 25.01.
299
301
 
300
302
  Returns
301
303
  -------
@@ -303,6 +305,10 @@ class Grid(HasPrivateTraits):
303
305
  The grid point x, y, z-coordinates in one array.
304
306
 
305
307
  """
308
+ msg = (
309
+ "The 'pos' method is deprecated and will be removed in version 25.01. " "Use the 'gpos' attribute instead."
310
+ )
311
+ warn(msg, DeprecationWarning, stacklevel=2)
306
312
  return self.gpos # array([[0.], [0.], [0.]])
307
313
 
308
314
  def subdomain(self, sector):
@@ -535,7 +541,7 @@ class RectGrid3D(RectGrid):
535
541
  nzsteps = Property(desc='number of grid points along x-axis')
536
542
 
537
543
  # Private trait for increment handling
538
- _increment = Any(0.1)
544
+ _increment = Union(Float(), CArray(shape=(3,), dtype=float), default_value=0.1, desc='step size')
539
545
 
540
546
  #: The cell side length for the grid. This can either be a scalar (same
541
547
  #: increments in all 3 dimensions) or a (3,) array of floats with
@@ -550,8 +556,8 @@ class RectGrid3D(RectGrid):
550
556
  if isscalar(increment):
551
557
  try:
552
558
  self._increment = absolute(float(increment))
553
- except:
554
- raise TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment)
559
+ except ValueError as ve:
560
+ raise TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment) from ve
555
561
  elif len(increment) == 3:
556
562
  self._increment = array(increment, dtype=float)
557
563
  else:
@@ -559,14 +565,18 @@ class RectGrid3D(RectGrid):
559
565
 
560
566
  # Respective increments in x,y, and z-direction (in m).
561
567
  # Deprecated: Use :attr:`~RectGrid.increment` for this functionality
562
- increment3D = Property(desc='3D step sizes')
568
+ increment3D = Property(desc='3D step sizes') # noqa N815
563
569
 
564
- def _get_increment3D(self):
570
+ def _get_increment3D(self): # noqa N802
571
+ msg = "Using 'increment3D' is deprecated and will be removed in version 25.01." "Use 'increment' instead."
572
+ warn(msg, DeprecationWarning, stacklevel=2)
565
573
  if isscalar(self._increment):
566
574
  return array([self._increment, self._increment, self._increment])
567
575
  return self._increment
568
576
 
569
- def _set_increment3D(self, inc):
577
+ def _set_increment3D(self, inc): # noqa N802
578
+ msg = "Using 'increment3D' is deprecated and will be removed in version 25.01." "Use 'increment' instead."
579
+ warn(msg, DeprecationWarning, stacklevel=2)
570
580
  if not isscalar(inc) and len(inc) == 3:
571
581
  self._increment = array(inc, dtype=float)
572
582
  else:
@@ -587,21 +597,21 @@ class RectGrid3D(RectGrid):
587
597
 
588
598
  @property_depends_on('x_min, x_max, _increment')
589
599
  def _get_nxsteps(self):
590
- i = abs(self.increment3D[0])
600
+ i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[0])
591
601
  if i != 0:
592
602
  return int(round((abs(self.x_max - self.x_min) + i) / i))
593
603
  return 1
594
604
 
595
605
  @property_depends_on('y_min, y_max, _increment')
596
606
  def _get_nysteps(self):
597
- i = abs(self.increment3D[1])
607
+ i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[1])
598
608
  if i != 0:
599
609
  return int(round((abs(self.y_max - self.y_min) + i) / i))
600
610
  return 1
601
611
 
602
612
  @property_depends_on('z_min, z_max, _increment')
603
613
  def _get_nzsteps(self):
604
- i = abs(self.increment3D[2])
614
+ i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[2])
605
615
  if i != 0:
606
616
  return int(round((abs(self.z_max - self.z_min) + i) / i))
607
617
  return 1
@@ -655,9 +665,13 @@ class RectGrid3D(RectGrid):
655
665
  if z < self.z_min or z > self.z_max:
656
666
  msg = f'z-value out of range {z:f} ({self.z_min:f}, {self.z_max:f})'
657
667
  raise ValueError(msg)
658
- xi = int(round((x - self.x_min) / self.increment3D[0]))
659
- yi = int(round((y - self.y_min) / self.increment3D[1]))
660
- zi = int(round((z - self.z_min) / self.increment3D[2]))
668
+ if isscalar(self.increment):
669
+ incx = incy = incz = self.increment
670
+ else:
671
+ incx, incy, incz = self.increment
672
+ xi = int(round((x - self.x_min) / incx))
673
+ yi = int(round((y - self.y_min) / incy))
674
+ zi = int(round((z - self.z_min) / incz))
661
675
  return xi, yi, zi
662
676
 
663
677
  def indices(self, x1, y1, z1, x2, y2, z2):
@@ -792,7 +806,7 @@ class LineGrid(Grid):
792
806
  def _get_gpos(self):
793
807
  dist = self.length / (self.numpoints - 1)
794
808
  loc = array(self.loc, dtype=float).reshape((3, 1))
795
- direc_n = self.direction / norm(self.direction)
809
+ direc_n = array(self.direction) / norm(self.direction)
796
810
  pos = zeros((self.numpoints, 3))
797
811
  for s in range(self.numpoints):
798
812
  pos[s] = loc.T + direc_n * dist * s
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):