acoular 23.11__py3-none-any.whl → 24.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +118 -50
- acoular/calib.py +29 -38
- acoular/configuration.py +116 -73
- acoular/demo/__init__.py +10 -4
- acoular/demo/acoular_demo.py +78 -53
- acoular/environments.py +265 -262
- acoular/fastFuncs.py +361 -191
- acoular/fbeamform.py +1478 -1407
- acoular/grids.py +501 -545
- acoular/h5cache.py +50 -59
- acoular/h5files.py +154 -137
- acoular/internal.py +10 -11
- acoular/microphones.py +57 -53
- acoular/sdinput.py +47 -52
- acoular/signals.py +167 -179
- acoular/sources.py +818 -693
- acoular/spectra.py +349 -359
- acoular/tbeamform.py +414 -413
- acoular/tfastfuncs.py +178 -101
- acoular/tools/__init__.py +25 -0
- acoular/tools/aiaa.py +186 -0
- acoular/tools/helpers.py +189 -0
- acoular/tools/metrics.py +165 -0
- acoular/tprocess.py +1201 -1143
- acoular/traitsviews.py +513 -501
- acoular/trajectory.py +50 -52
- acoular/version.py +5 -6
- acoular/xml/minidsp_uma-16.xml +20 -0
- acoular/xml/{minidsp_uma16.xml → minidsp_uma-16_mirrored.xml} +3 -0
- {acoular-23.11.dist-info → acoular-24.5.dist-info}/METADATA +47 -40
- acoular-24.5.dist-info/RECORD +50 -0
- {acoular-23.11.dist-info → acoular-24.5.dist-info}/WHEEL +1 -1
- acoular-24.5.dist-info/licenses/LICENSE +28 -0
- acoular/fileimport.py +0 -380
- acoular/nidaqimport.py +0 -273
- acoular/tests/reference_data/BeamformerBase.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMF.npy +0 -0
- acoular/tests/reference_data/BeamformerCapon.npy +0 -0
- acoular/tests/reference_data/BeamformerClean.npy +0 -0
- acoular/tests/reference_data/BeamformerCleansc.npy +0 -0
- acoular/tests/reference_data/BeamformerCleant.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSq.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerDamas.npy +0 -0
- acoular/tests/reference_data/BeamformerDamasPlus.npy +0 -0
- acoular/tests/reference_data/BeamformerEig.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerFunctional.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerGridlessOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerMusic.npy +0 -0
- acoular/tests/reference_data/BeamformerOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerTime.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSq.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeTraj.npy +0 -0
- acoular/tests/reference_data/Environment.npy +0 -0
- acoular/tests/reference_data/Example1_numerical_values_testsum.h5 +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/GeneralFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/OpenJet.npy +0 -0
- acoular/tests/reference_data/PointSource.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_csm.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_ev.npy +0 -0
- acoular/tests/reference_data/RotatingFlow.npy +0 -0
- acoular/tests/reference_data/SlotJet.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/reference_data/UniformFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/beamformer_traj_time_data.h5 +0 -0
- acoular/tests/run_tests.sh +0 -18
- acoular/tests/run_tests_osx.sh +0 -16
- acoular/tests/test.npy +0 -0
- acoular/tests/test_beamformer_results.py +0 -204
- acoular/tests/test_classes.py +0 -60
- acoular/tests/test_digest.py +0 -125
- acoular/tests/test_environments.py +0 -73
- acoular/tests/test_example1.py +0 -124
- acoular/tests/test_grid.py +0 -92
- acoular/tests/test_integrate.py +0 -102
- acoular/tests/test_signals.py +0 -60
- acoular/tests/test_sources.py +0 -65
- acoular/tests/test_spectra.py +0 -38
- acoular/tests/test_timecache.py +0 -35
- acoular/tests/test_tprocess.py +0 -90
- acoular/tests/test_traj_beamformer_results.py +0 -164
- acoular/tests/unsupported/SpeedComparison/OvernightTestcasesBeamformer_nMics32_nGridPoints100_nFreqs4_nTrials10.png +0 -0
- acoular/tests/unsupported/SpeedComparison/cythonBeamformer.pyx +0 -237
- acoular/tests/unsupported/SpeedComparison/mainForCython.py +0 -103
- acoular/tests/unsupported/SpeedComparison/mainForParallelJit.py +0 -143
- acoular/tests/unsupported/SpeedComparison/setupCythonOpenMP.py +0 -63
- acoular/tests/unsupported/SpeedComparison/sharedFunctions.py +0 -153
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_AllImportantMethods.png +0 -0
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_faverage.png +0 -0
- acoular/tests/unsupported/SpeedComparison/vglOptimierungFAverage.py +0 -204
- acoular/tests/unsupported/SpeedComparison/vglOptimierungGaussSeidel.py +0 -182
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAMFULL_INVERSE.py +0 -764
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAM_OS.py +0 -231
- acoular/tests/unsupported/SpeedComparison/whatsFastestWayFor_absASquared.py +0 -48
- acoular/tests/unsupported/functionalBeamformer.py +0 -123
- acoular/tests/unsupported/precisionTest.py +0 -153
- acoular/tests/unsupported/validationOfBeamformerFuncsPOSTAcoularIntegration.py +0 -254
- acoular/tests/unsupported/validationOfBeamformerFuncsPREeAcoularIntegration.py +0 -531
- acoular/tools.py +0 -418
- acoular-23.11.dist-info/RECORD +0 -146
- acoular-23.11.dist-info/licenses/LICENSE +0 -29
- {acoular-23.11.dist-info → acoular-24.5.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/environments.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
#pylint: disable-msg=E0611, E1103, C0103, R0901, R0902, R0903, R0904, W0232
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
6
4
|
"""Implements acoustic environments with and without flow.
|
|
7
5
|
|
|
8
6
|
.. autosummary::
|
|
@@ -17,56 +15,81 @@
|
|
|
17
15
|
SlotJet
|
|
18
16
|
|
|
19
17
|
"""
|
|
18
|
+
|
|
20
19
|
import numba as nb
|
|
21
|
-
from numpy import
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
from numpy import (
|
|
21
|
+
arange,
|
|
22
|
+
arccos,
|
|
23
|
+
arctan2,
|
|
24
|
+
array,
|
|
25
|
+
ascontiguousarray,
|
|
26
|
+
cos,
|
|
27
|
+
cross,
|
|
28
|
+
dot,
|
|
29
|
+
empty,
|
|
30
|
+
exp,
|
|
31
|
+
float32,
|
|
32
|
+
float64,
|
|
33
|
+
hstack,
|
|
34
|
+
identity,
|
|
35
|
+
isscalar,
|
|
36
|
+
matmul,
|
|
37
|
+
newaxis,
|
|
38
|
+
pi,
|
|
39
|
+
sign,
|
|
40
|
+
sin,
|
|
41
|
+
sqrt,
|
|
42
|
+
sum,
|
|
43
|
+
vstack,
|
|
44
|
+
zeros_like,
|
|
45
|
+
)
|
|
24
46
|
from numpy.linalg.linalg import norm
|
|
25
47
|
from scipy.integrate import ode
|
|
26
48
|
from scipy.interpolate import LinearNDInterpolator
|
|
27
49
|
from scipy.spatial import ConvexHull
|
|
28
|
-
from traits.api import
|
|
29
|
-
CArray, cached_property, Trait, Dict
|
|
50
|
+
from traits.api import CArray, Dict, Float, HasPrivateTraits, Int, Property, Trait, cached_property
|
|
30
51
|
|
|
31
52
|
from .internal import digest
|
|
32
53
|
|
|
33
|
-
f64ro = nb.types.Array(nb.types.float64,2,'C',readonly=True)
|
|
34
|
-
f32ro = nb.types.Array(nb.types.float32,2,'C',readonly=True)
|
|
54
|
+
f64ro = nb.types.Array(nb.types.float64, 2, 'C', readonly=True)
|
|
55
|
+
f32ro = nb.types.Array(nb.types.float32, 2, 'C', readonly=True)
|
|
56
|
+
|
|
35
57
|
|
|
36
|
-
@nb.njit([(f64ro, f64ro), (f64ro, f32ro), (f32ro, f64ro),(f32ro, f32ro)],
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"""computes distance matrix, accelerated with numba
|
|
58
|
+
@nb.njit([(f64ro, f64ro), (f64ro, f32ro), (f32ro, f64ro), (f32ro, f32ro)], cache=True, fastmath=True)
|
|
59
|
+
def dist_mat(gpos, mpos):
|
|
60
|
+
"""Computes distance matrix, accelerated with numba.
|
|
40
61
|
|
|
41
62
|
Args:
|
|
63
|
+
----
|
|
42
64
|
gpos (3,N)
|
|
43
65
|
mpos (3,M)
|
|
44
66
|
|
|
45
|
-
Returns
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
46
69
|
(N,M) distance matrix
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
_,
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
_, M = mpos.shape
|
|
73
|
+
_, N = gpos.shape
|
|
74
|
+
rm = empty((N, M), dtype=gpos.dtype)
|
|
75
|
+
TWO = rm.dtype.type(2.0) # make sure to have a float32 or float 64 literal
|
|
52
76
|
m0 = mpos[0]
|
|
53
77
|
m1 = mpos[1]
|
|
54
78
|
m2 = mpos[2]
|
|
55
79
|
for n in range(N):
|
|
56
|
-
g0 = gpos[0,n]
|
|
57
|
-
g1 = gpos[1,n]
|
|
58
|
-
g2 = gpos[2,n]
|
|
80
|
+
g0 = gpos[0, n]
|
|
81
|
+
g1 = gpos[1, n]
|
|
82
|
+
g2 = gpos[2, n]
|
|
59
83
|
for m in range(M):
|
|
60
|
-
rm[n,m] = sqrt((g0 - m0[m])**TWO + (g1 - m1[m])**TWO + (g2 - m2[m])**TWO)
|
|
84
|
+
rm[n, m] = sqrt((g0 - m0[m]) ** TWO + (g1 - m1[m]) ** TWO + (g2 - m2[m]) ** TWO)
|
|
61
85
|
return rm
|
|
62
86
|
|
|
63
87
|
|
|
64
|
-
def cartToCyl(x, Q=
|
|
65
|
-
"""
|
|
66
|
-
Returns the cylindrical coordinate representation of a input position
|
|
88
|
+
def cartToCyl(x, Q=None):
|
|
89
|
+
"""Returns the cylindrical coordinate representation of a input position
|
|
67
90
|
which was before transformed into a modified cartesian coordinate, which
|
|
68
91
|
has flow into positive z direction.
|
|
69
|
-
|
|
92
|
+
|
|
70
93
|
Parameters
|
|
71
94
|
----------
|
|
72
95
|
x : float[3, nPoints]
|
|
@@ -75,80 +98,78 @@ def cartToCyl(x, Q=identity(3)):
|
|
|
75
98
|
Orthogonal transformation matrix. If provided, the pos vectors are
|
|
76
99
|
transformed via posiMod = Q * x, before transforming those modified
|
|
77
100
|
coordinates into cylindrical ones. Default is identity matrix.
|
|
78
|
-
|
|
101
|
+
|
|
79
102
|
Returns
|
|
80
103
|
-------
|
|
81
104
|
cylCoord : [3, nPoints]
|
|
82
105
|
cylindrical representation of those n points with (phi, r, z)
|
|
106
|
+
|
|
83
107
|
"""
|
|
84
|
-
|
|
108
|
+
Q = identity(3) if Q is None else Q
|
|
109
|
+
if not (Q == identity(3)).all(): # noqa: SIM300
|
|
85
110
|
x = matmul(Q, x) # modified position vector
|
|
86
|
-
|
|
87
|
-
return cylCoord
|
|
111
|
+
return array([arctan2(x[1], x[0]), sqrt(x[0] ** 2 + x[1] ** 2), x[2]])
|
|
88
112
|
|
|
89
113
|
|
|
90
|
-
def cylToCart(x, Q=
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
cylindrical representation of those n points with (phi, r, z)
|
|
100
|
-
cartesian coordinates of n points
|
|
101
|
-
|
|
102
|
-
Q : float[3,3]
|
|
103
|
-
Orthogonal transformation matrix. If provided, the pos vectors are
|
|
104
|
-
transformed via posiMod = Q * x, before transforming those modified
|
|
105
|
-
coordinates into cylindrical ones. Default is identity matrix.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Returns
|
|
109
|
-
-------
|
|
110
|
-
CartCoord : [3, nPoints]
|
|
114
|
+
def cylToCart(x, Q=None):
|
|
115
|
+
"""Returns the cartesian coordinate representation of a input position
|
|
116
|
+
which was before transformed into a cylindrical coordinate, which
|
|
117
|
+
has flow into positive z direction.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
x : float[3, nPoints]
|
|
122
|
+
cylindrical representation of those n points with (phi, r, z)
|
|
111
123
|
cartesian coordinates of n points
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return CartCoord
|
|
124
|
+
|
|
125
|
+
Q : float[3,3]
|
|
126
|
+
Orthogonal transformation matrix. If provided, the pos vectors are
|
|
127
|
+
transformed via posiMod = Q * x, before transforming those modified
|
|
128
|
+
coordinates into cylindrical ones. Default is identity matrix.
|
|
118
129
|
|
|
119
130
|
|
|
120
|
-
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
CartCoord : [3, nPoints]
|
|
134
|
+
cartesian coordinates of n points
|
|
135
|
+
|
|
121
136
|
"""
|
|
122
|
-
|
|
137
|
+
Q = identity(3) if Q is None else Q
|
|
138
|
+
if not (Q == identity(3)).all(): # noqa: SIM300
|
|
139
|
+
x = matmul(Q, x) # modified position vector
|
|
140
|
+
return array([x[1] * sin(x[0]), x[1] * cos(x[0]), x[2]])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class Environment(HasPrivateTraits):
|
|
144
|
+
"""A simple acoustic environment without flow.
|
|
123
145
|
|
|
124
146
|
This class provides the facilities to calculate the travel time (distances)
|
|
125
147
|
between grid point locations and microphone locations.
|
|
126
148
|
"""
|
|
149
|
+
|
|
127
150
|
# internal identifier
|
|
128
151
|
digest = Property(
|
|
129
|
-
depends_on=['c'],
|
|
130
|
-
|
|
152
|
+
depends_on=['c'],
|
|
153
|
+
)
|
|
131
154
|
|
|
132
155
|
#: The speed of sound, defaults to 343 m/s
|
|
133
|
-
c = Float(343
|
|
134
|
-
desc="speed of sound")
|
|
156
|
+
c = Float(343.0, desc='speed of sound')
|
|
135
157
|
|
|
136
158
|
#: The region of interest (ROI), not needed for most types of environment
|
|
137
|
-
roi = Trait(None,(None,CArray))
|
|
159
|
+
roi = Trait(None, (None, CArray))
|
|
138
160
|
|
|
139
|
-
def _get_digest(
|
|
140
|
-
return digest(
|
|
161
|
+
def _get_digest(self):
|
|
162
|
+
return digest(self)
|
|
141
163
|
|
|
142
|
-
def _r(
|
|
143
|
-
"""
|
|
144
|
-
Calculates distances between grid point locations and microphone
|
|
164
|
+
def _r(self, gpos, mpos=0.0):
|
|
165
|
+
"""Calculates distances between grid point locations and microphone
|
|
145
166
|
locations or the origin. Functionality may change in the future.
|
|
146
167
|
|
|
147
168
|
Parameters
|
|
148
169
|
----------
|
|
149
170
|
gpos : array of floats of shape (3, N)
|
|
150
171
|
The locations of points in the beamforming map grid in 3D cartesian
|
|
151
|
-
co-ordinates.
|
|
172
|
+
co-ordinates.
|
|
152
173
|
mpos : array of floats of shape (3, M), optional
|
|
153
174
|
The locations of microphones in 3D cartesian co-ordinates. If not
|
|
154
175
|
given, then only one microphone at the origin (0, 0, 0) is
|
|
@@ -157,48 +178,47 @@ class Environment( HasPrivateTraits ):
|
|
|
157
178
|
Returns
|
|
158
179
|
-------
|
|
159
180
|
r : array of floats
|
|
160
|
-
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
181
|
+
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
161
182
|
then only a one-dimensional array is returned.
|
|
183
|
+
|
|
162
184
|
"""
|
|
163
185
|
if isscalar(mpos):
|
|
164
|
-
mpos = array((0, 0, 0), dtype
|
|
165
|
-
rm = dist_mat(ascontiguousarray(gpos),ascontiguousarray(mpos))
|
|
166
|
-
# mpos = mpos[:, newaxis, :]
|
|
167
|
-
# rmv = gpos[:, :, newaxis]-mpos
|
|
168
|
-
# rm = sum(rmv*rmv, 0)**0.5
|
|
186
|
+
mpos = array((0, 0, 0), dtype=float64)[:, newaxis]
|
|
187
|
+
rm = dist_mat(ascontiguousarray(gpos), ascontiguousarray(mpos))
|
|
188
|
+
# mpos = mpos[:, newaxis, :]
|
|
189
|
+
# rmv = gpos[:, :, newaxis]-mpos
|
|
190
|
+
# rm = sum(rmv*rmv, 0)**0.5
|
|
169
191
|
if rm.shape[1] == 1:
|
|
170
192
|
rm = rm[:, 0]
|
|
171
193
|
return rm
|
|
172
194
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
An acoustic environment with uniform flow.
|
|
195
|
+
|
|
196
|
+
class UniformFlowEnvironment(Environment):
|
|
197
|
+
"""An acoustic environment with uniform flow.
|
|
176
198
|
|
|
177
199
|
This class provides the facilities to calculate the travel time (distances)
|
|
178
200
|
between grid point locations and microphone locations in a uniform flow
|
|
179
201
|
field.
|
|
180
202
|
"""
|
|
203
|
+
|
|
181
204
|
#: The Mach number, defaults to 0.
|
|
182
|
-
ma = Float(0.0,
|
|
183
|
-
desc="flow mach number")
|
|
205
|
+
ma = Float(0.0, desc='flow mach number')
|
|
184
206
|
|
|
185
207
|
#: The unit vector that gives the direction of the flow, defaults to
|
|
186
208
|
#: flow in x-direction.
|
|
187
|
-
fdv = CArray(
|
|
188
|
-
desc="flow direction")
|
|
209
|
+
fdv = CArray(dtype=float64, shape=(3,), value=array((1.0, 0, 0)), desc='flow direction')
|
|
189
210
|
|
|
190
211
|
# internal identifier
|
|
191
212
|
digest = Property(
|
|
192
|
-
depends_on=['c', 'ma', 'fdv'],
|
|
193
|
-
|
|
213
|
+
depends_on=['c', 'ma', 'fdv'],
|
|
214
|
+
)
|
|
194
215
|
|
|
195
216
|
@cached_property
|
|
196
|
-
def _get_digest(
|
|
197
|
-
return digest(
|
|
217
|
+
def _get_digest(self):
|
|
218
|
+
return digest(self)
|
|
198
219
|
|
|
199
|
-
def _r(
|
|
200
|
-
"""
|
|
201
|
-
Calculates the virtual distances between grid point locations and
|
|
220
|
+
def _r(self, gpos, mpos=0.0):
|
|
221
|
+
"""Calculates the virtual distances between grid point locations and
|
|
202
222
|
microphone locations or the origin. These virtual distances correspond
|
|
203
223
|
to travel times of the sound. Functionality may change in the future.
|
|
204
224
|
|
|
@@ -215,35 +235,33 @@ class UniformFlowEnvironment( Environment):
|
|
|
215
235
|
Returns
|
|
216
236
|
-------
|
|
217
237
|
array of floats
|
|
218
|
-
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
238
|
+
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
219
239
|
then only a one-dimensional array is returned.
|
|
240
|
+
|
|
220
241
|
"""
|
|
221
242
|
if isscalar(mpos):
|
|
222
|
-
mpos = array((0, 0, 0), dtype
|
|
223
|
-
fdv = self.fdv/sqrt((self.fdv*self.fdv).sum())
|
|
243
|
+
mpos = array((0, 0, 0), dtype=float32)[:, newaxis]
|
|
244
|
+
fdv = self.fdv / sqrt((self.fdv * self.fdv).sum())
|
|
224
245
|
mpos = mpos[:, newaxis, :]
|
|
225
|
-
rmv = gpos[:, :, newaxis]-mpos
|
|
226
|
-
rm = sqrt(sum(rmv*rmv, 0))
|
|
227
|
-
macostheta = (self.ma*sum(rmv.reshape((3, -1))*fdv[:, newaxis], 0)
|
|
228
|
-
|
|
229
|
-
rm *= 1/(-macostheta + sqrt(macostheta*macostheta-self.ma*self.ma+1))
|
|
246
|
+
rmv = gpos[:, :, newaxis] - mpos
|
|
247
|
+
rm = sqrt(sum(rmv * rmv, 0))
|
|
248
|
+
macostheta = (self.ma * sum(rmv.reshape((3, -1)) * fdv[:, newaxis], 0) / rm.reshape(-1)).reshape(rm.shape)
|
|
249
|
+
rm *= 1 / (-macostheta + sqrt(macostheta * macostheta - self.ma * self.ma + 1))
|
|
230
250
|
if rm.shape[1] == 1:
|
|
231
251
|
rm = rm[:, 0]
|
|
232
252
|
return rm
|
|
233
|
-
|
|
234
253
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
An abstract base class for a spatial flow field.
|
|
238
|
-
|
|
254
|
+
|
|
255
|
+
class FlowField(HasPrivateTraits):
|
|
256
|
+
"""An abstract base class for a spatial flow field."""
|
|
257
|
+
|
|
239
258
|
digest = Property
|
|
240
259
|
|
|
241
|
-
def _get_digest(
|
|
260
|
+
def _get_digest(self):
|
|
242
261
|
return ''
|
|
243
262
|
|
|
244
|
-
def v(
|
|
245
|
-
"""
|
|
246
|
-
Provides the flow field as a function of the location. This is
|
|
263
|
+
def v(self, xx): # noqa: ARG002
|
|
264
|
+
"""Provides the flow field as a function of the location. This is
|
|
247
265
|
implemented here for the possibly most simple case: a quiescent fluid.
|
|
248
266
|
|
|
249
267
|
Parameters
|
|
@@ -257,49 +275,45 @@ class FlowField( HasPrivateTraits ):
|
|
|
257
275
|
The first element in the tuple is the velocity vector and the
|
|
258
276
|
second is the Jacobian of the velocity vector field, both at the
|
|
259
277
|
given location.
|
|
278
|
+
|
|
260
279
|
"""
|
|
261
|
-
v = array((0
|
|
262
|
-
dv = array(((0
|
|
280
|
+
v = array((0.0, 0.0, 0.0))
|
|
281
|
+
dv = array(((0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0)))
|
|
263
282
|
return -v, -dv
|
|
264
283
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
Provides an analytical approximation of the flow field of a slot jet,
|
|
284
|
+
|
|
285
|
+
class SlotJet(FlowField):
|
|
286
|
+
"""Provides an analytical approximation of the flow field of a slot jet,
|
|
268
287
|
see :ref:`Albertson et al., 1950<Albertson1950>`.
|
|
269
288
|
"""
|
|
289
|
+
|
|
270
290
|
#: Exit velocity at jet origin, i.e. the nozzle. Defaults to 0.
|
|
271
|
-
v0 = Float(0.0,
|
|
272
|
-
desc="exit velocity")
|
|
291
|
+
v0 = Float(0.0, desc='exit velocity')
|
|
273
292
|
|
|
274
|
-
#: Location of a point at the slot center line,
|
|
293
|
+
#: Location of a point at the slot center line,
|
|
275
294
|
#: defaults to the co-ordinate origin.
|
|
276
|
-
origin = CArray(
|
|
277
|
-
desc="center of nozzle")
|
|
295
|
+
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
278
296
|
|
|
279
297
|
#: Unit flow direction of the slot jet, defaults to (1,0,0).
|
|
280
|
-
flow = CArray(
|
|
281
|
-
desc="flow direction")
|
|
298
|
+
flow = CArray(dtype=float64, shape=(3,), value=array((1.0, 0.0, 0.0)), desc='flow direction')
|
|
282
299
|
|
|
283
300
|
#: Unit vector parallel to slot center plane, defaults to (0,1,0).
|
|
284
|
-
plane = CArray(
|
|
285
|
-
|
|
286
|
-
|
|
301
|
+
plane = CArray(dtype=float64, shape=(3,), value=array((0.0, 1.0, 0.0)), desc='slot center line direction')
|
|
302
|
+
|
|
287
303
|
#: Width of the slot, defaults to 0.2 .
|
|
288
|
-
B = Float(0.2,
|
|
289
|
-
desc="nozzle diameter")
|
|
304
|
+
B = Float(0.2, desc='nozzle diameter')
|
|
290
305
|
|
|
291
306
|
# internal identifier
|
|
292
307
|
digest = Property(
|
|
293
|
-
depends_on=['v0', 'origin', 'flow', 'plane', 'B'],
|
|
294
|
-
|
|
308
|
+
depends_on=['v0', 'origin', 'flow', 'plane', 'B'],
|
|
309
|
+
)
|
|
295
310
|
|
|
296
311
|
@cached_property
|
|
297
|
-
def _get_digest(
|
|
298
|
-
return digest(
|
|
312
|
+
def _get_digest(self):
|
|
313
|
+
return digest(self)
|
|
299
314
|
|
|
300
|
-
def v(
|
|
301
|
-
"""
|
|
302
|
-
Provides the flow field as a function of the location. This is
|
|
315
|
+
def v(self, xx):
|
|
316
|
+
"""Provides the flow field as a function of the location. This is
|
|
303
317
|
implemented here only for the component in the direction of :attr:`flow`;
|
|
304
318
|
entrainment components are set to zero.
|
|
305
319
|
|
|
@@ -314,21 +328,22 @@ class SlotJet( FlowField ):
|
|
|
314
328
|
The first element in the tuple is the velocity vector and the
|
|
315
329
|
second is the Jacobian of the velocity vector field, both at the
|
|
316
330
|
given location.
|
|
331
|
+
|
|
317
332
|
"""
|
|
318
333
|
# TODO: better to make sure that self.flow and self.plane are indeed unit vectors before
|
|
319
334
|
# normalize
|
|
320
|
-
flow = self.flow/norm(self.flow)
|
|
321
|
-
plane = self.plane/norm(self.plane)
|
|
335
|
+
flow = self.flow / norm(self.flow)
|
|
336
|
+
plane = self.plane / norm(self.plane)
|
|
322
337
|
# additional axes of global co-ordinate system
|
|
323
|
-
yy = -cross(flow,plane)
|
|
324
|
-
zz = cross(flow,yy)
|
|
338
|
+
yy = -cross(flow, plane)
|
|
339
|
+
zz = cross(flow, yy)
|
|
325
340
|
# distance from slot exit plane
|
|
326
|
-
xx1 = xx-self.origin
|
|
327
|
-
# local co-ordinate system
|
|
328
|
-
x = dot(flow,xx1)
|
|
329
|
-
y = dot(yy,xx1)
|
|
330
|
-
x1 = 0.109*x
|
|
331
|
-
h1 = abs(y)+sqrt(pi)*0.5*x1-0.5*self.B
|
|
341
|
+
xx1 = xx - self.origin
|
|
342
|
+
# local co-ordinate system
|
|
343
|
+
x = dot(flow, xx1)
|
|
344
|
+
y = dot(yy, xx1)
|
|
345
|
+
x1 = 0.109 * x
|
|
346
|
+
h1 = abs(y) + sqrt(pi) * 0.5 * x1 - 0.5 * self.B
|
|
332
347
|
if h1 < 0.0:
|
|
333
348
|
# core jet
|
|
334
349
|
Ux = self.v0
|
|
@@ -336,18 +351,18 @@ class SlotJet( FlowField ):
|
|
|
336
351
|
Udy = 0
|
|
337
352
|
else:
|
|
338
353
|
# shear layer
|
|
339
|
-
Ux = self.v0*exp(-h1*h1/(2*x1*x1))
|
|
340
|
-
Udx = (h1*h1/(x*x1*x1)-sqrt(pi)*0.5*h1/(x*x1))*Ux
|
|
341
|
-
Udy = -sign(y)*h1*Ux/(x1*x1)
|
|
354
|
+
Ux = self.v0 * exp(-h1 * h1 / (2 * x1 * x1))
|
|
355
|
+
Udx = (h1 * h1 / (x * x1 * x1) - sqrt(pi) * 0.5 * h1 / (x * x1)) * Ux
|
|
356
|
+
Udy = -sign(y) * h1 * Ux / (x1 * x1)
|
|
342
357
|
# Jacobi matrix
|
|
343
|
-
dU = array(((Udx,0,0),(Udy,0,0),(0,0,0))).T
|
|
358
|
+
dU = array(((Udx, 0, 0), (Udy, 0, 0), (0, 0, 0))).T
|
|
344
359
|
# rotation matrix
|
|
345
|
-
R = array((flow,yy,zz)).T
|
|
346
|
-
return dot(R,array((Ux,0,0))), dot(dot(R,dU),R.T)
|
|
360
|
+
R = array((flow, yy, zz)).T
|
|
361
|
+
return dot(R, array((Ux, 0, 0))), dot(dot(R, dU), R.T)
|
|
347
362
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Provides an analytical approximation of the flow field of an open jet,
|
|
363
|
+
|
|
364
|
+
class OpenJet(FlowField):
|
|
365
|
+
"""Provides an analytical approximation of the flow field of an open jet,
|
|
351
366
|
see :ref:`Albertson et al., 1950<Albertson1950>`.
|
|
352
367
|
|
|
353
368
|
Notes
|
|
@@ -355,31 +370,29 @@ class OpenJet( FlowField ):
|
|
|
355
370
|
This is not a fully generic implementation and is limited to flow in the
|
|
356
371
|
x-direction only. No other directions are possible at the moment and flow
|
|
357
372
|
components in the other direction are zero.
|
|
373
|
+
|
|
358
374
|
"""
|
|
375
|
+
|
|
359
376
|
#: Exit velocity at jet origin, i.e. the nozzle. Defaults to 0.
|
|
360
|
-
v0 = Float(0.0,
|
|
361
|
-
desc="exit velocity")
|
|
377
|
+
v0 = Float(0.0, desc='exit velocity')
|
|
362
378
|
|
|
363
379
|
#: Location of the nozzle center, defaults to the co-ordinate origin.
|
|
364
|
-
origin = CArray(
|
|
365
|
-
desc="center of nozzle")
|
|
380
|
+
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
366
381
|
|
|
367
382
|
#: Diameter of the nozzle, defaults to 0.2 .
|
|
368
|
-
D = Float(0.2,
|
|
369
|
-
desc="nozzle diameter")
|
|
383
|
+
D = Float(0.2, desc='nozzle diameter')
|
|
370
384
|
|
|
371
385
|
# internal identifier
|
|
372
386
|
digest = Property(
|
|
373
|
-
depends_on=['v0', 'origin', 'D'],
|
|
374
|
-
|
|
387
|
+
depends_on=['v0', 'origin', 'D'],
|
|
388
|
+
)
|
|
375
389
|
|
|
376
390
|
@cached_property
|
|
377
|
-
def _get_digest(
|
|
378
|
-
return digest(
|
|
391
|
+
def _get_digest(self):
|
|
392
|
+
return digest(self)
|
|
379
393
|
|
|
380
|
-
def v(
|
|
381
|
-
"""
|
|
382
|
-
Provides the flow field as a function of the location. This is
|
|
394
|
+
def v(self, xx):
|
|
395
|
+
"""Provides the flow field as a function of the location. This is
|
|
383
396
|
implemented here only for a jet in `x`-direction and the `y`- and
|
|
384
397
|
`z`-components are set to zero.
|
|
385
398
|
|
|
@@ -394,73 +407,65 @@ class OpenJet( FlowField ):
|
|
|
394
407
|
The first element in the tuple is the velocity vector and the
|
|
395
408
|
second is the Jacobian of the velocity vector field, both at the
|
|
396
409
|
given location.
|
|
410
|
+
|
|
397
411
|
"""
|
|
398
|
-
x, y, z = xx-self.origin
|
|
399
|
-
r = sqrt(y*y+z*z)
|
|
400
|
-
x1 = 0.081*x
|
|
401
|
-
h1 = r+x1-0.5*self.D
|
|
402
|
-
U = self.v0*exp(-h1*h1/(2*x1*x1))
|
|
412
|
+
x, y, z = xx - self.origin
|
|
413
|
+
r = sqrt(y * y + z * z)
|
|
414
|
+
x1 = 0.081 * x
|
|
415
|
+
h1 = r + x1 - 0.5 * self.D
|
|
416
|
+
U = self.v0 * exp(-h1 * h1 / (2 * x1 * x1))
|
|
403
417
|
if h1 < 0.0:
|
|
404
418
|
Udr = 0.0
|
|
405
419
|
U = self.v0
|
|
406
420
|
else:
|
|
407
|
-
Udr = -h1*U/(x1*x1)
|
|
421
|
+
Udr = -h1 * U / (x1 * x1)
|
|
408
422
|
if r > 0.0:
|
|
409
|
-
Udy = y*Udr/r
|
|
410
|
-
Udz = z*Udr/r
|
|
423
|
+
Udy = y * Udr / r
|
|
424
|
+
Udz = z * Udr / r
|
|
411
425
|
else:
|
|
412
426
|
Udy = Udz = 0.0
|
|
413
|
-
Udx = (h1*h1/(x*x1*x1)-h1/(x*x1))*U
|
|
427
|
+
Udx = (h1 * h1 / (x * x1 * x1) - h1 / (x * x1)) * U
|
|
414
428
|
if h1 < 0.0:
|
|
415
429
|
Udx = 0
|
|
416
430
|
|
|
417
431
|
# flow field
|
|
418
|
-
v = array(
|
|
432
|
+
v = array((U, 0.0, 0.0))
|
|
419
433
|
# Jacobi matrix
|
|
420
|
-
dv = array(
|
|
434
|
+
dv = array(((Udx, 0.0, 0.0), (Udy, 0.0, 0.0), (Udz, 0.0, 0.0))).T
|
|
421
435
|
return v, dv
|
|
422
436
|
|
|
423
437
|
|
|
438
|
+
class RotatingFlow(FlowField):
|
|
439
|
+
"""Provides an analytical approximation of the flow field of a rotating fluid with constant flow."""
|
|
424
440
|
|
|
425
|
-
|
|
426
|
-
class RotatingFlow( FlowField ):
|
|
427
|
-
"""
|
|
428
|
-
Provides an analytical approximation of the flow field of a rotating fluid with constant flow.
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
"""
|
|
432
441
|
#: Exit velocity at jet origin, i.e. the nozzle. Defaults to 0.
|
|
433
|
-
rpm = Float(0.0,
|
|
434
|
-
desc="revolutions per minute of the virtual array; negative values for clockwise rotation")
|
|
442
|
+
rpm = Float(0.0, desc='revolutions per minute of the virtual array; negative values for clockwise rotation')
|
|
435
443
|
|
|
436
|
-
v0 = Float(0.0,
|
|
437
|
-
desc="flow velocity")
|
|
444
|
+
v0 = Float(0.0, desc='flow velocity')
|
|
438
445
|
|
|
439
446
|
#: Location of the nozzle center, defaults to the co-ordinate origin.
|
|
440
|
-
origin = CArray(
|
|
441
|
-
desc="center of nozzle")
|
|
447
|
+
origin = CArray(dtype=float64, shape=(3,), value=array((0.0, 0.0, 0.0)), desc='center of nozzle')
|
|
442
448
|
|
|
443
449
|
# internal identifier
|
|
444
450
|
digest = Property(
|
|
445
|
-
depends_on=['v0', 'origin', 'rpm'],
|
|
446
|
-
|
|
451
|
+
depends_on=['v0', 'origin', 'rpm'],
|
|
452
|
+
)
|
|
447
453
|
|
|
448
454
|
# internal identifier
|
|
449
455
|
omega = Property(
|
|
450
|
-
depends_on=['rpm'],
|
|
451
|
-
|
|
456
|
+
depends_on=['rpm'],
|
|
457
|
+
)
|
|
452
458
|
|
|
453
459
|
@cached_property
|
|
454
460
|
def _get_omega(self):
|
|
455
461
|
return 2 * pi * self.rpm / 60
|
|
456
462
|
|
|
457
463
|
@cached_property
|
|
458
|
-
def _get_digest(
|
|
459
|
-
return digest(
|
|
464
|
+
def _get_digest(self):
|
|
465
|
+
return digest(self)
|
|
460
466
|
|
|
461
|
-
def v(
|
|
462
|
-
"""
|
|
463
|
-
Provides the rotating flow field around the z-Axis as a function of the location.
|
|
467
|
+
def v(self, xx):
|
|
468
|
+
"""Provides the rotating flow field around the z-Axis as a function of the location.
|
|
464
469
|
|
|
465
470
|
Parameters
|
|
466
471
|
----------
|
|
@@ -473,55 +478,55 @@ class RotatingFlow( FlowField ):
|
|
|
473
478
|
The first element in the tuple is the velocity vector and the
|
|
474
479
|
second is the Jacobian of the velocity vector field, both at the
|
|
475
480
|
given location.
|
|
481
|
+
|
|
476
482
|
"""
|
|
477
|
-
x, y, z = xx-self.origin
|
|
483
|
+
x, y, z = xx - self.origin
|
|
478
484
|
|
|
479
485
|
# rotational speed
|
|
480
486
|
omega = self.omega
|
|
481
487
|
|
|
482
|
-
#velocity vector
|
|
488
|
+
# velocity vector
|
|
483
489
|
U = omega * y
|
|
484
490
|
V = -omega * x
|
|
485
|
-
W = self.v0
|
|
486
|
-
|
|
491
|
+
W = self.v0
|
|
492
|
+
|
|
487
493
|
# flow field
|
|
488
|
-
v = array(
|
|
494
|
+
v = array((U, V, W))
|
|
489
495
|
# Jacobi matrix
|
|
490
|
-
dv = array(
|
|
496
|
+
dv = array(((0, -omega, 0.0), (omega, 0, 0.0), (0.0, 0.0, 0.0))).T
|
|
491
497
|
return v, dv
|
|
492
498
|
|
|
493
499
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
def spiral_sphere(N, Om=2*pi, b=array((0, 0, 1))): #change to 4*pi
|
|
497
|
-
"""
|
|
498
|
-
Internal helper function for the raycasting that returns an array of
|
|
500
|
+
def spiral_sphere(N, Om=None, b=None): # change to 4*pi
|
|
501
|
+
"""Internal helper function for the raycasting that returns an array of
|
|
499
502
|
unit vectors (N, 3) giving equally distributed directions on a part of
|
|
500
503
|
sphere given by the center direction b and the solid angle Om.
|
|
501
504
|
"""
|
|
505
|
+
Om = 2 * pi if Om is None else Om
|
|
506
|
+
b = array((0, 0, 1)) if b is None else b
|
|
502
507
|
# first produce 'equally' distributed directions in spherical coords
|
|
503
|
-
o = 4*pi/Om
|
|
504
|
-
h = -1+ 2*arange(N)/(N*o-1.)
|
|
508
|
+
o = 4 * pi / Om
|
|
509
|
+
h = -1 + 2 * arange(N) / (N * o - 1.0)
|
|
505
510
|
theta = arccos(h)
|
|
506
511
|
phi = zeros_like(theta)
|
|
507
512
|
for i, hk in enumerate(h[1:]):
|
|
508
|
-
phi[i+1] = phi[i]+3.6/sqrt(N*o*(1-hk*hk)) % (2*pi)
|
|
513
|
+
phi[i + 1] = phi[i] + 3.6 / sqrt(N * o * (1 - hk * hk)) % (2 * pi)
|
|
509
514
|
# translate to cartesian coords
|
|
510
515
|
xyz = vstack((sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)))
|
|
511
516
|
# mirror everything on a plane so that b points into the center
|
|
512
517
|
a = xyz[:, 0]
|
|
513
|
-
b = b/norm(b)
|
|
514
|
-
ab = (a-b)[:, newaxis]
|
|
515
|
-
if norm(ab)<1e-10:
|
|
518
|
+
b = b / norm(b)
|
|
519
|
+
ab = (a - b)[:, newaxis]
|
|
520
|
+
if norm(ab) < 1e-10:
|
|
516
521
|
return xyz
|
|
517
522
|
# this is the Householder matrix for mirroring
|
|
518
|
-
H = identity(3)-dot(ab, ab.T)/dot(ab.T, a)
|
|
523
|
+
H = identity(3) - dot(ab, ab.T) / dot(ab.T, a)
|
|
519
524
|
# actual mirroring
|
|
520
525
|
return dot(H, xyz)
|
|
521
526
|
|
|
527
|
+
|
|
522
528
|
class GeneralFlowEnvironment(Environment):
|
|
523
|
-
"""
|
|
524
|
-
An acoustic environment with a generic flow field.
|
|
529
|
+
"""An acoustic environment with a generic flow field.
|
|
525
530
|
|
|
526
531
|
This class provides the facilities to calculate the travel time (distances)
|
|
527
532
|
between grid point locations and microphone locations in a generic flow
|
|
@@ -531,33 +536,30 @@ class GeneralFlowEnvironment(Environment):
|
|
|
531
536
|
backwards in time. The result is interpolated within a tetrahedal grid
|
|
532
537
|
spanned between these rays.
|
|
533
538
|
"""
|
|
539
|
+
|
|
534
540
|
#: The flow field, must be of type :class:`~acoular.environments.FlowField`.
|
|
535
|
-
ff = Trait(FlowField,
|
|
536
|
-
desc="flow field")
|
|
541
|
+
ff = Trait(FlowField, desc='flow field')
|
|
537
542
|
|
|
538
543
|
#: Number of rays used per solid angle :math:`\Omega`, defaults to 200.
|
|
539
|
-
N = Int(200,
|
|
540
|
-
desc="number of rays per Om")
|
|
544
|
+
N = Int(200, desc='number of rays per Om')
|
|
541
545
|
|
|
542
546
|
#: The maximum solid angle used in the algorithm, defaults to :math:`\pi`.
|
|
543
|
-
Om = Float(pi,
|
|
544
|
-
desc="maximum solid angle")
|
|
547
|
+
Om = Float(pi, desc='maximum solid angle')
|
|
545
548
|
|
|
546
549
|
# internal identifier
|
|
547
550
|
digest = Property(
|
|
548
|
-
depends_on=['c', 'ff.digest', 'N', 'Om'],
|
|
549
|
-
|
|
551
|
+
depends_on=['c', 'ff.digest', 'N', 'Om'],
|
|
552
|
+
)
|
|
550
553
|
|
|
551
554
|
# internal dictionary of interpolators
|
|
552
555
|
idict = Dict
|
|
553
556
|
|
|
554
557
|
@cached_property
|
|
555
|
-
def _get_digest(
|
|
556
|
-
return digest(
|
|
558
|
+
def _get_digest(self):
|
|
559
|
+
return digest(self)
|
|
557
560
|
|
|
558
|
-
def _r(
|
|
559
|
-
"""
|
|
560
|
-
Calculates the virtual distances between grid point locations and
|
|
561
|
+
def _r(self, gpos, mpos=0.0):
|
|
562
|
+
"""Calculates the virtual distances between grid point locations and
|
|
561
563
|
microphone locations or the origin. These virtual distances correspond
|
|
562
564
|
to travel times of the sound along a ray that is traced through the
|
|
563
565
|
medium. Functionality may change in the future.
|
|
@@ -575,21 +577,22 @@ class GeneralFlowEnvironment(Environment):
|
|
|
575
577
|
Returns
|
|
576
578
|
-------
|
|
577
579
|
array of floats
|
|
578
|
-
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
580
|
+
The distances in a twodimensional (N, M) array of floats. If M==1,
|
|
579
581
|
then only a one-dimensional array is returned.
|
|
582
|
+
|
|
580
583
|
"""
|
|
581
584
|
c = self.c
|
|
582
|
-
|
|
585
|
+
|
|
583
586
|
if isscalar(mpos):
|
|
584
|
-
mpos = array((0, 0, 0), dtype
|
|
587
|
+
mpos = array((0, 0, 0), dtype=float32)[:, newaxis]
|
|
585
588
|
|
|
586
589
|
gt = empty((gpos.shape[-1], mpos.shape[-1]))
|
|
587
590
|
for micnum, x0 in enumerate(mpos.T):
|
|
588
|
-
key = x0.tobytes()
|
|
589
|
-
#
|
|
591
|
+
key = x0.tobytes() # make array hashable
|
|
592
|
+
# TODO: the interpolator also depends the roi, so idict keys should also depend on roi
|
|
590
593
|
# OR the idict should be cleaned if roi changes
|
|
591
594
|
try:
|
|
592
|
-
li = self.idict[key]
|
|
595
|
+
li = self.idict[key] # fetch stored interpolator
|
|
593
596
|
except KeyError:
|
|
594
597
|
# if interpolator doesn't exist, construct it
|
|
595
598
|
roi = gpos
|
|
@@ -601,11 +604,10 @@ class GeneralFlowEnvironment(Environment):
|
|
|
601
604
|
gt[:, micnum] = li(gpos.T)
|
|
602
605
|
if gt.shape[1] == 1:
|
|
603
606
|
gt = gt[:, 0]
|
|
604
|
-
return c*gt
|
|
607
|
+
return c * gt # return distance along ray
|
|
605
608
|
|
|
606
|
-
def get_interpolator(
|
|
607
|
-
"""
|
|
608
|
-
gets an LinearNDInterpolator object
|
|
609
|
+
def get_interpolator(self, roi, x0):
|
|
610
|
+
"""Gets an LinearNDInterpolator object.
|
|
609
611
|
|
|
610
612
|
Parameters
|
|
611
613
|
----------
|
|
@@ -614,52 +616,55 @@ class GeneralFlowEnvironment(Environment):
|
|
|
614
616
|
co-ordinates. Used to estimate the maximum distance and ROI
|
|
615
617
|
extension and center.
|
|
616
618
|
x0 : array of floats of shape (3)
|
|
617
|
-
The location of the microphone in 3D cartesian co-ordinates.
|
|
619
|
+
The location of the microphone in 3D cartesian co-ordinates.
|
|
618
620
|
|
|
619
621
|
Returns
|
|
620
622
|
-------
|
|
621
623
|
LinearNDInterpolator object
|
|
624
|
+
|
|
622
625
|
"""
|
|
623
626
|
c = self.c
|
|
624
627
|
|
|
625
628
|
# the DE system
|
|
626
|
-
def f1(t, y, v):
|
|
629
|
+
def f1(t, y, v): # noqa: ARG001
|
|
627
630
|
x = y[0:3]
|
|
628
631
|
s = y[3:6]
|
|
629
632
|
vv, dv = v(x)
|
|
630
|
-
sa = sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2])
|
|
633
|
+
sa = sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2])
|
|
631
634
|
x = empty(6)
|
|
632
|
-
x[0:3] = c*s/sa - vv
|
|
633
|
-
x[3:6] = dot(s, -dv.T)
|
|
635
|
+
x[0:3] = c * s / sa - vv # time reversal
|
|
636
|
+
x[3:6] = dot(s, -dv.T) # time reversal
|
|
634
637
|
return x
|
|
635
638
|
|
|
636
639
|
# integration along a single ray
|
|
637
640
|
def fr(x0, n0, rmax, dt, v, xyz, t):
|
|
638
|
-
s0 = n0 / (c+dot(v(x0)[0], n0))
|
|
641
|
+
s0 = n0 / (c + dot(v(x0)[0], n0))
|
|
639
642
|
y0 = hstack((x0, s0))
|
|
640
643
|
oo = ode(f1)
|
|
641
644
|
oo.set_f_params(v)
|
|
642
|
-
oo.set_integrator(
|
|
643
|
-
|
|
644
|
-
|
|
645
|
+
oo.set_integrator(
|
|
646
|
+
'vode',
|
|
647
|
+
rtol=1e-4, # accuracy !
|
|
648
|
+
max_step=1e-4 * rmax,
|
|
649
|
+
) # for thin shear layer
|
|
645
650
|
oo.set_initial_value(y0, 0)
|
|
646
651
|
while oo.successful():
|
|
647
652
|
xyz.append(oo.y[0:3])
|
|
648
653
|
t.append(oo.t)
|
|
649
|
-
if norm(oo.y[0:3]-x0)>rmax:
|
|
654
|
+
if norm(oo.y[0:3] - x0) > rmax:
|
|
650
655
|
break
|
|
651
|
-
oo.integrate(oo.t+dt)
|
|
656
|
+
oo.integrate(oo.t + dt)
|
|
652
657
|
|
|
653
658
|
gs2 = roi.shape[-1]
|
|
654
659
|
vv = self.ff.v
|
|
655
660
|
NN = int(sqrt(self.N))
|
|
656
|
-
xe = roi.mean(1)
|
|
657
|
-
r = x0[:, newaxis]-roi
|
|
658
|
-
rmax = sqrt((r*r).sum(0).max())
|
|
659
|
-
nv = spiral_sphere(self.N, self.Om, b=xe-x0)
|
|
660
|
-
rstep = rmax/sqrt(self.N)
|
|
661
|
+
xe = roi.mean(1) # center of grid
|
|
662
|
+
r = x0[:, newaxis] - roi
|
|
663
|
+
rmax = sqrt((r * r).sum(0).max()) # maximum distance
|
|
664
|
+
nv = spiral_sphere(self.N, self.Om, b=xe - x0)
|
|
665
|
+
rstep = rmax / sqrt(self.N)
|
|
661
666
|
rmax += rstep
|
|
662
|
-
tstep = rstep/c
|
|
667
|
+
tstep = rstep / c
|
|
663
668
|
xyz = []
|
|
664
669
|
t = []
|
|
665
670
|
lastind = 0
|
|
@@ -672,10 +677,8 @@ class GeneralFlowEnvironment(Environment):
|
|
|
672
677
|
dd.add_points(xyz[lastind:], restart=True)
|
|
673
678
|
lastind = len(xyz)
|
|
674
679
|
# ConvexHull includes grid if no grid points on hull
|
|
675
|
-
if dd.simplices.min()>=gs2:
|
|
680
|
+
if dd.simplices.min() >= gs2:
|
|
676
681
|
break
|
|
677
682
|
xyz = array(xyz)
|
|
678
683
|
t = array(t)
|
|
679
684
|
return LinearNDInterpolator(xyz, t)
|
|
680
|
-
|
|
681
|
-
|