acoular 25.4__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/__init__.py +2 -4
- acoular/aiaa/aiaa.py +10 -10
- acoular/base.py +12 -34
- acoular/calib.py +20 -19
- acoular/configuration.py +3 -3
- acoular/demo/__init__.py +6 -1
- acoular/demo/acoular_demo.py +34 -10
- acoular/deprecation.py +10 -1
- acoular/environments.py +107 -117
- acoular/fastFuncs.py +16 -10
- acoular/fbeamform.py +300 -402
- acoular/fprocess.py +7 -33
- acoular/grids.py +228 -134
- acoular/h5cache.py +10 -4
- acoular/h5files.py +106 -9
- acoular/internal.py +4 -0
- acoular/microphones.py +22 -10
- acoular/process.py +7 -53
- acoular/sdinput.py +8 -5
- acoular/signals.py +29 -27
- acoular/sources.py +205 -335
- acoular/spectra.py +33 -43
- acoular/tbeamform.py +220 -199
- acoular/tools/helpers.py +52 -33
- acoular/tools/metrics.py +5 -10
- acoular/tprocess.py +1392 -647
- acoular/traitsviews.py +1 -3
- acoular/trajectory.py +5 -5
- acoular/version.py +4 -3
- {acoular-25.4.dist-info → acoular-25.10.dist-info}/METADATA +8 -4
- acoular-25.10.dist-info/RECORD +56 -0
- acoular-25.4.dist-info/RECORD +0 -56
- {acoular-25.4.dist-info → acoular-25.10.dist-info}/WHEEL +0 -0
- {acoular-25.4.dist-info → acoular-25.10.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.4.dist-info → acoular-25.10.dist-info}/licenses/LICENSE +0 -0
acoular/grids.py
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
4
4
|
"""
|
|
5
5
|
Implement support for multidimensional grids and integration sectors.
|
|
6
6
|
|
|
7
|
+
.. inheritance-diagram::
|
|
8
|
+
acoular.grids.Grid
|
|
9
|
+
acoular.grids.RectGrid
|
|
10
|
+
acoular.grids.RectGrid3D
|
|
11
|
+
acoular.grids.ImportGrid
|
|
12
|
+
acoular.grids.LineGrid
|
|
13
|
+
acoular.grids.MergeGrid
|
|
14
|
+
:top-classes:
|
|
15
|
+
acoular.grids.Grid
|
|
16
|
+
:parts: 1
|
|
17
|
+
|
|
18
|
+
.. inheritance-diagram::
|
|
19
|
+
acoular.grids.Sector
|
|
20
|
+
:include-subclasses:
|
|
21
|
+
:top-classes:
|
|
22
|
+
acoular.grids.Sector
|
|
23
|
+
:parts: 1
|
|
24
|
+
|
|
7
25
|
.. autosummary::
|
|
8
26
|
:toctree: generated/
|
|
9
27
|
|
|
@@ -28,32 +46,10 @@ Implement support for multidimensional grids and integration sectors.
|
|
|
28
46
|
# imports from other packages
|
|
29
47
|
import xml.dom.minidom
|
|
30
48
|
from abc import abstractmethod
|
|
49
|
+
from pathlib import Path
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
append,
|
|
35
|
-
arange,
|
|
36
|
-
argmin,
|
|
37
|
-
array,
|
|
38
|
-
asarray,
|
|
39
|
-
concatenate,
|
|
40
|
-
copysign,
|
|
41
|
-
fabs,
|
|
42
|
-
inf,
|
|
43
|
-
isscalar,
|
|
44
|
-
ma,
|
|
45
|
-
mgrid,
|
|
46
|
-
newaxis,
|
|
47
|
-
ones,
|
|
48
|
-
ones_like,
|
|
49
|
-
s_,
|
|
50
|
-
sum, # noqa: A004
|
|
51
|
-
tile,
|
|
52
|
-
unique,
|
|
53
|
-
where,
|
|
54
|
-
zeros,
|
|
55
|
-
)
|
|
56
|
-
from scipy.linalg import norm
|
|
51
|
+
import numpy as np
|
|
52
|
+
import scipy.linalg as spla
|
|
57
53
|
|
|
58
54
|
# from matplotlib.path import Path
|
|
59
55
|
from scipy.spatial import Delaunay
|
|
@@ -72,13 +68,11 @@ from traits.api import (
|
|
|
72
68
|
Union,
|
|
73
69
|
cached_property,
|
|
74
70
|
observe,
|
|
75
|
-
on_trait_change,
|
|
76
71
|
property_depends_on,
|
|
77
72
|
)
|
|
78
73
|
from traits.trait_errors import TraitError
|
|
79
74
|
|
|
80
75
|
# acoular imports
|
|
81
|
-
from .deprecation import deprecated_alias
|
|
82
76
|
from .internal import digest, ldigest
|
|
83
77
|
|
|
84
78
|
|
|
@@ -135,11 +129,11 @@ def in_hull(p, hull, border=True, tol=0):
|
|
|
135
129
|
|
|
136
130
|
|
|
137
131
|
def _det(xvert, yvert):
|
|
138
|
-
xvert = asarray(xvert, dtype=float)
|
|
139
|
-
yvert = asarray(yvert, dtype=float)
|
|
140
|
-
x_prev = concatenate(([xvert[-1]], xvert[:-1]))
|
|
141
|
-
y_prev = concatenate(([yvert[-1]], yvert[:-1]))
|
|
142
|
-
return sum(yvert * x_prev - xvert * y_prev, axis=0)
|
|
132
|
+
xvert = np.asarray(xvert, dtype=float)
|
|
133
|
+
yvert = np.asarray(yvert, dtype=float)
|
|
134
|
+
x_prev = np.concatenate(([xvert[-1]], xvert[:-1]))
|
|
135
|
+
y_prev = np.concatenate(([yvert[-1]], yvert[:-1]))
|
|
136
|
+
return np.sum(yvert * x_prev - xvert * y_prev, axis=0)
|
|
143
137
|
|
|
144
138
|
|
|
145
139
|
class Polygon:
|
|
@@ -173,14 +167,14 @@ class Polygon:
|
|
|
173
167
|
if len(x) != len(y):
|
|
174
168
|
msg = 'x and y must be equally sized.'
|
|
175
169
|
raise IndexError(msg)
|
|
176
|
-
self.x = asarray(x, dtype=float)
|
|
177
|
-
self.y = asarray(y, dtype=float)
|
|
170
|
+
self.x = np.asarray(x, dtype=float)
|
|
171
|
+
self.y = np.asarray(y, dtype=float)
|
|
178
172
|
# Closes the polygon if were open
|
|
179
173
|
x1, y1 = x[0], y[0]
|
|
180
174
|
xn, yn = x[-1], y[-1]
|
|
181
175
|
if x1 != xn or y1 != yn:
|
|
182
|
-
self.x = concatenate((self.x, [x1]))
|
|
183
|
-
self.y = concatenate((self.y, [y1]))
|
|
176
|
+
self.x = np.concatenate((self.x, [x1]))
|
|
177
|
+
self.y = np.concatenate((self.y, [y1]))
|
|
184
178
|
# Anti-clockwise coordinates
|
|
185
179
|
if _det(self.x, self.y) < 0:
|
|
186
180
|
self.x = self.x[::-1]
|
|
@@ -216,12 +210,12 @@ class Polygon:
|
|
|
216
210
|
The method uses an improved algorithm based on Nordbeck and Rydstedt for determining
|
|
217
211
|
whether a point is inside a polygon :cite:`SLOAN198545`.
|
|
218
212
|
"""
|
|
219
|
-
xpoint = asarray(xpoint, dtype=float)
|
|
220
|
-
ypoint = asarray(ypoint, dtype=float)
|
|
213
|
+
xpoint = np.asarray(xpoint, dtype=float)
|
|
214
|
+
ypoint = np.asarray(ypoint, dtype=float)
|
|
221
215
|
# Scalar to array
|
|
222
216
|
if xpoint.shape == ():
|
|
223
|
-
xpoint = array([xpoint], dtype=float)
|
|
224
|
-
ypoint = array([ypoint], dtype=float)
|
|
217
|
+
xpoint = np.array([xpoint], dtype=float)
|
|
218
|
+
ypoint = np.array([ypoint], dtype=float)
|
|
225
219
|
scalar = True
|
|
226
220
|
else:
|
|
227
221
|
scalar = False
|
|
@@ -231,16 +225,16 @@ class Polygon:
|
|
|
231
225
|
raise IndexError(msg)
|
|
232
226
|
# If snear = True: Dist to nearest side < nearest vertex
|
|
233
227
|
# If snear = False: Dist to nearest vertex < nearest side
|
|
234
|
-
snear = ma.masked_all(xpoint.shape, dtype=bool)
|
|
228
|
+
snear = np.ma.masked_all(xpoint.shape, dtype=bool)
|
|
235
229
|
# Initialize arrays
|
|
236
|
-
mindst = ones_like(xpoint, dtype=float) * inf
|
|
237
|
-
j = ma.masked_all(xpoint.shape, dtype=int)
|
|
230
|
+
mindst = np.ones_like(xpoint, dtype=float) * np.inf
|
|
231
|
+
j = np.ma.masked_all(xpoint.shape, dtype=int)
|
|
238
232
|
x = self.x
|
|
239
233
|
y = self.y
|
|
240
234
|
n = len(x) - 1 # Number of sides/vertices defining the polygon
|
|
241
235
|
# Loop over each side defining polygon
|
|
242
236
|
for i in range(n):
|
|
243
|
-
d = ones_like(xpoint, dtype=float) * inf
|
|
237
|
+
d = np.ones_like(xpoint, dtype=float) * np.inf
|
|
244
238
|
# Start of side has coords (x1, y1)
|
|
245
239
|
# End of side has coords (x2, y2)
|
|
246
240
|
# Point has coords (xpoint, ypoint)
|
|
@@ -275,7 +269,7 @@ class Polygon:
|
|
|
275
269
|
snear[mask & tlt0] = False
|
|
276
270
|
# Point is closer to this side than to any other side or vertex
|
|
277
271
|
snear[mask & tle1] = True
|
|
278
|
-
if ma.count(snear) != snear.size:
|
|
272
|
+
if np.ma.count(snear) != snear.size:
|
|
279
273
|
msg = 'Error computing distances'
|
|
280
274
|
raise IndexError(msg)
|
|
281
275
|
mindst **= 0.5
|
|
@@ -286,22 +280,21 @@ class Polygon:
|
|
|
286
280
|
jo = j.copy()
|
|
287
281
|
jo[j == 0] -= 1
|
|
288
282
|
area = _det([x[j + 1], x[j], x[jo - 1]], [y[j + 1], y[j], y[jo - 1]])
|
|
289
|
-
mindst[~snear] = copysign(mindst, area)[~snear]
|
|
283
|
+
mindst[~snear] = np.copysign(mindst, area)[~snear]
|
|
290
284
|
# Point is closer to its nearest side than to its nearest vertex, check
|
|
291
285
|
# if point is to left or right of this side.
|
|
292
286
|
# If point is to left of side it is inside polygon, else point is
|
|
293
287
|
# outside polygon.
|
|
294
288
|
area = _det([x[j], x[j + 1], xpoint], [y[j], y[j + 1], ypoint])
|
|
295
|
-
mindst[snear] = copysign(mindst, area)[snear]
|
|
289
|
+
mindst[snear] = np.copysign(mindst, area)[snear]
|
|
296
290
|
# Point is on side of polygon
|
|
297
|
-
mindst[fabs(mindst) < smalld] = 0
|
|
291
|
+
mindst[np.fabs(mindst) < smalld] = 0
|
|
298
292
|
# If input values were scalar then the output should be too
|
|
299
293
|
if scalar:
|
|
300
294
|
mindst = float(mindst)
|
|
301
295
|
return mindst
|
|
302
296
|
|
|
303
297
|
|
|
304
|
-
@deprecated_alias({'gpos': 'pos'})
|
|
305
298
|
class Grid(ABCHasStrictTraits):
|
|
306
299
|
"""
|
|
307
300
|
Abstract base class for grid geometries.
|
|
@@ -309,6 +302,15 @@ class Grid(ABCHasStrictTraits):
|
|
|
309
302
|
This class defines a common interface for all grid geometries and provides tools to query grid
|
|
310
303
|
properties and related data. It is intended to serve as a base class for specialized grid
|
|
311
304
|
implementations and should not be instantiated directly as it lacks concrete functionality.
|
|
305
|
+
|
|
306
|
+
.. _units_note_grids:
|
|
307
|
+
.. admonition:: Unit of length
|
|
308
|
+
|
|
309
|
+
The source code is agnostic to the unit of length. The positions' coordinates are assumed to
|
|
310
|
+
be in meters. This is consistent with the standard :class:`~acoular.environments.Environment`
|
|
311
|
+
class which uses the speed of sound at 20°C at sea level under standard atmosphere pressure in
|
|
312
|
+
m/s. If the positions' coordinates are provided in a unit other than meter, it is advisable to
|
|
313
|
+
change the :attr:`~acoular.environments.Environment.c` attribute to match the given unit.
|
|
312
314
|
"""
|
|
313
315
|
|
|
314
316
|
#: The total number of grid points. This property is automatically calculated based on other
|
|
@@ -321,6 +323,7 @@ class Grid(ABCHasStrictTraits):
|
|
|
321
323
|
|
|
322
324
|
#: The grid positions represented as a (3, :attr:`size`) array of :class:`floats<float>`.
|
|
323
325
|
#: (read-only)
|
|
326
|
+
#: All positions' coordinates are in meters by default (:ref:`see here <units_note_grids>`).
|
|
324
327
|
pos = Property(desc='x, y, z positions of grid points')
|
|
325
328
|
|
|
326
329
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
@@ -373,10 +376,78 @@ class Grid(ABCHasStrictTraits):
|
|
|
373
376
|
# construct grid-shaped array with "True" entries where sector is
|
|
374
377
|
xyi = sector.contains(xpos).reshape(self.shape)
|
|
375
378
|
# return indices of "True" entries
|
|
376
|
-
return where(xyi)
|
|
379
|
+
return np.where(xyi)
|
|
380
|
+
|
|
381
|
+
def export_gpos(self, filename):
|
|
382
|
+
"""
|
|
383
|
+
Export the grid positions to an XML file.
|
|
384
|
+
|
|
385
|
+
This method generates an XML file containing the positions of all grid points.
|
|
386
|
+
Each point is represented by a ``<pos>`` element with ``Name``, ``x``, ``y``, and ``z``
|
|
387
|
+
attributes. The generated XML is formatted to match the structure required for importing
|
|
388
|
+
into the :class:`ImportGrid` class.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
filename : :class:`str`
|
|
393
|
+
The path to the file to which the grid positions will be written. The file
|
|
394
|
+
extension must be ``.xml``.
|
|
395
|
+
|
|
396
|
+
Raises
|
|
397
|
+
------
|
|
398
|
+
:obj:`OSError`
|
|
399
|
+
If the file cannot be written due to permissions issues or invalid file paths.
|
|
400
|
+
|
|
401
|
+
Notes
|
|
402
|
+
-----
|
|
403
|
+
- The file will be saved in UTF-8 encoding.
|
|
404
|
+
- The ``Name`` attribute for each point is set as ``"Point {i+1}"``, where ``i`` is the
|
|
405
|
+
index of the grid point.
|
|
406
|
+
- If subgrids are defined, they will be included as the ``subgrid`` attribute.
|
|
407
|
+
|
|
408
|
+
Examples
|
|
409
|
+
--------
|
|
410
|
+
Export a grid with 100 points to an XML file:
|
|
411
|
+
|
|
412
|
+
>>> import acoular as ac
|
|
413
|
+
>>> import numpy as np
|
|
414
|
+
>>> grid = ac.ImportGrid()
|
|
415
|
+
>>> # Create some grid points
|
|
416
|
+
>>> points = np.arange(9).reshape(3, 3)
|
|
417
|
+
>>> grid.pos = points
|
|
418
|
+
>>> grid.export_gpos('grid_points.xml') # doctest: +SKIP
|
|
419
|
+
|
|
420
|
+
The generated ``grid_points.xml`` file will look like this:
|
|
421
|
+
|
|
422
|
+
.. code-block:: xml
|
|
423
|
+
|
|
424
|
+
<?xml version="1.1" encoding="utf-8"?><Grid name="grid_points">
|
|
425
|
+
<pos Name="Point 1" x="0" y="1" z="2"/>
|
|
426
|
+
<pos Name="Point 2" x="3" y="4" z="5"/>
|
|
427
|
+
<pos Name="Point 3" x="6" y="7" z="8"/>
|
|
428
|
+
</Grid>
|
|
429
|
+
"""
|
|
430
|
+
filepath = Path(filename)
|
|
431
|
+
basename = filepath.stem
|
|
432
|
+
with filepath.open('w', encoding='utf-8') as f:
|
|
433
|
+
f.write(f'<?xml version="1.1" encoding="utf-8"?><Grid name="{basename}">\n')
|
|
434
|
+
for i in range(self.pos.shape[-1]):
|
|
435
|
+
has_subgrids = hasattr(self, 'subgrids') and len(self.subgrids) > i
|
|
436
|
+
subgrid_attr = f'subgrid="{self.subgrids[i]}"' if has_subgrids else ''
|
|
437
|
+
pos_str = ' '.join(
|
|
438
|
+
[
|
|
439
|
+
' <pos',
|
|
440
|
+
f'Name="Point {i+1}"',
|
|
441
|
+
f'x="{self.pos[0, i]}"',
|
|
442
|
+
f'y="{self.pos[1, i]}"',
|
|
443
|
+
f'z="{self.pos[2, i]}"',
|
|
444
|
+
f'{subgrid_attr}/>\n',
|
|
445
|
+
]
|
|
446
|
+
)
|
|
447
|
+
f.write(pos_str)
|
|
448
|
+
f.write('</Grid>')
|
|
377
449
|
|
|
378
450
|
|
|
379
|
-
@deprecated_alias({'gpos': 'pos'}, read_only=True)
|
|
380
451
|
class RectGrid(Grid):
|
|
381
452
|
"""
|
|
382
453
|
Provides a 2D Cartesian grid for beamforming results.
|
|
@@ -409,6 +480,9 @@ class RectGrid(Grid):
|
|
|
409
480
|
#: Number of grid points along y-axis. (read-only)
|
|
410
481
|
nysteps = Property(desc='number of grid points along y-axis')
|
|
411
482
|
|
|
483
|
+
#: The grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form. (read-only)
|
|
484
|
+
extent = Property(desc='grid extent as (x_min, x_max, y_min, y_max)')
|
|
485
|
+
|
|
412
486
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
413
487
|
digest = Property(
|
|
414
488
|
depends_on=['x_min', 'x_max', 'y_min', 'y_max', 'z', 'increment'],
|
|
@@ -442,7 +516,7 @@ class RectGrid(Grid):
|
|
|
442
516
|
|
|
443
517
|
@property_depends_on(['x_min', 'x_max', 'y_min', 'y_max', 'increment'])
|
|
444
518
|
def _get_pos(self):
|
|
445
|
-
bpos = mgrid[
|
|
519
|
+
bpos = np.mgrid[
|
|
446
520
|
self.x_min : self.x_max : self.nxsteps * 1j,
|
|
447
521
|
self.y_min : self.y_max : self.nysteps * 1j,
|
|
448
522
|
self.z : self.z + 0.1,
|
|
@@ -450,6 +524,56 @@ class RectGrid(Grid):
|
|
|
450
524
|
bpos.resize((3, self.size))
|
|
451
525
|
return bpos
|
|
452
526
|
|
|
527
|
+
@property_depends_on(['x_min', 'x_max', 'y_min', 'y_max'])
|
|
528
|
+
def _get_extent(self):
|
|
529
|
+
# Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
530
|
+
#
|
|
531
|
+
# Returns
|
|
532
|
+
# -------
|
|
533
|
+
# :class:`tuple` of :class:`floats<float>`
|
|
534
|
+
# (:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
535
|
+
# extent.
|
|
536
|
+
#
|
|
537
|
+
# Notes
|
|
538
|
+
# -----
|
|
539
|
+
# This property is intended for use with the ``extent`` parameter of
|
|
540
|
+
# :obj:`matplotlib.pyplot.imshow`.
|
|
541
|
+
#
|
|
542
|
+
# Examples
|
|
543
|
+
# --------
|
|
544
|
+
# >>> from acoular import RectGrid
|
|
545
|
+
# >>> grid = RectGrid()
|
|
546
|
+
# >>> grid.y_min = -5
|
|
547
|
+
# >>> grid.y_max = 5
|
|
548
|
+
# >>> grid.extent
|
|
549
|
+
# (-1.0, 1.0, -5.0, 5.0)
|
|
550
|
+
return (self.x_min, self.x_max, self.y_min, self.y_max)
|
|
551
|
+
|
|
552
|
+
def extend(self):
|
|
553
|
+
"""
|
|
554
|
+
Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
555
|
+
|
|
556
|
+
Returns
|
|
557
|
+
-------
|
|
558
|
+
:class:`tuple` of :class:`floats<float>`
|
|
559
|
+
(:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
560
|
+
extent.
|
|
561
|
+
|
|
562
|
+
Notes
|
|
563
|
+
-----
|
|
564
|
+
This method is deprecated. Use the :attr:`extent` property instead.
|
|
565
|
+
"""
|
|
566
|
+
import warnings
|
|
567
|
+
|
|
568
|
+
msg = ' '.join(
|
|
569
|
+
[
|
|
570
|
+
"Deprecated use of 'extend' method (will be removed in version 26.04).",
|
|
571
|
+
"Please use the 'extent' trait instead.",
|
|
572
|
+
]
|
|
573
|
+
)
|
|
574
|
+
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
575
|
+
return self.extent
|
|
576
|
+
|
|
453
577
|
def index(self, x, y):
|
|
454
578
|
"""
|
|
455
579
|
Find the indices of a grid point near a given coordinate.
|
|
@@ -508,64 +632,36 @@ class RectGrid(Grid):
|
|
|
508
632
|
dr2 = (xpos[0, :] - r[0]) ** 2 + (xpos[1, :] - r[1]) ** 2
|
|
509
633
|
# array with true/false entries
|
|
510
634
|
inds = dr2 <= r[2] ** 2
|
|
511
|
-
for
|
|
512
|
-
xi, yi = self.index(xpos[0,
|
|
635
|
+
for n_p in np.arange(self.size)[inds]: # n_p -- points in x2-circle
|
|
636
|
+
xi, yi = self.index(xpos[0, n_p], xpos[1, n_p])
|
|
513
637
|
xis += [xi]
|
|
514
638
|
yis += [yi]
|
|
515
639
|
if not (xis and yis): # if no points in circle, take nearest one
|
|
516
640
|
return self.index(r[0], r[1])
|
|
517
|
-
return array(xis), array(yis)
|
|
641
|
+
return np.array(xis), np.array(yis)
|
|
518
642
|
if len(r) == 4: # rectangular subdomain - old functionality
|
|
519
643
|
xi1, yi1 = self.index(min(r[0], r[2]), min(r[1], r[3]))
|
|
520
644
|
xi2, yi2 = self.index(max(r[0], r[2]), max(r[1], r[3]))
|
|
521
|
-
return s_[xi1 : xi2 + 1], s_[yi1 : yi2 + 1]
|
|
645
|
+
return np.s_[xi1 : xi2 + 1], np.s_[yi1 : yi2 + 1]
|
|
522
646
|
xpos = self.pos
|
|
523
647
|
xis = []
|
|
524
648
|
yis = []
|
|
525
649
|
# replaced matplotlib Path by numpy
|
|
526
|
-
# p = Path(array(r).reshape(-1,2))
|
|
650
|
+
# p = Path(np.array(r).reshape(-1,2))
|
|
527
651
|
# inds = p.contains_points()
|
|
528
|
-
# inds = in_poly(xpos[:2,:].T,array(r).reshape(-1,2))
|
|
529
|
-
poly = Polygon(array(r).reshape(-1, 2)[:, 0], array(r).reshape(-1, 2)[:, 1])
|
|
652
|
+
# inds = in_poly(xpos[:2,:].T,np.array(r).reshape(-1,2))
|
|
653
|
+
poly = Polygon(np.array(r).reshape(-1, 2)[:, 0], np.array(r).reshape(-1, 2)[:, 1])
|
|
530
654
|
dists = poly.is_inside(xpos[0, :], xpos[1, :])
|
|
531
655
|
inds = dists >= 0
|
|
532
|
-
for
|
|
533
|
-
xi, yi = self.index(xpos[0,
|
|
656
|
+
for n_p in np.arange(self.size)[inds]: # n_p -- points in x2-circle
|
|
657
|
+
xi, yi = self.index(xpos[0, n_p], xpos[1, n_p])
|
|
534
658
|
xis += [xi]
|
|
535
659
|
yis += [yi]
|
|
536
660
|
if not (xis and yis): # if no points inside, take nearest to center
|
|
537
|
-
center = array(r).reshape(-1, 2).mean(0)
|
|
661
|
+
center = np.array(r).reshape(-1, 2).mean(0)
|
|
538
662
|
return self.index(center[0], center[1])
|
|
539
|
-
return array(xis), array(yis)
|
|
540
|
-
# return arange(self.size)[inds]
|
|
541
|
-
|
|
542
|
-
def extend(self):
|
|
543
|
-
"""
|
|
544
|
-
Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
545
|
-
|
|
546
|
-
Returns
|
|
547
|
-
-------
|
|
548
|
-
:class:`tuple` of :class:`floats<float>`
|
|
549
|
-
(:attr:`x_min`, :attr:`x_max`, :attr:`y_min`, :attr:`y_max`) representing the grid's
|
|
550
|
-
extent.
|
|
551
|
-
|
|
552
|
-
Notes
|
|
553
|
-
-----
|
|
554
|
-
- ``pylab.imhow`` is the same as :obj:`matplotlib.pyplot.imshow`. It's only using a
|
|
555
|
-
different namespace.
|
|
556
|
-
- The return of the method is ment for the ``extent`` parameter of
|
|
557
|
-
:obj:`matplotlib.pyplot.imshow`.
|
|
558
|
-
|
|
559
|
-
Examples
|
|
560
|
-
--------
|
|
561
|
-
>>> from acoular import RectGrid
|
|
562
|
-
>>> grid = RectGrid()
|
|
563
|
-
>>> grid.y_min = -5
|
|
564
|
-
>>> grid.y_max = 5
|
|
565
|
-
>>> grid.extend()
|
|
566
|
-
(-1.0, 1.0, -5.0, 5.0)
|
|
567
|
-
"""
|
|
568
|
-
return (self.x_min, self.x_max, self.y_min, self.y_max)
|
|
663
|
+
return np.array(xis), np.array(yis)
|
|
664
|
+
# return np.arange(self.size)[inds]
|
|
569
665
|
|
|
570
666
|
|
|
571
667
|
class RectGrid3D(RectGrid):
|
|
@@ -575,10 +671,10 @@ class RectGrid3D(RectGrid):
|
|
|
575
671
|
The grid has cubic or nearly cubic cells. It is defined by lower and upper x-, y- and z-limits.
|
|
576
672
|
"""
|
|
577
673
|
|
|
578
|
-
#: The lower z-limit that defines the grid. Default is ``-1``.
|
|
674
|
+
#: The lower z-limit that defines the grid. Default is ``-1.0``.
|
|
579
675
|
z_min = Float(-1.0, desc='minimum z-value')
|
|
580
676
|
|
|
581
|
-
#: The upper z-limit that defines the grid. Default is ``1``.
|
|
677
|
+
#: The upper z-limit that defines the grid. Default is ``1.0``.
|
|
582
678
|
z_max = Float(1.0, desc='maximum z-value')
|
|
583
679
|
|
|
584
680
|
#: Number of grid points along x-axis. (read-only)
|
|
@@ -602,13 +698,13 @@ class RectGrid3D(RectGrid):
|
|
|
602
698
|
return self._increment
|
|
603
699
|
|
|
604
700
|
def _set_increment(self, increment):
|
|
605
|
-
if isscalar(increment):
|
|
701
|
+
if np.isscalar(increment):
|
|
606
702
|
try:
|
|
607
|
-
self._increment =
|
|
703
|
+
self._increment = np.abs(float(increment))
|
|
608
704
|
except ValueError as ve:
|
|
609
705
|
raise TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment) from ve
|
|
610
706
|
elif len(increment) == 3:
|
|
611
|
-
self._increment = array(increment, dtype=float)
|
|
707
|
+
self._increment = np.array(increment, dtype=float)
|
|
612
708
|
else:
|
|
613
709
|
raise (TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment))
|
|
614
710
|
|
|
@@ -627,28 +723,28 @@ class RectGrid3D(RectGrid):
|
|
|
627
723
|
|
|
628
724
|
@property_depends_on(['x_min', 'x_max', '_increment'])
|
|
629
725
|
def _get_nxsteps(self):
|
|
630
|
-
i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[0])
|
|
726
|
+
i = abs(self.increment) if np.isscalar(self.increment) else abs(self.increment[0])
|
|
631
727
|
if i != 0:
|
|
632
728
|
return int(round((abs(self.x_max - self.x_min) + i) / i))
|
|
633
729
|
return 1
|
|
634
730
|
|
|
635
731
|
@property_depends_on(['y_min', 'y_max', '_increment'])
|
|
636
732
|
def _get_nysteps(self):
|
|
637
|
-
i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[1])
|
|
733
|
+
i = abs(self.increment) if np.isscalar(self.increment) else abs(self.increment[1])
|
|
638
734
|
if i != 0:
|
|
639
735
|
return int(round((abs(self.y_max - self.y_min) + i) / i))
|
|
640
736
|
return 1
|
|
641
737
|
|
|
642
738
|
@property_depends_on(['z_min', 'z_max', '_increment'])
|
|
643
739
|
def _get_nzsteps(self):
|
|
644
|
-
i = abs(self.increment) if isscalar(self.increment) else abs(self.increment[2])
|
|
740
|
+
i = abs(self.increment) if np.isscalar(self.increment) else abs(self.increment[2])
|
|
645
741
|
if i != 0:
|
|
646
742
|
return int(round((abs(self.z_max - self.z_min) + i) / i))
|
|
647
743
|
return 1
|
|
648
744
|
|
|
649
745
|
@property_depends_on('digest')
|
|
650
746
|
def _get_pos(self):
|
|
651
|
-
bpos = mgrid[
|
|
747
|
+
bpos = np.mgrid[
|
|
652
748
|
self.x_min : self.x_max : self.nxsteps * 1j,
|
|
653
749
|
self.y_min : self.y_max : self.nysteps * 1j,
|
|
654
750
|
self.z_min : self.z_max : self.nzsteps * 1j,
|
|
@@ -704,7 +800,7 @@ class RectGrid3D(RectGrid):
|
|
|
704
800
|
if z < self.z_min or z > self.z_max:
|
|
705
801
|
msg = f'z-value out of range {z:f} ({self.z_min:f}, {self.z_max:f})'
|
|
706
802
|
raise ValueError(msg)
|
|
707
|
-
if isscalar(self.increment):
|
|
803
|
+
if np.isscalar(self.increment):
|
|
708
804
|
incx = incy = incz = self.increment
|
|
709
805
|
else:
|
|
710
806
|
incx, incy, incz = self.increment
|
|
@@ -757,10 +853,9 @@ class RectGrid3D(RectGrid):
|
|
|
757
853
|
"""
|
|
758
854
|
xi1, yi1, zi1 = self.index(min(x1, x2), min(y1, y2), min(z1, z2))
|
|
759
855
|
xi2, yi2, zi2 = self.index(max(x1, x2), max(y1, y2), max(z1, z2))
|
|
760
|
-
return s_[xi1 : xi2 + 1], s_[yi1 : yi2 + 1], s_[zi1 : zi2 + 1]
|
|
856
|
+
return np.s_[xi1 : xi2 + 1], np.s_[yi1 : yi2 + 1], np.s_[zi1 : zi2 + 1]
|
|
761
857
|
|
|
762
858
|
|
|
763
|
-
@deprecated_alias({'from_file': 'file', 'gpos_file': 'pos'})
|
|
764
859
|
class ImportGrid(Grid):
|
|
765
860
|
"""
|
|
766
861
|
Load a 3D grid from an XML file.
|
|
@@ -800,8 +895,8 @@ class ImportGrid(Grid):
|
|
|
800
895
|
def _set_pos(self, pos):
|
|
801
896
|
self._gpos = pos
|
|
802
897
|
|
|
803
|
-
@
|
|
804
|
-
def
|
|
898
|
+
@observe('file')
|
|
899
|
+
def _import_pos(self, event): # noqa ARG002
|
|
805
900
|
"""
|
|
806
901
|
Import the grid point locations and subgrid names from an XML file.
|
|
807
902
|
|
|
@@ -818,14 +913,14 @@ class ImportGrid(Grid):
|
|
|
818
913
|
|
|
819
914
|
>>> import numpy as np
|
|
820
915
|
>>>
|
|
821
|
-
>>> # Grid 1: ten points
|
|
916
|
+
>>> # Grid 1: ten points arranged in a circle in the x-y plane at z=0
|
|
822
917
|
>>> args = 2 * np.pi * np.arange(10) / 10
|
|
823
918
|
>>> x1 = np.cos(args)
|
|
824
919
|
>>> y1 = np.sin(args)
|
|
825
920
|
>>> z1 = np.zeros_like(x1)
|
|
826
921
|
>>> grid1 = np.vstack([x1, y1, z1]).T
|
|
827
922
|
>>>
|
|
828
|
-
>>> # Grid 2: nine points
|
|
923
|
+
>>> # Grid 2: nine points arranged in a mesh grid in the x-y plane at z=1
|
|
829
924
|
>>> a = np.linspace(-1, 1, 3)
|
|
830
925
|
>>> x2, y2 = np.meshgrid(a, a)
|
|
831
926
|
>>> z2 = np.ones_like(x2)
|
|
@@ -924,11 +1019,10 @@ class ImportGrid(Grid):
|
|
|
924
1019
|
for el in doc.getElementsByTagName('pos'):
|
|
925
1020
|
names.append(el.getAttribute('subgrid'))
|
|
926
1021
|
xyz.append([float(el.getAttribute(a)) for a in 'xyz'])
|
|
927
|
-
self._gpos = array(xyz, 'd').swapaxes(0, 1)
|
|
928
|
-
self.subgrids = array(names)
|
|
1022
|
+
self._gpos = np.array(xyz, 'd').swapaxes(0, 1)
|
|
1023
|
+
self.subgrids = np.array(names)
|
|
929
1024
|
|
|
930
1025
|
|
|
931
|
-
@deprecated_alias({'gpos': 'pos', 'numpoints': 'num_points'}, read_only=['gpos'])
|
|
932
1026
|
class LineGrid(Grid):
|
|
933
1027
|
"""
|
|
934
1028
|
Define a 3D grid for a line geometry.
|
|
@@ -976,8 +1070,9 @@ class LineGrid(Grid):
|
|
|
976
1070
|
#: are set. (read-only)
|
|
977
1071
|
size = Property(desc='overall number of grid points')
|
|
978
1072
|
|
|
979
|
-
#: A (3, :attr:`size`) array containing the x, y, and z positions
|
|
980
|
-
#:
|
|
1073
|
+
#: A (3, :attr:`size`) array containing the x, y, and z positions of the grid points.
|
|
1074
|
+
#: (read-only)
|
|
1075
|
+
#: All positions' coordinates are in meters by default (:ref:`see here <units_note_grids>`).
|
|
981
1076
|
pos = Property(desc='x, y, z positions of grid points')
|
|
982
1077
|
|
|
983
1078
|
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
@@ -1000,15 +1095,14 @@ class LineGrid(Grid):
|
|
|
1000
1095
|
@property_depends_on(['num_points', 'length', 'direction', 'loc'])
|
|
1001
1096
|
def _get_pos(self):
|
|
1002
1097
|
dist = self.length / (self.num_points - 1)
|
|
1003
|
-
loc = array(self.loc, dtype=float).reshape((3, 1))
|
|
1004
|
-
direc_n = array(self.direction) / norm(self.direction)
|
|
1005
|
-
pos = zeros((self.num_points, 3))
|
|
1098
|
+
loc = np.array(self.loc, dtype=float).reshape((3, 1))
|
|
1099
|
+
direc_n = np.array(self.direction) / spla.norm(self.direction)
|
|
1100
|
+
pos = np.zeros((self.num_points, 3))
|
|
1006
1101
|
for s in range(self.num_points):
|
|
1007
1102
|
pos[s] = loc.T + direc_n * dist * s
|
|
1008
1103
|
return pos.T
|
|
1009
1104
|
|
|
1010
1105
|
|
|
1011
|
-
@deprecated_alias({'gpos': 'pos'}, read_only=True)
|
|
1012
1106
|
class MergeGrid(Grid):
|
|
1013
1107
|
"""
|
|
1014
1108
|
Base class for merging multiple grid geometries.
|
|
@@ -1070,17 +1164,17 @@ class MergeGrid(Grid):
|
|
|
1070
1164
|
|
|
1071
1165
|
@property_depends_on(['digest'])
|
|
1072
1166
|
def _get_subgrids(self):
|
|
1073
|
-
subgrids = zeros((1, 0), dtype=str)
|
|
1167
|
+
subgrids = np.zeros((1, 0), dtype=str)
|
|
1074
1168
|
for grid in self.grids:
|
|
1075
|
-
subgrids = append(subgrids, tile(grid.__class__.__name__ + grid.digest, grid.size))
|
|
1076
|
-
return subgrids[:, newaxis].T
|
|
1169
|
+
subgrids = np.append(subgrids, np.tile(grid.__class__.__name__ + grid.digest, grid.size))
|
|
1170
|
+
return subgrids[:, np.newaxis].T
|
|
1077
1171
|
|
|
1078
1172
|
@property_depends_on(['digest'])
|
|
1079
1173
|
def _get_pos(self):
|
|
1080
|
-
bpos = zeros((3, 0))
|
|
1174
|
+
bpos = np.zeros((3, 0))
|
|
1081
1175
|
for grid in self.grids:
|
|
1082
|
-
bpos = append(bpos, grid.pos, axis=1)
|
|
1083
|
-
return unique(bpos, axis=1)
|
|
1176
|
+
bpos = np.append(bpos, grid.pos, axis=1)
|
|
1177
|
+
return np.unique(bpos, axis=1)
|
|
1084
1178
|
|
|
1085
1179
|
|
|
1086
1180
|
class Sector(ABCHasStrictTraits):
|
|
@@ -1134,7 +1228,7 @@ class Sector(ABCHasStrictTraits):
|
|
|
1134
1228
|
>>> sector.contains(positions)
|
|
1135
1229
|
array([ True, True])
|
|
1136
1230
|
"""
|
|
1137
|
-
return ones(pos.shape[1], dtype=bool)
|
|
1231
|
+
return np.ones(pos.shape[1], dtype=bool)
|
|
1138
1232
|
|
|
1139
1233
|
|
|
1140
1234
|
class SingleSector(Sector):
|
|
@@ -1247,7 +1341,7 @@ class RectSector(SingleSector):
|
|
|
1247
1341
|
x = (xmin + xmax) / 2.0
|
|
1248
1342
|
y = (ymin + ymax) / 2.0
|
|
1249
1343
|
dr2 = (pos[0, :] - x) ** 2 + (pos[1, :] - y) ** 2
|
|
1250
|
-
inds[argmin(dr2)] = True
|
|
1344
|
+
inds[np.argmin(dr2)] = True
|
|
1251
1345
|
|
|
1252
1346
|
return inds.astype(bool)
|
|
1253
1347
|
|
|
@@ -1335,7 +1429,7 @@ class RectSector3D(RectSector):
|
|
|
1335
1429
|
x = (xmin + xmax) / 2.0
|
|
1336
1430
|
y = (ymin + ymax) / 2.0
|
|
1337
1431
|
dr2 = (pos[0, :] - x) ** 2 + (pos[1, :] - y) ** 2
|
|
1338
|
-
inds[argmin(dr2)] = True
|
|
1432
|
+
inds[np.argmin(dr2)] = True
|
|
1339
1433
|
|
|
1340
1434
|
return inds.astype(bool)
|
|
1341
1435
|
|
|
@@ -1403,7 +1497,7 @@ class CircSector(SingleSector):
|
|
|
1403
1497
|
|
|
1404
1498
|
# if there's no point inside
|
|
1405
1499
|
if ~inds.any() and self.default_nearest:
|
|
1406
|
-
inds[argmin(dr2)] = True
|
|
1500
|
+
inds[np.argmin(dr2)] = True
|
|
1407
1501
|
|
|
1408
1502
|
return inds
|
|
1409
1503
|
|
|
@@ -1460,14 +1554,14 @@ class PolySector(SingleSector):
|
|
|
1460
1554
|
>>> sec.contains(grid.pos)
|
|
1461
1555
|
array([False, False, False, False, True, True, False, True, True])
|
|
1462
1556
|
"""
|
|
1463
|
-
poly = Polygon(array(self.edges).reshape(-1, 2)[:, 0], array(self.edges).reshape(-1, 2)[:, 1])
|
|
1557
|
+
poly = Polygon(np.array(self.edges).reshape(-1, 2)[:, 0], np.array(self.edges).reshape(-1, 2)[:, 1])
|
|
1464
1558
|
dists = poly.is_inside(pos[0, :], pos[1, :])
|
|
1465
1559
|
inds = dists >= -self.abs_tol if self.include_border else dists > 0
|
|
1466
1560
|
|
|
1467
1561
|
# if none inside, take nearest
|
|
1468
1562
|
if ~inds.any() and self.default_nearest:
|
|
1469
|
-
dr2 = array(self.edges).reshape(-1, 2).mean(0)
|
|
1470
|
-
inds[argmin(dr2)] = True
|
|
1563
|
+
dr2 = np.array(self.edges).reshape(-1, 2).mean(0)
|
|
1564
|
+
inds[np.argmin(dr2)] = True
|
|
1471
1565
|
|
|
1472
1566
|
return inds
|
|
1473
1567
|
|
|
@@ -1520,12 +1614,12 @@ class ConvexSector(SingleSector):
|
|
|
1520
1614
|
>>> sec.contains(grid.pos)
|
|
1521
1615
|
array([False, False, False, False, True, True, False, True, True])
|
|
1522
1616
|
"""
|
|
1523
|
-
inds = in_hull(pos[:2, :].T, array(self.edges).reshape(-1, 2), border=self.include_border, tol=self.abs_tol)
|
|
1617
|
+
inds = in_hull(pos[:2, :].T, np.array(self.edges).reshape(-1, 2), border=self.include_border, tol=self.abs_tol)
|
|
1524
1618
|
|
|
1525
1619
|
# if none inside, take nearest
|
|
1526
1620
|
if ~inds.any() and self.default_nearest:
|
|
1527
|
-
dr2 = array(self.edges).reshape(-1, 2).mean(0) # Use the centroid of the polygon as the "center"
|
|
1528
|
-
inds[argmin(dr2)] = True
|
|
1621
|
+
dr2 = np.array(self.edges).reshape(-1, 2).mean(0) # Use the centroid of the polygon as the "center"
|
|
1622
|
+
inds[np.argmin(dr2)] = True
|
|
1529
1623
|
|
|
1530
1624
|
return inds
|
|
1531
1625
|
|
|
@@ -1580,7 +1674,7 @@ class MultiSector(Sector):
|
|
|
1580
1674
|
array([False, False, False, False, True, True, False, True, True])
|
|
1581
1675
|
"""
|
|
1582
1676
|
# initialize with only "False" entries
|
|
1583
|
-
inds = zeros(pos.shape[1], dtype=bool)
|
|
1677
|
+
inds = np.zeros(pos.shape[1], dtype=bool)
|
|
1584
1678
|
|
|
1585
1679
|
# add points contained in each sector
|
|
1586
1680
|
for sec in self.sectors:
|