acoular 25.7__py3-none-any.whl → 25.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/aiaa/aiaa.py +7 -9
- acoular/base.py +6 -9
- acoular/calib.py +19 -18
- acoular/configuration.py +2 -2
- acoular/environments.py +102 -113
- acoular/fbeamform.py +296 -301
- acoular/fprocess.py +7 -4
- acoular/grids.py +98 -111
- acoular/h5cache.py +5 -1
- acoular/h5files.py +96 -9
- acoular/microphones.py +22 -27
- acoular/process.py +7 -11
- acoular/sdinput.py +0 -5
- acoular/signals.py +29 -27
- acoular/sources.py +189 -322
- acoular/spectra.py +33 -44
- acoular/tbeamform.py +217 -199
- acoular/tools/helpers.py +25 -33
- acoular/tools/metrics.py +5 -10
- acoular/tprocess.py +173 -209
- acoular/trajectory.py +5 -5
- acoular/version.py +2 -2
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/METADATA +6 -2
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/RECORD +27 -27
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/WHEEL +0 -0
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/licenses/LICENSE +0 -0
acoular/aiaa/aiaa.py
CHANGED
|
@@ -27,18 +27,17 @@ Examples
|
|
|
27
27
|
|
|
28
28
|
import contextlib
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
import numpy as np
|
|
31
31
|
from traits.api import (
|
|
32
32
|
File,
|
|
33
33
|
Instance,
|
|
34
34
|
Property,
|
|
35
35
|
Union,
|
|
36
36
|
cached_property,
|
|
37
|
-
|
|
37
|
+
observe,
|
|
38
38
|
property_depends_on,
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
from acoular.deprecation import deprecated_alias
|
|
42
41
|
from acoular.h5files import H5FileBase, _get_h5file_class
|
|
43
42
|
from acoular.internal import digest
|
|
44
43
|
from acoular.microphones import MicGeom
|
|
@@ -85,7 +84,6 @@ class TriggerAIAABenchmark(TimeSamplesAIAABenchmark):
|
|
|
85
84
|
(self.num_samples, self.num_channels) = self.data.shape
|
|
86
85
|
|
|
87
86
|
|
|
88
|
-
@deprecated_alias({'name': 'file'}, removal_version='25.10')
|
|
89
87
|
class CsmAIAABenchmark(PowerSpectraImport):
|
|
90
88
|
"""Class to load the CSM that is stored in AIAA Benchmark HDF5 file."""
|
|
91
89
|
|
|
@@ -115,8 +113,8 @@ class CsmAIAABenchmark(PowerSpectraImport):
|
|
|
115
113
|
def _get_basename(self):
|
|
116
114
|
return get_file_basename(self.file)
|
|
117
115
|
|
|
118
|
-
@
|
|
119
|
-
def
|
|
116
|
+
@observe('basename')
|
|
117
|
+
def _load_data(self, event): # noqa ARG002
|
|
120
118
|
"""Open the .h5 file and set attributes."""
|
|
121
119
|
if self.h5f is not None:
|
|
122
120
|
with contextlib.suppress(OSError):
|
|
@@ -156,7 +154,7 @@ class CsmAIAABenchmark(PowerSpectraImport):
|
|
|
156
154
|
ndarray
|
|
157
155
|
Array of length *block_size/2+1* containing the sample frequencies.
|
|
158
156
|
"""
|
|
159
|
-
return array(self.h5f.get_data_by_reference('/CsmData/binCenterFrequenciesHz')[:].flatten(), dtype=float)
|
|
157
|
+
return np.array(self.h5f.get_data_by_reference('/CsmData/binCenterFrequenciesHz')[:].flatten(), dtype=float)
|
|
160
158
|
|
|
161
159
|
|
|
162
160
|
class MicAIAABenchmark(MicGeom):
|
|
@@ -172,8 +170,8 @@ class MicAIAABenchmark(MicGeom):
|
|
|
172
170
|
None, File(filter=['*.h5'], exists=True), desc='name of the h5 file containing the microphone geometry'
|
|
173
171
|
)
|
|
174
172
|
|
|
175
|
-
@
|
|
176
|
-
def _import_mpos(self):
|
|
173
|
+
@observe('file')
|
|
174
|
+
def _import_mpos(self, event): # noqa ARG002
|
|
177
175
|
"""
|
|
178
176
|
Import the microphone positions from .h5 file.
|
|
179
177
|
|
acoular/base.py
CHANGED
|
@@ -7,6 +7,12 @@ The classes in this module are abstract base classes that provide a common inter
|
|
|
7
7
|
that generate an output via the generator :meth:`result` in block-wise manner. They are not intended
|
|
8
8
|
to be used directly, but to be subclassed by classes that implement the actual signal processing.
|
|
9
9
|
|
|
10
|
+
.. inheritance-diagram::
|
|
11
|
+
acoular.base
|
|
12
|
+
:top-classes:
|
|
13
|
+
acoular.base.Generator
|
|
14
|
+
:parts: 1
|
|
15
|
+
|
|
10
16
|
.. autosummary::
|
|
11
17
|
:toctree: generated/
|
|
12
18
|
|
|
@@ -32,11 +38,9 @@ from traits.api import (
|
|
|
32
38
|
)
|
|
33
39
|
|
|
34
40
|
# acoular imports
|
|
35
|
-
from .deprecation import deprecated_alias
|
|
36
41
|
from .internal import digest
|
|
37
42
|
|
|
38
43
|
|
|
39
|
-
@deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, removal_version='25.10')
|
|
40
44
|
class Generator(ABCHasStrictTraits):
|
|
41
45
|
"""Interface for any generating signal processing block.
|
|
42
46
|
|
|
@@ -156,7 +160,6 @@ class SpectraGenerator(Generator):
|
|
|
156
160
|
"""
|
|
157
161
|
|
|
158
162
|
|
|
159
|
-
@deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
|
|
160
163
|
class TimeOut(SamplesGenerator):
|
|
161
164
|
"""
|
|
162
165
|
Abstract base class receiving from a :attr:`source` and returning time domain signals.
|
|
@@ -204,11 +207,6 @@ class TimeOut(SamplesGenerator):
|
|
|
204
207
|
"""
|
|
205
208
|
|
|
206
209
|
|
|
207
|
-
@deprecated_alias(
|
|
208
|
-
{'numchannels': 'num_channels', 'numsamples': 'num_samples', 'numfreqs': 'num_freqs'},
|
|
209
|
-
read_only=True,
|
|
210
|
-
removal_version='25.10',
|
|
211
|
-
)
|
|
212
210
|
class SpectraOut(SpectraGenerator):
|
|
213
211
|
"""
|
|
214
212
|
Abstract base class receiving from a :attr:`source` and returning frequency domain signals.
|
|
@@ -263,7 +261,6 @@ class SpectraOut(SpectraGenerator):
|
|
|
263
261
|
"""
|
|
264
262
|
|
|
265
263
|
|
|
266
|
-
@deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
|
|
267
264
|
class InOut(SamplesGenerator, SpectraGenerator):
|
|
268
265
|
"""
|
|
269
266
|
Abstract base class receiving from a :attr:`source` and returning signals in the same domain.
|
acoular/calib.py
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
4
|
"""Implements calibration of multichannel time signals.
|
|
5
5
|
|
|
6
|
+
.. inheritance-diagram::
|
|
7
|
+
acoular.calib
|
|
8
|
+
:top-classes:
|
|
9
|
+
acoular.base.InOut
|
|
10
|
+
:parts: 1
|
|
11
|
+
|
|
6
12
|
.. autosummary::
|
|
7
13
|
:toctree: generated/
|
|
8
14
|
|
|
@@ -12,19 +18,14 @@
|
|
|
12
18
|
# imports from other packages
|
|
13
19
|
import xml.dom.minidom
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
from traits.api import CArray, CInt, File, List, Property, Union, cached_property,
|
|
17
|
-
|
|
18
|
-
import acoular as ac
|
|
19
|
-
|
|
20
|
-
from .base import InOut
|
|
21
|
+
import numpy as np
|
|
22
|
+
from traits.api import CArray, CInt, File, List, Property, Union, cached_property, observe
|
|
21
23
|
|
|
22
24
|
# acoular imports
|
|
23
|
-
from .
|
|
25
|
+
from .base import InOut, SamplesGenerator, SpectraGenerator
|
|
24
26
|
from .internal import digest
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
@deprecated_alias({'from_file': 'file'}, removal_version='25.10')
|
|
28
29
|
class Calib(InOut):
|
|
29
30
|
"""Processing block for handling calibration data in `*.xml` or NumPy format.
|
|
30
31
|
|
|
@@ -99,8 +100,8 @@ class Calib(InOut):
|
|
|
99
100
|
# Internal identifier
|
|
100
101
|
digest = Property(depends_on=['source.digest', 'data'])
|
|
101
102
|
|
|
102
|
-
@
|
|
103
|
-
def
|
|
103
|
+
@observe('data')
|
|
104
|
+
def _update_num_mics(self, event): # noqa ARG002
|
|
104
105
|
"""Sets the number of microphones based on the shape of the data array."""
|
|
105
106
|
self.num_mics = self.data.shape[0]
|
|
106
107
|
|
|
@@ -109,14 +110,14 @@ class Calib(InOut):
|
|
|
109
110
|
if len(self.invalid_channels) == 0:
|
|
110
111
|
return slice(0, None, None)
|
|
111
112
|
allr = [i for i in range(self.num_mics) if i not in self.invalid_channels]
|
|
112
|
-
return array(allr)
|
|
113
|
+
return np.array(allr)
|
|
113
114
|
|
|
114
115
|
@cached_property
|
|
115
116
|
def _get_digest(self):
|
|
116
117
|
return digest(self)
|
|
117
118
|
|
|
118
|
-
@
|
|
119
|
-
def
|
|
119
|
+
@observe('file')
|
|
120
|
+
def _import_data(self, event): # noqa ARG002
|
|
120
121
|
"""Loads the calibration data from `*.xml` file ."""
|
|
121
122
|
doc = xml.dom.minidom.parse(self.file)
|
|
122
123
|
names = []
|
|
@@ -124,7 +125,7 @@ class Calib(InOut):
|
|
|
124
125
|
for element in doc.getElementsByTagName('pos'):
|
|
125
126
|
names.append(element.getAttribute('Name'))
|
|
126
127
|
data.append(float(element.getAttribute('factor')))
|
|
127
|
-
self.data = array(data, 'd')
|
|
128
|
+
self.data = np.array(data, 'd')
|
|
128
129
|
self.num_mics = self.data.shape[0]
|
|
129
130
|
|
|
130
131
|
def __validate_data(self):
|
|
@@ -136,13 +137,13 @@ class Calib(InOut):
|
|
|
136
137
|
msg = 'No source data available.'
|
|
137
138
|
raise ValueError(msg)
|
|
138
139
|
tobj = self.source
|
|
139
|
-
while isinstance(tobj,
|
|
140
|
+
while isinstance(tobj, InOut):
|
|
140
141
|
tobj = tobj.source
|
|
141
|
-
if isinstance(tobj,
|
|
142
|
+
if isinstance(tobj, SamplesGenerator) and (self.data[self.channels].shape[0] != tobj.num_channels):
|
|
142
143
|
msg = f'calibration data shape {self.data[self.channels].shape[0]} does not match \
|
|
143
144
|
source data shape {tobj.num_channels}'
|
|
144
145
|
raise ValueError(msg)
|
|
145
|
-
if isinstance(tobj,
|
|
146
|
+
if isinstance(tobj, SpectraGenerator) and (
|
|
146
147
|
self.data[self.channels].shape[0] != tobj.num_channels * tobj.num_freqs
|
|
147
148
|
):
|
|
148
149
|
msg = f'calibration data shape {self.data[self.channels].shape[0]} does not match \
|
|
@@ -170,4 +171,4 @@ class Calib(InOut):
|
|
|
170
171
|
"""
|
|
171
172
|
self.__validate_data()
|
|
172
173
|
for block in self.source.result(num):
|
|
173
|
-
yield block * self.data[self.channels][newaxis]
|
|
174
|
+
yield block * self.data[self.channels][np.newaxis]
|
acoular/configuration.py
CHANGED
|
@@ -39,9 +39,9 @@ if 'numpy' in sys.modules:
|
|
|
39
39
|
# check if it uses OpenBLAS or another library
|
|
40
40
|
if 'openblas' in temp_stdout.getvalue().lower() and environ.get('OPENBLAS_NUM_THREADS') != '1':
|
|
41
41
|
# it's OpenBLAS, set numba threads=1 to avoid overcommittment
|
|
42
|
-
import numba
|
|
42
|
+
import numba as nb
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
nb.set_num_threads(1)
|
|
45
45
|
warn(
|
|
46
46
|
'We detected that Numpy is already loaded and uses OpenBLAS. Because '
|
|
47
47
|
'this conflicts with Numba parallel execution, we disable parallel '
|
acoular/environments.py
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
"""
|
|
5
5
|
Implements acoustic environments with and without flow.
|
|
6
6
|
|
|
7
|
+
.. inheritance-diagram::
|
|
8
|
+
acoular.environments.Environment
|
|
9
|
+
:include-subclasses:
|
|
10
|
+
:top-classes:
|
|
11
|
+
acoular.environments.Environment
|
|
12
|
+
:parts: 1
|
|
13
|
+
|
|
14
|
+
.. inheritance-diagram::
|
|
15
|
+
acoular.environments.FlowField
|
|
16
|
+
:include-subclasses:
|
|
17
|
+
:top-classes:
|
|
18
|
+
acoular.environments.FlowField
|
|
19
|
+
:parts: 1
|
|
20
|
+
|
|
7
21
|
.. autosummary::
|
|
8
22
|
:toctree: generated/
|
|
9
23
|
|
|
@@ -24,35 +38,10 @@ from abc import abstractmethod
|
|
|
24
38
|
from warnings import warn
|
|
25
39
|
|
|
26
40
|
import numba as nb
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
arccos,
|
|
30
|
-
arctan2,
|
|
31
|
-
array,
|
|
32
|
-
ascontiguousarray,
|
|
33
|
-
cos,
|
|
34
|
-
cross,
|
|
35
|
-
dot,
|
|
36
|
-
empty,
|
|
37
|
-
exp,
|
|
38
|
-
float32,
|
|
39
|
-
float64,
|
|
40
|
-
hstack,
|
|
41
|
-
identity,
|
|
42
|
-
isscalar,
|
|
43
|
-
matmul,
|
|
44
|
-
newaxis,
|
|
45
|
-
pi,
|
|
46
|
-
sign,
|
|
47
|
-
sin,
|
|
48
|
-
sqrt,
|
|
49
|
-
sum, # noqa: A004
|
|
50
|
-
vstack,
|
|
51
|
-
zeros_like,
|
|
52
|
-
)
|
|
41
|
+
import numpy as np
|
|
42
|
+
import scipy.linalg as spla
|
|
53
43
|
from scipy.integrate import ode
|
|
54
44
|
from scipy.interpolate import LinearNDInterpolator
|
|
55
|
-
from scipy.linalg import norm
|
|
56
45
|
from scipy.spatial import ConvexHull
|
|
57
46
|
from traits.api import (
|
|
58
47
|
ABCHasStrictTraits,
|
|
@@ -100,7 +89,7 @@ def dist_mat(gpos, mpos): # pragma: no cover
|
|
|
100
89
|
"""
|
|
101
90
|
_, M = mpos.shape
|
|
102
91
|
_, N = gpos.shape
|
|
103
|
-
rm = empty((N, M), dtype=gpos.dtype)
|
|
92
|
+
rm = np.empty((N, M), dtype=gpos.dtype)
|
|
104
93
|
TWO = rm.dtype.type(2.0) # make sure to have a float32 or float 64 literal
|
|
105
94
|
m0 = mpos[0]
|
|
106
95
|
m1 = mpos[1]
|
|
@@ -110,7 +99,7 @@ def dist_mat(gpos, mpos): # pragma: no cover
|
|
|
110
99
|
g1 = gpos[1, n]
|
|
111
100
|
g2 = gpos[2, n]
|
|
112
101
|
for m in range(M):
|
|
113
|
-
rm[n, m] = sqrt((g0 - m0[m]) ** TWO + (g1 - m1[m]) ** TWO + (g2 - m2[m]) ** TWO)
|
|
102
|
+
rm[n, m] = np.sqrt((g0 - m0[m]) ** TWO + (g1 - m1[m]) ** TWO + (g2 - m2[m]) ** TWO)
|
|
114
103
|
return rm
|
|
115
104
|
|
|
116
105
|
|
|
@@ -137,10 +126,10 @@ def cartToCyl(x, Q=None): # noqa: N802, N803
|
|
|
137
126
|
Cylindrical representation of given `N` points in cartesian coodrinates as
|
|
138
127
|
an array of shape `(3, N)` with new coordinates :math:`(\phi, r, z)`.
|
|
139
128
|
"""
|
|
140
|
-
Q = identity(3) if Q is None else Q
|
|
141
|
-
if not (Q == identity(3)).all(): # noqa: SIM300
|
|
142
|
-
x =
|
|
143
|
-
return array([arctan2(x[1], x[0]), sqrt(x[0] ** 2 + x[1] ** 2), x[2]])
|
|
129
|
+
Q = np.identity(3) if Q is None else Q
|
|
130
|
+
if not (Q == np.identity(3)).all(): # noqa: SIM300
|
|
131
|
+
x = Q @ x # modified position vector
|
|
132
|
+
return np.array([np.arctan2(x[1], x[0]), np.sqrt(x[0] ** 2 + x[1] ** 2), x[2]])
|
|
144
133
|
|
|
145
134
|
|
|
146
135
|
def cylToCart(x, Q=None): # noqa: N802, N803
|
|
@@ -166,10 +155,10 @@ def cylToCart(x, Q=None): # noqa: N802, N803
|
|
|
166
155
|
Cartesian representation of given `N` points in cylindrical coodrinates as
|
|
167
156
|
an array of shape `(3, N)` with coodinates :math:`(x, y, z)`.
|
|
168
157
|
"""
|
|
169
|
-
Q = identity(3) if Q is None else Q
|
|
170
|
-
if not (Q == identity(3)).all(): # noqa: SIM300
|
|
171
|
-
x =
|
|
172
|
-
return array([x[1] * sin(x[0]), x[1] * cos(x[0]), x[2]])
|
|
158
|
+
Q = np.identity(3) if Q is None else Q
|
|
159
|
+
if not (Q == np.identity(3)).all(): # noqa: SIM300
|
|
160
|
+
x = Q @ x # modified position vector
|
|
161
|
+
return np.array([x[1] * np.sin(x[0]), x[1] * np.cos(x[0]), x[2]])
|
|
173
162
|
|
|
174
163
|
|
|
175
164
|
class Environment(HasStrictTraits):
|
|
@@ -231,12 +220,12 @@ class Environment(HasStrictTraits):
|
|
|
231
220
|
#
|
|
232
221
|
# - shape `(N,)` if ``mpos`` is a single point, or
|
|
233
222
|
# - shape `(N, M)` if ``mpos`` consists of multiple points.
|
|
234
|
-
if isscalar(mpos):
|
|
235
|
-
mpos = array((0, 0, 0), dtype=float64)[:, newaxis]
|
|
236
|
-
rm = dist_mat(ascontiguousarray(gpos), ascontiguousarray(mpos))
|
|
237
|
-
# mpos = mpos[:, newaxis, :]
|
|
238
|
-
# rmv = gpos[:, :, newaxis]-mpos
|
|
239
|
-
# rm = sum(rmv*rmv, 0)**0.5
|
|
223
|
+
if np.isscalar(mpos):
|
|
224
|
+
mpos = np.array((0, 0, 0), dtype=np.float64)[:, np.newaxis]
|
|
225
|
+
rm = dist_mat(np.ascontiguousarray(gpos), np.ascontiguousarray(mpos))
|
|
226
|
+
# mpos = mpos[:, np.newaxis, :]
|
|
227
|
+
# rmv = gpos[:, :, np.newaxis]-mpos
|
|
228
|
+
# rm = np.sum(rmv*rmv, 0)**0.5
|
|
240
229
|
if rm.shape[1] == 1:
|
|
241
230
|
rm = rm[:, 0]
|
|
242
231
|
return rm
|
|
@@ -265,7 +254,7 @@ class UniformFlowEnvironment(Environment):
|
|
|
265
254
|
|
|
266
255
|
#: A unit vector specifying the direction of the flow in 3D Cartesian coordinates.
|
|
267
256
|
#: Default is ``(1.0, 0, 0)``, which corresponds to flow in the x-direction.
|
|
268
|
-
fdv = CArray(dtype=float64, shape=(3,), value=array((1.0, 0, 0)), desc='flow direction')
|
|
257
|
+
fdv = CArray(dtype=np.float64, shape=(3,), value=np.array((1.0, 0, 0)), desc='flow direction')
|
|
269
258
|
|
|
270
259
|
#: A unique identifier based on the environment properties. (read-only)
|
|
271
260
|
digest = Property(
|
|
@@ -301,14 +290,14 @@ class UniformFlowEnvironment(Environment):
|
|
|
301
290
|
#
|
|
302
291
|
# - shape `(N,)` if ``mpos`` is a single point, or
|
|
303
292
|
# - shape `(N, M)` if ``mpos`` consists of multiple points.
|
|
304
|
-
if isscalar(mpos):
|
|
305
|
-
mpos = array((0, 0, 0), dtype=float32)[:, newaxis]
|
|
306
|
-
fdv = self.fdv / sqrt((self.fdv * self.fdv).sum())
|
|
307
|
-
mpos = mpos[:, newaxis, :]
|
|
308
|
-
rmv = gpos[:, :, newaxis] - mpos
|
|
309
|
-
rm = sqrt(sum(rmv * rmv, 0))
|
|
310
|
-
macostheta = (self.ma * sum(rmv.reshape((3, -1)) * fdv[:, newaxis], 0) / rm.reshape(-1)).reshape(rm.shape)
|
|
311
|
-
rm *= 1 / (-macostheta + sqrt(macostheta * macostheta - self.ma * self.ma + 1))
|
|
293
|
+
if np.isscalar(mpos):
|
|
294
|
+
mpos = np.array((0, 0, 0), dtype=np.float32)[:, np.newaxis]
|
|
295
|
+
fdv = self.fdv / np.sqrt((self.fdv * self.fdv).sum())
|
|
296
|
+
mpos = mpos[:, np.newaxis, :]
|
|
297
|
+
rmv = gpos[:, :, np.newaxis] - mpos
|
|
298
|
+
rm = np.sqrt(np.sum(rmv * rmv, 0))
|
|
299
|
+
macostheta = (self.ma * np.sum(rmv.reshape((3, -1)) * fdv[:, np.newaxis], 0) / rm.reshape(-1)).reshape(rm.shape)
|
|
300
|
+
rm *= 1 / (-macostheta + np.sqrt(macostheta * macostheta - self.ma * self.ma + 1))
|
|
312
301
|
if rm.shape[1] == 1:
|
|
313
302
|
rm = rm[:, 0]
|
|
314
303
|
return rm
|
|
@@ -358,14 +347,14 @@ class SlotJet(FlowField):
|
|
|
358
347
|
v0 = Float(0.0, desc='exit velocity')
|
|
359
348
|
|
|
360
349
|
#: The location of the slot nozzle center. Default is ``(0.0, 0.0, 0.0)``.
|
|
361
|
-
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
350
|
+
origin = CArray(dtype=np.float64, shape=(3,), value=np.array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
362
351
|
|
|
363
352
|
#: Unit vector representing the flow direction. Default is ``(1.0, 0.0, 0.0)``.
|
|
364
|
-
flow = CArray(dtype=float64, shape=(3,), value=array((1.0, 0.0, 0.0)), desc='flow direction')
|
|
353
|
+
flow = CArray(dtype=np.float64, shape=(3,), value=np.array((1.0, 0.0, 0.0)), desc='flow direction')
|
|
365
354
|
|
|
366
355
|
#: Unit vector parallel to the slot center plane, used to define the slot orientation.
|
|
367
356
|
#: Default is ``(0.0, 1.0, 0.0)``.
|
|
368
|
-
plane = CArray(dtype=float64, shape=(3,), value=array((0.0, 1.0, 0.0)), desc='slot center line direction')
|
|
357
|
+
plane = CArray(dtype=np.float64, shape=(3,), value=np.array((0.0, 1.0, 0.0)), desc='slot center line direction')
|
|
369
358
|
|
|
370
359
|
#: Width of the slot (slot diameter). Default is ``0.2``.
|
|
371
360
|
B = Float(0.2, desc='nozzle diameter')
|
|
@@ -415,18 +404,18 @@ class SlotJet(FlowField):
|
|
|
415
404
|
with respect to the spatial coordinates.
|
|
416
405
|
"""
|
|
417
406
|
# normalize
|
|
418
|
-
flow = self.flow / norm(self.flow)
|
|
419
|
-
plane = self.plane / norm(self.plane)
|
|
407
|
+
flow = self.flow / spla.norm(self.flow)
|
|
408
|
+
plane = self.plane / spla.norm(self.plane)
|
|
420
409
|
# additional axes of global coordinate system
|
|
421
|
-
yy = -cross(flow, plane)
|
|
422
|
-
zz = cross(flow, yy)
|
|
410
|
+
yy = -np.cross(flow, plane)
|
|
411
|
+
zz = np.cross(flow, yy)
|
|
423
412
|
# distance from slot exit plane
|
|
424
413
|
xx1 = xx - self.origin
|
|
425
414
|
# local coordinate system
|
|
426
|
-
x = dot(flow, xx1)
|
|
427
|
-
y = dot(yy, xx1)
|
|
415
|
+
x = np.dot(flow, xx1)
|
|
416
|
+
y = np.dot(yy, xx1)
|
|
428
417
|
x1 = 0.5668 / self.l * x # C1 in Albertson1950
|
|
429
|
-
h1 = abs(y) + sqrt(pi) * 0.5 * x1 - 0.5 * self.B
|
|
418
|
+
h1 = abs(y) + np.sqrt(np.pi) * 0.5 * x1 - 0.5 * self.B
|
|
430
419
|
if h1 < 0.0:
|
|
431
420
|
# core jet
|
|
432
421
|
Ux = self.v0
|
|
@@ -434,14 +423,14 @@ class SlotJet(FlowField):
|
|
|
434
423
|
Udy = 0
|
|
435
424
|
else:
|
|
436
425
|
# shear layer
|
|
437
|
-
Ux = self.v0 * exp(-h1 * h1 / (2 * x1 * x1))
|
|
438
|
-
Udx = (h1 * h1 / (x * x1 * x1) - sqrt(pi) * 0.5 * h1 / (x * x1)) * Ux
|
|
439
|
-
Udy = -sign(y) * h1 * Ux / (x1 * x1)
|
|
426
|
+
Ux = self.v0 * np.exp(-h1 * h1 / (2 * x1 * x1))
|
|
427
|
+
Udx = (h1 * h1 / (x * x1 * x1) - np.sqrt(np.pi) * 0.5 * h1 / (x * x1)) * Ux
|
|
428
|
+
Udy = -np.sign(y) * h1 * Ux / (x1 * x1)
|
|
440
429
|
# Jacobi matrix
|
|
441
|
-
dU = array(((Udx, 0, 0), (Udy, 0, 0), (0, 0, 0))).T
|
|
430
|
+
dU = np.array(((Udx, 0, 0), (Udy, 0, 0), (0, 0, 0))).T
|
|
442
431
|
# rotation matrix
|
|
443
|
-
R = array((flow, yy, zz)).T
|
|
444
|
-
return dot(R, array((Ux, 0, 0))), dot(dot(R, dU), R.T)
|
|
432
|
+
R = np.array((flow, yy, zz)).T
|
|
433
|
+
return np.dot(R, np.array((Ux, 0, 0))), np.dot(np.dot(R, dU), R.T)
|
|
445
434
|
|
|
446
435
|
|
|
447
436
|
class OpenJet(FlowField):
|
|
@@ -479,7 +468,7 @@ class OpenJet(FlowField):
|
|
|
479
468
|
v0 = Float(0.0, desc='exit velocity')
|
|
480
469
|
|
|
481
470
|
#: The location of the nozzle center. Default is ``(0.0, 0.0, 0.0)``.
|
|
482
|
-
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
471
|
+
origin = CArray(dtype=np.float64, shape=(3,), value=np.array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
483
472
|
|
|
484
473
|
#: Diameter of the nozzle. Default is ``0.2``.
|
|
485
474
|
D = Float(0.2, desc='nozzle diameter')
|
|
@@ -530,10 +519,10 @@ class OpenJet(FlowField):
|
|
|
530
519
|
to `y` and `z` are set to zero to avoid division by zero.
|
|
531
520
|
"""
|
|
532
521
|
x, y, z = xx - self.origin
|
|
533
|
-
r = sqrt(y * y + z * z)
|
|
522
|
+
r = np.sqrt(y * y + z * z)
|
|
534
523
|
x1 = 0.5022 / self.l * x # C2 in Albertson1950
|
|
535
524
|
h1 = r + x1 - 0.5 * self.D
|
|
536
|
-
U = self.v0 * exp(-h1 * h1 / (2 * x1 * x1))
|
|
525
|
+
U = self.v0 * np.exp(-h1 * h1 / (2 * x1 * x1))
|
|
537
526
|
if h1 < 0.0:
|
|
538
527
|
Udr = 0.0
|
|
539
528
|
U = self.v0
|
|
@@ -549,9 +538,9 @@ class OpenJet(FlowField):
|
|
|
549
538
|
Udx = 0
|
|
550
539
|
|
|
551
540
|
# flow field
|
|
552
|
-
v = array((U, 0.0, 0.0))
|
|
541
|
+
v = np.array((U, 0.0, 0.0))
|
|
553
542
|
# Jacobi matrix
|
|
554
|
-
dv = array(((Udx, 0.0, 0.0), (Udy, 0.0, 0.0), (Udz, 0.0, 0.0))).T
|
|
543
|
+
dv = np.array(((Udx, 0.0, 0.0), (Udy, 0.0, 0.0), (Udz, 0.0, 0.0))).T
|
|
555
544
|
return v, dv
|
|
556
545
|
|
|
557
546
|
|
|
@@ -568,7 +557,7 @@ class RotatingFlow(FlowField):
|
|
|
568
557
|
- The rotation is assumed to be about the z-axis. The velocity components in the x-y plane are
|
|
569
558
|
determined by the angular velocity :attr:`omega`, while the z-component is constant
|
|
570
559
|
and set by :attr:`v0`.
|
|
571
|
-
- The angular velocity :attr:`omega` is computed as: ``omega = 2 * pi * rps``,
|
|
560
|
+
- The angular velocity :attr:`omega` is computed as: ``omega = 2 * np.pi * rps``,
|
|
572
561
|
with the :attr:`rps` given in revolutions per second (i.e. Hz).
|
|
573
562
|
|
|
574
563
|
Examples
|
|
@@ -577,7 +566,7 @@ class RotatingFlow(FlowField):
|
|
|
577
566
|
>>> import numpy as np
|
|
578
567
|
>>>
|
|
579
568
|
>>> flow = RotatingFlow(rps=1, v0=1.0)
|
|
580
|
-
>>> velocity, jacobian = flow.v(array((1.0, 1.0, 0.0)))
|
|
569
|
+
>>> velocity, jacobian = flow.v(np.array((1.0, 1.0, 0.0)))
|
|
581
570
|
>>> velocity
|
|
582
571
|
array([-6.28318531, 6.28318531, 1. ])
|
|
583
572
|
>>> jacobian
|
|
@@ -619,7 +608,7 @@ class RotatingFlow(FlowField):
|
|
|
619
608
|
|
|
620
609
|
#: The location of the center of rotation.
|
|
621
610
|
#: Default is ``(0.0, 0.0, 0.0)``.
|
|
622
|
-
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of rotation')
|
|
611
|
+
origin = CArray(dtype=np.float64, shape=(3,), value=np.array((0.0, 0.0, 0.0)), desc='center of rotation')
|
|
623
612
|
|
|
624
613
|
#: A unique identifier based on the field properties. (read-only)
|
|
625
614
|
digest = Property(
|
|
@@ -634,7 +623,7 @@ class RotatingFlow(FlowField):
|
|
|
634
623
|
|
|
635
624
|
@cached_property
|
|
636
625
|
def _get_omega(self):
|
|
637
|
-
return 2 * pi * self.rps
|
|
626
|
+
return 2 * np.pi * self.rps
|
|
638
627
|
|
|
639
628
|
@cached_property
|
|
640
629
|
def _get_digest(self):
|
|
@@ -682,13 +671,13 @@ class RotatingFlow(FlowField):
|
|
|
682
671
|
W = self.v0
|
|
683
672
|
|
|
684
673
|
# flow field
|
|
685
|
-
v = array((U, V, W))
|
|
674
|
+
v = np.array((U, V, W))
|
|
686
675
|
# Jacobi matrix
|
|
687
|
-
dv = array(((0.0, omega, 0.0), (-omega, 0.0, 0.0), (0.0, 0.0, 0.0))).T
|
|
676
|
+
dv = np.array(((0.0, omega, 0.0), (-omega, 0.0, 0.0), (0.0, 0.0, 0.0))).T
|
|
688
677
|
return v, dv
|
|
689
678
|
|
|
690
679
|
|
|
691
|
-
def spiral_sphere(N, Om=None, b=None): # noqa: N803 # change to 4*pi
|
|
680
|
+
def spiral_sphere(N, Om=None, b=None): # noqa: N803 # change to 4*np.pi
|
|
692
681
|
"""
|
|
693
682
|
Generate unit vectors equally distributed over a sphere or a portion of it.
|
|
694
683
|
|
|
@@ -706,7 +695,7 @@ def spiral_sphere(N, Om=None, b=None): # noqa: N803 # change to 4*pi
|
|
|
706
695
|
The number of points to generate on the sphere.
|
|
707
696
|
|
|
708
697
|
Om : :class:`float`, optional
|
|
709
|
-
The solid angle in steradians to cover on the sphere. Default is ``2 * pi``,
|
|
698
|
+
The solid angle in steradians to cover on the sphere. Default is ``2 * np.pi``,
|
|
710
699
|
which corresponds to a hemisphere. Smaller values result in covering
|
|
711
700
|
a smaller portion of the hemisphere.
|
|
712
701
|
|
|
@@ -743,37 +732,37 @@ def spiral_sphere(N, Om=None, b=None): # noqa: N803 # change to 4*pi
|
|
|
743
732
|
Generate 50 points over half a hemisphere with the z-axis as the center direction:
|
|
744
733
|
|
|
745
734
|
>>> import numpy as np
|
|
746
|
-
>>> points = spiral_sphere(50, Om=np.pi, b=array((0, 0, 1)))
|
|
735
|
+
>>> points = spiral_sphere(50, Om=np.pi, b=np.array((0, 0, 1)))
|
|
747
736
|
>>> points.shape
|
|
748
737
|
(3, 50)
|
|
749
738
|
|
|
750
739
|
Generate 200 points with a different direction vector:
|
|
751
740
|
|
|
752
|
-
>>> points = spiral_sphere(200, b=array((1, 0, 0)))
|
|
741
|
+
>>> points = spiral_sphere(200, b=np.array((1, 0, 0)))
|
|
753
742
|
>>> points.shape
|
|
754
743
|
(3, 200)
|
|
755
744
|
"""
|
|
756
|
-
Om = 2 * pi if Om is None else Om
|
|
757
|
-
b = array((0, 0, 1)) if b is None else b
|
|
745
|
+
Om = 2 * np.pi if Om is None else Om
|
|
746
|
+
b = np.array((0, 0, 1)) if b is None else b
|
|
758
747
|
# first produce 'equally' distributed directions in spherical coords
|
|
759
|
-
o = 4 * pi / Om
|
|
760
|
-
h = -1 + 2 * arange(N) / (N * o - 1.0)
|
|
761
|
-
theta = arccos(h)
|
|
762
|
-
phi = zeros_like(theta)
|
|
748
|
+
o = 4 * np.pi / Om
|
|
749
|
+
h = -1 + 2 * np.arange(N) / (N * o - 1.0)
|
|
750
|
+
theta = np.arccos(h)
|
|
751
|
+
phi = np.zeros_like(theta)
|
|
763
752
|
for i, hk in enumerate(h[1:]):
|
|
764
|
-
phi[i + 1] = phi[i] + 3.6 / sqrt(N * o * (1 - hk * hk)) % (2 * pi)
|
|
753
|
+
phi[i + 1] = phi[i] + 3.6 / np.sqrt(N * o * (1 - hk * hk)) % (2 * np.pi)
|
|
765
754
|
# translate to cartesian coords
|
|
766
|
-
xyz = vstack((sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)))
|
|
755
|
+
xyz = np.vstack((np.sin(theta) * np.cos(phi), np.sin(theta) * np.sin(phi), np.cos(theta)))
|
|
767
756
|
# mirror everything on a plane so that b points into the center
|
|
768
757
|
a = xyz[:, 0]
|
|
769
|
-
b = b / norm(b)
|
|
770
|
-
ab = (a - b)[:, newaxis]
|
|
771
|
-
if norm(ab) < 1e-10:
|
|
758
|
+
b = b / spla.norm(b)
|
|
759
|
+
ab = (a - b)[:, np.newaxis]
|
|
760
|
+
if spla.norm(ab) < 1e-10:
|
|
772
761
|
return xyz
|
|
773
762
|
# this is the Householder matrix for mirroring
|
|
774
|
-
H = identity(3) - dot(ab, ab.T) / dot(ab.T, a)
|
|
763
|
+
H = np.identity(3) - np.dot(ab, ab.T) / np.dot(ab.T, a)
|
|
775
764
|
# actual mirroring
|
|
776
|
-
return dot(H, xyz)
|
|
765
|
+
return np.dot(H, xyz)
|
|
777
766
|
|
|
778
767
|
|
|
779
768
|
class GeneralFlowEnvironment(Environment):
|
|
@@ -816,7 +805,7 @@ class GeneralFlowEnvironment(Environment):
|
|
|
816
805
|
|
|
817
806
|
#: The maximum solid angle (in steradians) used in the ray-tracing algorithm.
|
|
818
807
|
#: Default is :obj:`numpy.pi`.
|
|
819
|
-
Om = Float(pi, desc='maximum solid angle')
|
|
808
|
+
Om = Float(np.pi, desc='maximum solid angle')
|
|
820
809
|
|
|
821
810
|
#: A unique identifier based on the environment properties. (read-only)
|
|
822
811
|
digest = Property(
|
|
@@ -834,10 +823,10 @@ class GeneralFlowEnvironment(Environment):
|
|
|
834
823
|
def _r(self, gpos, mpos=0.0):
|
|
835
824
|
c = self.c
|
|
836
825
|
|
|
837
|
-
if isscalar(mpos):
|
|
838
|
-
mpos = array((0, 0, 0), dtype=float32)[:, newaxis]
|
|
826
|
+
if np.isscalar(mpos):
|
|
827
|
+
mpos = np.array((0, 0, 0), dtype=np.float32)[:, np.newaxis]
|
|
839
828
|
|
|
840
|
-
gt = empty((gpos.shape[-1], mpos.shape[-1]))
|
|
829
|
+
gt = np.empty((gpos.shape[-1], mpos.shape[-1]))
|
|
841
830
|
for micnum, x0 in enumerate(mpos.T):
|
|
842
831
|
key = x0.tobytes() # make array hashable
|
|
843
832
|
try:
|
|
@@ -888,16 +877,16 @@ class GeneralFlowEnvironment(Environment):
|
|
|
888
877
|
x = y[0:3]
|
|
889
878
|
s = y[3:6]
|
|
890
879
|
vv, dv = v(x)
|
|
891
|
-
sa = sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2])
|
|
892
|
-
x = empty(6)
|
|
880
|
+
sa = np.sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2])
|
|
881
|
+
x = np.empty(6)
|
|
893
882
|
x[0:3] = c * s / sa - vv # time reversal
|
|
894
|
-
x[3:6] = dot(s, -dv.T) # time reversal
|
|
883
|
+
x[3:6] = np.dot(s, -dv.T) # time reversal
|
|
895
884
|
return x
|
|
896
885
|
|
|
897
886
|
# integration along a single ray
|
|
898
887
|
def fr(x0, n0, rmax, dt, v, xyz, t):
|
|
899
|
-
s0 = n0 / (c + dot(v(x0)[0], n0))
|
|
900
|
-
y0 = hstack((x0, s0))
|
|
888
|
+
s0 = n0 / (c + np.dot(v(x0)[0], n0))
|
|
889
|
+
y0 = np.hstack((x0, s0))
|
|
901
890
|
oo = ode(f1)
|
|
902
891
|
oo.set_f_params(v)
|
|
903
892
|
oo.set_integrator(
|
|
@@ -909,18 +898,18 @@ class GeneralFlowEnvironment(Environment):
|
|
|
909
898
|
while oo.successful():
|
|
910
899
|
xyz.append(oo.y[0:3])
|
|
911
900
|
t.append(oo.t)
|
|
912
|
-
if norm(oo.y[0:3] - x0) > rmax:
|
|
901
|
+
if spla.norm(oo.y[0:3] - x0) > rmax:
|
|
913
902
|
break
|
|
914
903
|
oo.integrate(oo.t + dt)
|
|
915
904
|
|
|
916
905
|
gs2 = roi.shape[-1]
|
|
917
906
|
vv = self.ff.v
|
|
918
|
-
NN = int(sqrt(self.N))
|
|
907
|
+
NN = int(np.sqrt(self.N))
|
|
919
908
|
xe = roi.mean(1) # center of grid
|
|
920
|
-
r = x0[:, newaxis] - roi
|
|
921
|
-
rmax = sqrt((r * r).sum(0).max()) # maximum distance
|
|
909
|
+
r = x0[:, np.newaxis] - roi
|
|
910
|
+
rmax = np.sqrt((r * r).sum(0).max()) # maximum distance
|
|
922
911
|
nv = spiral_sphere(self.N, self.Om, b=xe - x0)
|
|
923
|
-
rstep = rmax / sqrt(self.N)
|
|
912
|
+
rstep = rmax / np.sqrt(self.N)
|
|
924
913
|
rmax += rstep
|
|
925
914
|
tstep = rstep / c
|
|
926
915
|
xyz = []
|
|
@@ -930,13 +919,13 @@ class GeneralFlowEnvironment(Environment):
|
|
|
930
919
|
fr(x0, n0, rmax, tstep, vv, xyz, t)
|
|
931
920
|
if i and i % NN == 0:
|
|
932
921
|
if not lastind:
|
|
933
|
-
dd = ConvexHull(vstack((roi.T, xyz)), incremental=True)
|
|
922
|
+
dd = ConvexHull(np.vstack((roi.T, xyz)), incremental=True)
|
|
934
923
|
else:
|
|
935
924
|
dd.add_points(xyz[lastind:], restart=True)
|
|
936
925
|
lastind = len(xyz)
|
|
937
926
|
# ConvexHull includes grid if no grid points on hull
|
|
938
927
|
if dd.simplices.min() >= gs2:
|
|
939
928
|
break
|
|
940
|
-
xyz = array(xyz)
|
|
941
|
-
t = array(t)
|
|
929
|
+
xyz = np.array(xyz)
|
|
930
|
+
t = np.array(t)
|
|
942
931
|
return LinearNDInterpolator(xyz, t)
|