acoular 25.1__py3-none-any.whl → 25.3__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/demo/acoular_demo.py +5 -5
- acoular/environments.py +458 -218
- acoular/fprocess.py +199 -97
- acoular/grids.py +714 -303
- acoular/microphones.py +157 -25
- acoular/process.py +405 -201
- acoular/signals.py +382 -87
- acoular/sources.py +1147 -286
- acoular/spectra.py +280 -128
- acoular/trajectory.py +119 -43
- acoular/version.py +2 -2
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/METADATA +6 -5
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/RECORD +16 -16
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/WHEEL +0 -0
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/licenses/LICENSE +0 -0
acoular/grids.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# ------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
|
-
"""
|
|
4
|
+
"""
|
|
5
|
+
Implement support for multidimensional grids and integration sectors.
|
|
5
6
|
|
|
6
7
|
.. autosummary::
|
|
7
8
|
:toctree: generated/
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
PolySector
|
|
21
22
|
ConvexSector
|
|
22
23
|
MultiSector
|
|
23
|
-
|
|
24
|
+
Polygon
|
|
25
|
+
in_hull
|
|
24
26
|
"""
|
|
25
27
|
|
|
26
28
|
# imports from other packages
|
|
@@ -81,40 +83,58 @@ from .internal import digest, ldigest
|
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def in_hull(p, hull, border=True, tol=0):
|
|
84
|
-
"""Test if points in `p` are in `hull`
|
|
85
|
-
`p` should be a `NxK` coordinates of `N` points in `K` dimensions
|
|
86
|
-
`hull` is either a scipy.spatial.Delaunay object or the `MxK` array of the
|
|
87
|
-
coordinates of `M` points in `K`dimensions for which Delaunay triangulation
|
|
88
|
-
will be computed.
|
|
89
86
|
"""
|
|
90
|
-
if
|
|
91
|
-
hull = Delaunay(hull)
|
|
87
|
+
Test if points in ``p`` are in ``hull``, in- or excluding the border.
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
p : :class:`numpy.ndarray` of :class:`floats<float>`, shape `(N, K)`
|
|
92
|
+
Coordinates of `N` points in `K` dimensions.
|
|
96
93
|
|
|
94
|
+
hull : :class:`numpy.ndarray` of :class:`floats<float>`, shape `(M, K)`, or :class:`~scipy.spatial.Delaunay` object
|
|
95
|
+
Coordinates of `M` points in `K` dimensions for which Delaunay triangulation will be
|
|
96
|
+
computed.
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Input parameters:
|
|
98
|
+
border : bool, optional
|
|
99
|
+
Points in :attr:`p` on the border of :attr:`hull` will be kept in the return if `True`. If
|
|
100
|
+
`False`, only points inside :attr:`hull` will be kept. Default is `True`.
|
|
103
101
|
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
tol : :class:`float`, optional
|
|
103
|
+
Tolerance allowed in the :meth:`inside-triangle check<scipy.spatial.Delaunay.find_simplex>`.
|
|
104
|
+
Default is ``0``.
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
109
|
+
An array of boolean values indicating which points in ``p`` are inside the hull, same
|
|
110
|
+
shape as ``p``. Each entry is ``True`` if the corresponding point is inside the hull (or
|
|
111
|
+
on the border, if ``border=True``), and ``False`` otherwise.
|
|
109
112
|
|
|
110
113
|
Notes
|
|
111
114
|
-----
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
This function uses Delaunay triangulation to determine if a point is inside the convex hull,
|
|
116
|
+
which is efficient and robust for arbitrary shapes in higher-dimensional spaces.
|
|
117
|
+
|
|
118
|
+
Examples
|
|
119
|
+
--------
|
|
120
|
+
>>> from acoular.grids import in_hull
|
|
121
|
+
>>> import numpy as np
|
|
122
|
+
>>> from scipy.spatial import Delaunay
|
|
123
|
+
>>> points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
|
|
124
|
+
>>> hull = Delaunay(points)
|
|
125
|
+
>>> p = np.array([[0.5, 0.5], [2, 2]])
|
|
126
|
+
>>> in_hull(p, hull)
|
|
127
|
+
array([ True, False])
|
|
128
|
+
""" # noqa W505
|
|
129
|
+
if not isinstance(hull, Delaunay):
|
|
130
|
+
hull = Delaunay(hull)
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
if border:
|
|
133
|
+
return hull.find_simplex(p, tol=tol) >= 0
|
|
134
|
+
return hull.find_simplex(p, tol=tol) > 0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _det(xvert, yvert):
|
|
118
138
|
xvert = asarray(xvert, dtype=float)
|
|
119
139
|
yvert = asarray(yvert, dtype=float)
|
|
120
140
|
x_prev = concatenate(([xvert[-1]], xvert[:-1]))
|
|
@@ -123,10 +143,30 @@ def _det(xvert, yvert):
|
|
|
123
143
|
|
|
124
144
|
|
|
125
145
|
class Polygon:
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
"""
|
|
147
|
+
Create an object representing a general polygon in a 2D plane.
|
|
148
|
+
|
|
149
|
+
This class allows defining a polygon by specifying the coordinates of its vertices and provides
|
|
150
|
+
methods for checking whether a set of points lies inside the polygon, or if a point is closer to
|
|
151
|
+
a side or vertex of the polygon.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
x : array_like
|
|
156
|
+
Array of x-coordinates of the vertices that define the polygon. These coordinates should
|
|
157
|
+
form a closed shape (i.e., the last point should be the same as the first point).
|
|
158
|
+
|
|
159
|
+
y : array_like
|
|
160
|
+
Array of y-coordinates of the vertices that define the polygon. These coordinates should
|
|
161
|
+
correspond to the x-coordinates, forming a closed shape.
|
|
162
|
+
|
|
163
|
+
Attributes
|
|
164
|
+
----------
|
|
165
|
+
x : :class:`numpy.ndarray`
|
|
166
|
+
Array of x-coordinates of the polygon vertices.
|
|
167
|
+
|
|
168
|
+
y : :class:`numpy.ndarray`
|
|
169
|
+
Array of y-coordinates of the polygon vertices.
|
|
130
170
|
"""
|
|
131
171
|
|
|
132
172
|
def __init__(self, x, y):
|
|
@@ -147,29 +187,34 @@ class Polygon:
|
|
|
147
187
|
self.y = self.y[::-1]
|
|
148
188
|
|
|
149
189
|
def is_inside(self, xpoint, ypoint, smalld=1e-12):
|
|
150
|
-
"""
|
|
190
|
+
"""
|
|
191
|
+
Check if a point or set of points are inside the polygon.
|
|
151
192
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
xpoint : :class:`float` or array_like
|
|
196
|
+
Array of x-coordinates of the points to be tested.
|
|
156
197
|
|
|
157
|
-
|
|
198
|
+
ypoint : :class:`float` or array_like
|
|
199
|
+
Array of y-coordinates of the points to be tested.
|
|
158
200
|
|
|
159
|
-
|
|
201
|
+
smalld : :class:`float`, optional
|
|
202
|
+
Tolerance used for floating point comparisons when checking if a point is exactly on a
|
|
203
|
+
polygon's edge. The default value is ``1e-12``.
|
|
160
204
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
205
|
+
Returns
|
|
206
|
+
-------
|
|
207
|
+
:class:`float` or array_like
|
|
208
|
+
The distance from the point to the nearest point on the polygon. The values returned
|
|
209
|
+
have the following meanings:
|
|
210
|
+
- ``mindst < 0``: Point is outside the polygon.
|
|
211
|
+
- ``mindst = 0``: Point is on an edge of the polygon.
|
|
212
|
+
- ``mindst > 0``: Point is inside the polygon.
|
|
166
213
|
|
|
167
214
|
Notes
|
|
168
215
|
-----
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Software, Vol 7, No. 1, pp 45-47.
|
|
172
|
-
|
|
216
|
+
The method uses an improved algorithm based on Nordbeck and Rydstedt for determining
|
|
217
|
+
whether a point is inside a polygon :cite:`SLOAN198545`.
|
|
173
218
|
"""
|
|
174
219
|
xpoint = asarray(xpoint, dtype=float)
|
|
175
220
|
ypoint = asarray(ypoint, dtype=float)
|
|
@@ -258,67 +303,71 @@ class Polygon:
|
|
|
258
303
|
|
|
259
304
|
@deprecated_alias({'gpos': 'pos'})
|
|
260
305
|
class Grid(ABCHasStrictTraits):
|
|
261
|
-
"""
|
|
306
|
+
"""
|
|
307
|
+
Abstract base class for grid geometries.
|
|
262
308
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
be used directly as it contains no real functionality.
|
|
309
|
+
This class defines a common interface for all grid geometries and provides tools to query grid
|
|
310
|
+
properties and related data. It is intended to serve as a base class for specialized grid
|
|
311
|
+
implementations and should not be instantiated directly as it lacks concrete functionality.
|
|
267
312
|
"""
|
|
268
313
|
|
|
269
|
-
#:
|
|
270
|
-
#:
|
|
314
|
+
#: The total number of grid points. This property is automatically calculated based on other
|
|
315
|
+
#: defining attributes of the grid. (read-only)
|
|
271
316
|
size = Property(desc='overall number of grid points')
|
|
272
317
|
|
|
273
|
-
#:
|
|
274
|
-
#:
|
|
318
|
+
#: The shape of the grid, represented as a tuple. Primarily useful for Cartesian grids.
|
|
319
|
+
#: (read-only)
|
|
275
320
|
shape = Property(desc='grid shape as tuple')
|
|
276
321
|
|
|
277
|
-
#:
|
|
278
|
-
#:
|
|
322
|
+
#: The grid positions represented as a (3, :attr:`size`) array of :class:`floats<float>`.
|
|
323
|
+
#: (read-only)
|
|
279
324
|
pos = Property(desc='x, y, z positions of grid points')
|
|
280
325
|
|
|
281
|
-
|
|
326
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
282
327
|
digest = Property
|
|
283
328
|
|
|
284
329
|
@abstractmethod
|
|
285
330
|
def _get_digest(self):
|
|
286
|
-
"""
|
|
331
|
+
"""Generate a unique digest for the grid."""
|
|
287
332
|
|
|
288
|
-
# 'digest' is a placeholder for other properties in derived classes,
|
|
289
|
-
#
|
|
333
|
+
# 'digest' is a placeholder for other properties in derived classes, necessary to trigger the
|
|
334
|
+
# depends on mechanism
|
|
290
335
|
@property_depends_on(['digest'])
|
|
291
336
|
@abstractmethod
|
|
292
337
|
def _get_size(self):
|
|
293
|
-
"""
|
|
338
|
+
"""Return the number of grid points."""
|
|
294
339
|
|
|
295
340
|
# 'digest' is a placeholder for other properties in derived classes
|
|
296
341
|
@property_depends_on(['digest'])
|
|
297
342
|
@abstractmethod
|
|
298
343
|
def _get_shape(self):
|
|
299
|
-
"""
|
|
344
|
+
"""Return the shape of the grid as a Tuple."""
|
|
300
345
|
|
|
301
346
|
@property_depends_on(['digest'])
|
|
302
347
|
@abstractmethod
|
|
303
348
|
def _get_pos(self):
|
|
304
|
-
"""
|
|
349
|
+
"""Return the grid positions as array of floats, shape (3, :attr:`size`)."""
|
|
305
350
|
|
|
306
351
|
def subdomain(self, sector):
|
|
307
|
-
"""
|
|
352
|
+
"""
|
|
353
|
+
Return the indices for a subdomain in the grid.
|
|
308
354
|
|
|
309
|
-
Allows arbitrary subdomains of type :class:`Sector
|
|
355
|
+
Allows arbitrary subdomains of type :class:`Sector`.
|
|
310
356
|
|
|
311
357
|
Parameters
|
|
312
358
|
----------
|
|
313
|
-
sector : :class:`Sector`
|
|
359
|
+
sector : :class:`Sector` object
|
|
314
360
|
Sector describing the subdomain.
|
|
315
361
|
|
|
316
362
|
Returns
|
|
317
363
|
-------
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
364
|
+
:class:`tuple`
|
|
365
|
+
A 2-tuple of arrays of integers or :obj:`numpy.s_` objects that can be used to mask or
|
|
366
|
+
select the specified subdomain from a grid-shaped array.
|
|
321
367
|
|
|
368
|
+
Notes
|
|
369
|
+
-----
|
|
370
|
+
The :func:`numpy.where` method is used to determine the the indices.
|
|
322
371
|
"""
|
|
323
372
|
xpos = self.pos
|
|
324
373
|
# construct grid-shaped array with "True" entries where sector is
|
|
@@ -329,38 +378,38 @@ class Grid(ABCHasStrictTraits):
|
|
|
329
378
|
|
|
330
379
|
@deprecated_alias({'gpos': 'pos'}, read_only=True)
|
|
331
380
|
class RectGrid(Grid):
|
|
332
|
-
"""
|
|
381
|
+
"""
|
|
382
|
+
Provides a 2D Cartesian grid for beamforming results.
|
|
333
383
|
|
|
334
|
-
|
|
335
|
-
to the z-axis. It is defined by lower and upper x- and
|
|
336
|
-
z co-ordinate.
|
|
384
|
+
This grid is composed of square or nearly square cells and lies on a plane perpendicular
|
|
385
|
+
to the z-axis. It is defined by the lower and upper x- and y-limits and a constant z-coordinate.
|
|
337
386
|
"""
|
|
338
387
|
|
|
339
|
-
#: The lower x-limit that defines the grid
|
|
388
|
+
#: The lower x-limit that defines the grid. Default is ``-1``.
|
|
340
389
|
x_min = Float(-1.0, desc='minimum x-value')
|
|
341
390
|
|
|
342
|
-
#: The upper x-limit that defines the grid
|
|
391
|
+
#: The upper x-limit that defines the grid. Default is ``1``.
|
|
343
392
|
x_max = Float(1.0, desc='maximum x-value')
|
|
344
393
|
|
|
345
|
-
#: The lower y-limit that defines the grid
|
|
394
|
+
#: The lower y-limit that defines the grid. Default is ``-1``.
|
|
346
395
|
y_min = Float(-1.0, desc='minimum y-value')
|
|
347
396
|
|
|
348
|
-
#: The upper y-limit that defines the grid
|
|
397
|
+
#: The upper y-limit that defines the grid. Default is ``1``.
|
|
349
398
|
y_max = Float(1.0, desc='maximum y-value')
|
|
350
399
|
|
|
351
|
-
#: The z
|
|
400
|
+
#: The constant z-coordinate of the grid plane. Default is ``1.0``.
|
|
352
401
|
z = Float(1.0, desc='position on z-axis')
|
|
353
402
|
|
|
354
|
-
#: The
|
|
403
|
+
#: The side length of each cell. Default is ``0.1``.
|
|
355
404
|
increment = Float(0.1, desc='step size')
|
|
356
405
|
|
|
357
|
-
#: Number of grid points along x-axis
|
|
406
|
+
#: Number of grid points along x-axis. (read-only)
|
|
358
407
|
nxsteps = Property(desc='number of grid points along x-axis')
|
|
359
408
|
|
|
360
|
-
#: Number of grid points along y-axis
|
|
409
|
+
#: Number of grid points along y-axis. (read-only)
|
|
361
410
|
nysteps = Property(desc='number of grid points along y-axis')
|
|
362
411
|
|
|
363
|
-
|
|
412
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
364
413
|
digest = Property(
|
|
365
414
|
depends_on=['x_min', 'x_max', 'y_min', 'y_max', 'z', 'increment'],
|
|
366
415
|
)
|
|
@@ -393,14 +442,6 @@ class RectGrid(Grid):
|
|
|
393
442
|
|
|
394
443
|
@property_depends_on(['x_min', 'x_max', 'y_min', 'y_max', 'increment'])
|
|
395
444
|
def _get_pos(self):
|
|
396
|
-
"""Calculates grid co-ordinates.
|
|
397
|
-
|
|
398
|
-
Returns
|
|
399
|
-
-------
|
|
400
|
-
array of floats of shape (3, :attr:`~Grid.size`)
|
|
401
|
-
The grid point x, y, z-coordinates in one array.
|
|
402
|
-
|
|
403
|
-
"""
|
|
404
445
|
bpos = mgrid[
|
|
405
446
|
self.x_min : self.x_max : self.nxsteps * 1j,
|
|
406
447
|
self.y_min : self.y_max : self.nysteps * 1j,
|
|
@@ -410,22 +451,25 @@ class RectGrid(Grid):
|
|
|
410
451
|
return bpos
|
|
411
452
|
|
|
412
453
|
def index(self, x, y):
|
|
413
|
-
"""
|
|
414
|
-
|
|
415
|
-
This can be used to query results or co-ordinates at/near a certain
|
|
416
|
-
co-ordinate.
|
|
454
|
+
"""
|
|
455
|
+
Find the indices of a grid point near a given coordinate.
|
|
417
456
|
|
|
418
457
|
Parameters
|
|
419
458
|
----------
|
|
420
|
-
x
|
|
421
|
-
The
|
|
459
|
+
x : :class:`float`
|
|
460
|
+
The x coordinate of interest.
|
|
461
|
+
y : :class:`float`
|
|
462
|
+
The y coordinate of interest.
|
|
422
463
|
|
|
423
464
|
Returns
|
|
424
465
|
-------
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
co-ordinates from an array with the same shape as the grid.
|
|
466
|
+
:class:`tuple` of :class:`ints<int>`
|
|
467
|
+
Indices corresponding to the nearest grid point.
|
|
428
468
|
|
|
469
|
+
Raises
|
|
470
|
+
------
|
|
471
|
+
ValueError
|
|
472
|
+
If the coordinates are outside the grid boundaries.
|
|
429
473
|
"""
|
|
430
474
|
if x < self.x_min or x > self.x_max:
|
|
431
475
|
msg = 'x-value out of range'
|
|
@@ -438,28 +482,24 @@ class RectGrid(Grid):
|
|
|
438
482
|
return xi, yi
|
|
439
483
|
|
|
440
484
|
def indices(self, *r):
|
|
441
|
-
"""
|
|
485
|
+
"""
|
|
486
|
+
Find the indices of a subdomain in the grid.
|
|
442
487
|
|
|
443
|
-
|
|
444
|
-
This can be used to mask or to query results from a certain
|
|
445
|
-
sector or subdomain.
|
|
488
|
+
Supports rectangular, circular, and polygonal subdomains.
|
|
446
489
|
|
|
447
490
|
Parameters
|
|
448
491
|
----------
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
polygonial shape with corners at (x_n, y_n).
|
|
492
|
+
r : :class:`tuple` of :class:`floats<float>`
|
|
493
|
+
Defines the subdomain shape and dimensions:
|
|
494
|
+
- If 3 values are provided: center ``(x1, y1)`` and radius ``r2`` define a circle.
|
|
495
|
+
- If 4 values are provided: corners ``(x1, y1)`` and ``(x2, y2)`` define a
|
|
496
|
+
rectangle.
|
|
497
|
+
- If more than 4 values are provided: vertices ``(xn, yn)`` define a polygon.
|
|
456
498
|
|
|
457
499
|
Returns
|
|
458
500
|
-------
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
an array with the same shape as the grid.
|
|
462
|
-
|
|
501
|
+
:class:`tuple`
|
|
502
|
+
A 2-tuple of indices or slices corresponding to the subdomain.
|
|
463
503
|
"""
|
|
464
504
|
if len(r) == 3: # only 3 values given -> use x,y,radius method
|
|
465
505
|
xpos = self.pos
|
|
@@ -500,46 +540,62 @@ class RectGrid(Grid):
|
|
|
500
540
|
# return arange(self.size)[inds]
|
|
501
541
|
|
|
502
542
|
def extend(self):
|
|
503
|
-
"""
|
|
543
|
+
"""
|
|
544
|
+
Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
|
|
504
545
|
|
|
505
546
|
Returns
|
|
506
547
|
-------
|
|
507
|
-
|
|
508
|
-
|
|
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.
|
|
509
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)
|
|
510
567
|
"""
|
|
511
568
|
return (self.x_min, self.x_max, self.y_min, self.y_max)
|
|
512
569
|
|
|
513
570
|
|
|
514
571
|
class RectGrid3D(RectGrid):
|
|
515
|
-
"""
|
|
572
|
+
"""
|
|
573
|
+
Provide a cartesian 3D grid for the beamforming results.
|
|
516
574
|
|
|
517
|
-
The grid has cubic or nearly cubic cells. It is defined by lower and upper
|
|
518
|
-
x-, y- and z-limits.
|
|
575
|
+
The grid has cubic or nearly cubic cells. It is defined by lower and upper x-, y- and z-limits.
|
|
519
576
|
"""
|
|
520
577
|
|
|
521
|
-
#: The lower z-limit that defines the grid
|
|
578
|
+
#: The lower z-limit that defines the grid. Default is ``-1``.
|
|
522
579
|
z_min = Float(-1.0, desc='minimum z-value')
|
|
523
580
|
|
|
524
|
-
#: The upper z-limit that defines the grid
|
|
581
|
+
#: The upper z-limit that defines the grid. Default is ``1``.
|
|
525
582
|
z_max = Float(1.0, desc='maximum z-value')
|
|
526
583
|
|
|
527
|
-
#: Number of grid points along x-axis
|
|
584
|
+
#: Number of grid points along x-axis. (read-only)
|
|
528
585
|
nxsteps = Property(desc='number of grid points along x-axis')
|
|
529
586
|
|
|
530
|
-
#: Number of grid points along y-axis
|
|
587
|
+
#: Number of grid points along y-axis. (read-only)
|
|
531
588
|
nysteps = Property(desc='number of grid points along y-axis')
|
|
532
589
|
|
|
533
|
-
#: Number of grid points along
|
|
534
|
-
nzsteps = Property(desc='number of grid points along
|
|
590
|
+
#: Number of grid points along z-axis. (read-only)
|
|
591
|
+
nzsteps = Property(desc='number of grid points along z-axis')
|
|
535
592
|
|
|
536
593
|
# Private trait for increment handling
|
|
537
594
|
_increment = Union(Float(), CArray(shape=(3,), dtype=float), default_value=0.1, desc='step size')
|
|
538
595
|
|
|
539
|
-
#: The cell side length for the grid. This can either be a scalar (same
|
|
540
|
-
#:
|
|
541
|
-
#:
|
|
542
|
-
#: Defaults to 0.1.
|
|
596
|
+
#: The cell side length for the grid. This can either be a scalar (same increments in all 3
|
|
597
|
+
#: dimensions) or a (3,) array of :class:`tuple` of :class:`floats<float>` with respective
|
|
598
|
+
#: increments in x-, y-, and z-direction. Default is ``0.1``.
|
|
543
599
|
increment = Property(desc='step size')
|
|
544
600
|
|
|
545
601
|
def _get_increment(self):
|
|
@@ -556,7 +612,7 @@ class RectGrid3D(RectGrid):
|
|
|
556
612
|
else:
|
|
557
613
|
raise (TraitError(args=self, name='increment', info='Float or CArray(3,)', value=increment))
|
|
558
614
|
|
|
559
|
-
|
|
615
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
560
616
|
digest = Property(
|
|
561
617
|
depends_on=['x_min', 'x_max', 'y_min', 'y_max', 'z_min', 'z_max', '_increment'],
|
|
562
618
|
)
|
|
@@ -592,14 +648,6 @@ class RectGrid3D(RectGrid):
|
|
|
592
648
|
|
|
593
649
|
@property_depends_on('digest')
|
|
594
650
|
def _get_pos(self):
|
|
595
|
-
"""Calculates grid co-ordinates.
|
|
596
|
-
|
|
597
|
-
Returns
|
|
598
|
-
-------
|
|
599
|
-
array of floats of shape (3, :attr:`~Grid.size`)
|
|
600
|
-
The grid point x, y, z-coordinates in one array.
|
|
601
|
-
|
|
602
|
-
"""
|
|
603
651
|
bpos = mgrid[
|
|
604
652
|
self.x_min : self.x_max : self.nxsteps * 1j,
|
|
605
653
|
self.y_min : self.y_max : self.nysteps * 1j,
|
|
@@ -613,22 +661,39 @@ class RectGrid3D(RectGrid):
|
|
|
613
661
|
return digest(self)
|
|
614
662
|
|
|
615
663
|
def index(self, x, y, z):
|
|
616
|
-
"""
|
|
664
|
+
"""
|
|
665
|
+
Return the indices for a grid point near a certain coordinate.
|
|
617
666
|
|
|
618
|
-
This can be used to query results or
|
|
619
|
-
|
|
667
|
+
This can be used to query results or coordinates at or near a certain coordinate. Raises an
|
|
668
|
+
exception if the given coordinate is outside the grid.
|
|
620
669
|
|
|
621
670
|
Parameters
|
|
622
671
|
----------
|
|
623
|
-
x, y, z : float
|
|
624
|
-
The
|
|
672
|
+
x, y, z : :class:`float`
|
|
673
|
+
The coordinates for which the indices is queried.
|
|
625
674
|
|
|
626
675
|
Returns
|
|
627
676
|
-------
|
|
628
|
-
3
|
|
629
|
-
The indices that give the grid point nearest to the given x, y, z
|
|
630
|
-
|
|
631
|
-
|
|
677
|
+
3-:class:`tuple` of :class:`ints<int>`
|
|
678
|
+
The indices that give the grid point nearest to the given x, y, z coordinates from an
|
|
679
|
+
array with the same shape as the grid.
|
|
680
|
+
|
|
681
|
+
Examples
|
|
682
|
+
--------
|
|
683
|
+
Check which of the points in a simple 8-point rectangular grid is closest to the point
|
|
684
|
+
``(0.5, 0.5, 1.0)``.
|
|
685
|
+
|
|
686
|
+
>>> import acoular as ac
|
|
687
|
+
>>>
|
|
688
|
+
>>> grid = ac.RectGrid3D()
|
|
689
|
+
>>> grid.increment = 2
|
|
690
|
+
>>> grid.pos
|
|
691
|
+
array([[-1., -1., -1., -1., 1., 1., 1., 1.],
|
|
692
|
+
[-1., -1., 1., 1., -1., -1., 1., 1.],
|
|
693
|
+
[-1., 1., -1., 1., -1., 1., -1., 1.]])
|
|
694
|
+
>>>
|
|
695
|
+
>>> grid.index(0.5, 0.5, 1.0)
|
|
696
|
+
(1, 1, 1)
|
|
632
697
|
"""
|
|
633
698
|
if x < self.x_min or x > self.x_max:
|
|
634
699
|
msg = f'x-value out of range {x:f} ({self.x_min:f}, {self.x_max:f})'
|
|
@@ -649,23 +714,46 @@ class RectGrid3D(RectGrid):
|
|
|
649
714
|
return xi, yi, zi
|
|
650
715
|
|
|
651
716
|
def indices(self, x1, y1, z1, x2, y2, z2):
|
|
652
|
-
"""
|
|
717
|
+
"""
|
|
718
|
+
Return the indices for a subdomain in the grid.
|
|
653
719
|
|
|
654
|
-
Allows box-shaped subdomains. This can be used to
|
|
655
|
-
|
|
720
|
+
Allows box-shaped subdomains. This can be used to mask or to query results from a certain
|
|
721
|
+
sector or subdomain.
|
|
656
722
|
|
|
657
723
|
Parameters
|
|
658
724
|
----------
|
|
659
|
-
x1, y1, z1, x2, y2, z2 : float
|
|
660
|
-
A box-shaped sector is assumed that is given by two corners
|
|
661
|
-
(
|
|
725
|
+
x1, y1, z1, x2, y2, z2 : :class:`float`
|
|
726
|
+
A box-shaped sector is assumed that is given by two corners ``(x1,y1,z1)`` and
|
|
727
|
+
``(x2,y2,z2)``.
|
|
662
728
|
|
|
663
729
|
Returns
|
|
664
730
|
-------
|
|
665
|
-
3
|
|
666
|
-
The indices that can be used to mask/select the grid subdomain from
|
|
667
|
-
|
|
668
|
-
|
|
731
|
+
3-:class:`tuple` of :obj:`numpy.s_` objects
|
|
732
|
+
The indices that can be used to mask/select the grid subdomain from an array with the
|
|
733
|
+
same shape as the grid.
|
|
734
|
+
|
|
735
|
+
Examples
|
|
736
|
+
--------
|
|
737
|
+
Get the indices of the grid points of a simple 27-point rectangular grid which are located
|
|
738
|
+
inside the first octant.
|
|
739
|
+
|
|
740
|
+
>>> import acoular as ac
|
|
741
|
+
>>>
|
|
742
|
+
>>> grid = ac.RectGrid3D()
|
|
743
|
+
>>> grid.increment = 1
|
|
744
|
+
>>> grid.pos
|
|
745
|
+
array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., 0., 0., 0., 0.,
|
|
746
|
+
0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1.,
|
|
747
|
+
1.],
|
|
748
|
+
[-1., -1., -1., 0., 0., 0., 1., 1., 1., -1., -1., -1., 0.,
|
|
749
|
+
0., 0., 1., 1., 1., -1., -1., -1., 0., 0., 0., 1., 1.,
|
|
750
|
+
1.],
|
|
751
|
+
[-1., 0., 1., -1., 0., 1., -1., 0., 1., -1., 0., 1., -1.,
|
|
752
|
+
0., 1., -1., 0., 1., -1., 0., 1., -1., 0., 1., -1., 0.,
|
|
753
|
+
1.]])
|
|
754
|
+
>>>
|
|
755
|
+
>>> grid.indices(0, 0, 0, 1, 1, 1)
|
|
756
|
+
(slice(1, 3, None), slice(1, 3, None), slice(1, 3, None))
|
|
669
757
|
"""
|
|
670
758
|
xi1, yi1, zi1 = self.index(min(x1, x2), min(y1, y2), min(z1, z2))
|
|
671
759
|
xi2, yi2, zi2 = self.index(max(x1, x2), max(y1, y2), max(z1, z2))
|
|
@@ -674,29 +762,33 @@ class RectGrid3D(RectGrid):
|
|
|
674
762
|
|
|
675
763
|
@deprecated_alias({'from_file': 'file', 'gpos_file': 'pos'})
|
|
676
764
|
class ImportGrid(Grid):
|
|
677
|
-
"""
|
|
765
|
+
"""
|
|
766
|
+
Load a 3D grid from an XML file.
|
|
767
|
+
|
|
768
|
+
This class is used to import a 3D grid defined in an XML file. The grid's
|
|
769
|
+
positions and subgrid names are parsed and stored for further processing.
|
|
770
|
+
"""
|
|
678
771
|
|
|
679
772
|
#: Name of the .xml-file from which to read the data.
|
|
680
773
|
file = File(filter=['*.xml'], exists=True, desc='name of the xml file to import')
|
|
681
774
|
|
|
682
775
|
_gpos = CArray(dtype=float, desc='x, y, z position of all Grid Points')
|
|
683
776
|
|
|
777
|
+
#: Names of subgrids for each point.
|
|
778
|
+
#: This is an optional property, typically used when grids are divided into named subregions.
|
|
684
779
|
subgrids = CArray(desc='names of subgrids for each point')
|
|
685
780
|
|
|
686
|
-
|
|
781
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
687
782
|
digest = Property(depends_on=['_gpos'])
|
|
688
783
|
|
|
689
784
|
@cached_property
|
|
690
785
|
def _get_digest(self):
|
|
691
786
|
return digest(self)
|
|
692
787
|
|
|
693
|
-
# 'digest' is a placeholder for other properties in derived classes,
|
|
694
|
-
# necessary to trigger the depends on mechanism
|
|
695
788
|
@property_depends_on(['_gpos'])
|
|
696
789
|
def _get_size(self):
|
|
697
790
|
return self.pos.shape[-1]
|
|
698
791
|
|
|
699
|
-
# 'digest' is a placeholder for other properties in derived classes
|
|
700
792
|
@property_depends_on(['_gpos'])
|
|
701
793
|
def _get_shape(self):
|
|
702
794
|
return (self.pos.shape[-1],)
|
|
@@ -710,7 +802,122 @@ class ImportGrid(Grid):
|
|
|
710
802
|
|
|
711
803
|
@on_trait_change('file')
|
|
712
804
|
def import_gpos(self):
|
|
713
|
-
"""
|
|
805
|
+
"""
|
|
806
|
+
Import the grid point locations and subgrid names from an XML file.
|
|
807
|
+
|
|
808
|
+
This method is automatically called whenever the :attr:`file` attribute changes.
|
|
809
|
+
|
|
810
|
+
Notes
|
|
811
|
+
-----
|
|
812
|
+
The XML file should have elements with tag ``<pos>``, where each ``<pos>`` element
|
|
813
|
+
contains attributes for ``x``, ``y``, ``z``, and optionally ``subgrid``.
|
|
814
|
+
|
|
815
|
+
Examples
|
|
816
|
+
--------
|
|
817
|
+
Generate the positional data of two microphone grids:
|
|
818
|
+
|
|
819
|
+
>>> import numpy as np
|
|
820
|
+
>>>
|
|
821
|
+
>>> # Grid 1: ten points aranged in a circle in the x-y plane at z=0
|
|
822
|
+
>>> args = 2 * np.pi * np.arange(10) / 10
|
|
823
|
+
>>> x1 = np.cos(args)
|
|
824
|
+
>>> y1 = np.sin(args)
|
|
825
|
+
>>> z1 = np.zeros_like(x1)
|
|
826
|
+
>>> grid1 = np.vstack([x1, y1, z1]).T
|
|
827
|
+
>>>
|
|
828
|
+
>>> # Grid 2: nine points aranged in a mesh grid the the x-y plane at z=1
|
|
829
|
+
>>> a = np.linspace(-1, 1, 3)
|
|
830
|
+
>>> x2, y2 = np.meshgrid(a, a)
|
|
831
|
+
>>> z2 = np.ones_like(x2)
|
|
832
|
+
>>> grid2 = np.vstack([x2, y2, z2])
|
|
833
|
+
|
|
834
|
+
Save the generated data in an XML file:
|
|
835
|
+
|
|
836
|
+
>>> import xml.etree.cElementTree as ET
|
|
837
|
+
>>>
|
|
838
|
+
>>> # Create the root element for the XML document
|
|
839
|
+
>>> root = ET.Element('root')
|
|
840
|
+
>>>
|
|
841
|
+
>>> # Loop over both grid 1 and grid 2, and create XML elements for each of their points
|
|
842
|
+
>>> for num, grid in enumerate([grid1, grid2]): # doctest: +SKIP
|
|
843
|
+
... for x, y, z in grid:
|
|
844
|
+
... # For each point in the grid, create a 'pos' XML element
|
|
845
|
+
... ET.SubElement(root, 'pos', subgrid=str(num), x=str(x), y=str(y), z=str(z))
|
|
846
|
+
>>>
|
|
847
|
+
>>> # Create the final XML tree
|
|
848
|
+
>>> tree = ET.ElementTree(root)
|
|
849
|
+
>>>
|
|
850
|
+
>>> # Write the XML tree to a file named 'two_grids.xml'
|
|
851
|
+
>>> tree.write('two_grids.xml') # doctest: +SKIP
|
|
852
|
+
|
|
853
|
+
The ``two_grids.xml`` file will look like this:
|
|
854
|
+
|
|
855
|
+
.. code-block:: xml
|
|
856
|
+
|
|
857
|
+
<root>
|
|
858
|
+
<pos subgrid="0" x="1.0" y="0.0" z="0.0" />
|
|
859
|
+
<pos subgrid="0" x="0.8090169943749475" y="0.5877852522924731" z="0.0" />
|
|
860
|
+
<pos subgrid="0" x="0.30901699437494745" y="0.9510565162951535" z="0.0" />
|
|
861
|
+
<pos subgrid="0" x="-0.30901699437494734" y="0.9510565162951536" z="0.0" />
|
|
862
|
+
<pos subgrid="0" x="-0.8090169943749473" y="0.5877852522924732" z="0.0" />
|
|
863
|
+
<pos subgrid="0" x="-1.0" y="1.2246467991473532e-16" z="0.0" />
|
|
864
|
+
<pos subgrid="0" x="-0.8090169943749475" y="-0.587785252292473" z="0.0" />
|
|
865
|
+
<pos subgrid="0" x="-0.30901699437494756" y="-0.9510565162951535" z="0.0" />
|
|
866
|
+
<pos subgrid="0" x="0.30901699437494723" y="-0.9510565162951536" z="0.0" />
|
|
867
|
+
<pos subgrid="0" x="0.8090169943749473" y="-0.5877852522924732" z="0.0" />
|
|
868
|
+
<pos subgrid="1" x="-1.0" y="0.0" z="1.0" />
|
|
869
|
+
<pos subgrid="1" x="-1.0" y="0.0" z="1.0" />
|
|
870
|
+
<pos subgrid="1" x="-1.0" y="0.0" z="1.0" />
|
|
871
|
+
<pos subgrid="1" x="-1.0" y="-1.0" z="-1.0" />
|
|
872
|
+
<pos subgrid="1" x="0.0" y="0.0" z="0.0" />
|
|
873
|
+
<pos subgrid="1" x="1.0" y="1.0" z="1.0" />
|
|
874
|
+
<pos subgrid="1" x="1.0" y="1.0" z="1.0" />
|
|
875
|
+
<pos subgrid="1" x="1.0" y="1.0" z="1.0" />
|
|
876
|
+
<pos subgrid="1" x="1.0" y="1.0" z="1.0" />
|
|
877
|
+
</root>
|
|
878
|
+
|
|
879
|
+
Importing the ``two_grids.xml`` file:
|
|
880
|
+
|
|
881
|
+
>>> import acoular as ac
|
|
882
|
+
>>>
|
|
883
|
+
>>> grids = ac.ImportGrid(file='two_grids.xml') # doctest: +SKIP
|
|
884
|
+
>>> grids.size # doctest: +SKIP
|
|
885
|
+
19
|
|
886
|
+
>>> grids.subgrids # doctest: +SKIP
|
|
887
|
+
array(['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1',
|
|
888
|
+
'1', '1', '1', '1', '1', '1'], dtype='<U1')
|
|
889
|
+
>>> grids.gpos.T # doctest: +SKIP
|
|
890
|
+
array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00],
|
|
891
|
+
[ 8.09016994e-01, 5.87785252e-01, 0.00000000e+00],
|
|
892
|
+
[ 3.09016994e-01, 9.51056516e-01, 0.00000000e+00],
|
|
893
|
+
[-3.09016994e-01, 9.51056516e-01, 0.00000000e+00],
|
|
894
|
+
[-8.09016994e-01, 5.87785252e-01, 0.00000000e+00],
|
|
895
|
+
[-1.00000000e+00, 1.22464680e-16, 0.00000000e+00],
|
|
896
|
+
[-8.09016994e-01, -5.87785252e-01, 0.00000000e+00],
|
|
897
|
+
[-3.09016994e-01, -9.51056516e-01, 0.00000000e+00],
|
|
898
|
+
[ 3.09016994e-01, -9.51056516e-01, 0.00000000e+00],
|
|
899
|
+
[ 8.09016994e-01, -5.87785252e-01, 0.00000000e+00],
|
|
900
|
+
[-1.00000000e+00, 0.00000000e+00, 1.00000000e+00],
|
|
901
|
+
[-1.00000000e+00, 0.00000000e+00, 1.00000000e+00],
|
|
902
|
+
[-1.00000000e+00, 0.00000000e+00, 1.00000000e+00],
|
|
903
|
+
[-1.00000000e+00, -1.00000000e+00, -1.00000000e+00],
|
|
904
|
+
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
|
|
905
|
+
[ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00],
|
|
906
|
+
[ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00],
|
|
907
|
+
[ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00],
|
|
908
|
+
[ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00]])
|
|
909
|
+
|
|
910
|
+
Consider two XML files, ``grid1.xml`` and ``grid2.xml`` containing different grids.
|
|
911
|
+
|
|
912
|
+
>>> import acoular as ac
|
|
913
|
+
>>>
|
|
914
|
+
>>> grid = ac.ImportGrid(file='grid1.xml') # doctest: +SKIP
|
|
915
|
+
>>> grid.size # doctest: +SKIP
|
|
916
|
+
8
|
|
917
|
+
>>> grid.file = 'grid2.xml' # doctest: +SKIP
|
|
918
|
+
>>> grid.size # doctest: +SKIP
|
|
919
|
+
12
|
|
920
|
+
"""
|
|
714
921
|
doc = xml.dom.minidom.parse(self.file)
|
|
715
922
|
names = []
|
|
716
923
|
xyz = []
|
|
@@ -723,28 +930,57 @@ class ImportGrid(Grid):
|
|
|
723
930
|
|
|
724
931
|
@deprecated_alias({'gpos': 'pos', 'numpoints': 'num_points'}, read_only=['gpos'])
|
|
725
932
|
class LineGrid(Grid):
|
|
726
|
-
"""
|
|
933
|
+
"""
|
|
934
|
+
Define a 3D grid for a line geometry.
|
|
935
|
+
|
|
936
|
+
The :class:`LineGrid` class represents a grid where points are arranged linearly in 3D space.
|
|
937
|
+
The grid is defined by a starting location (:attr:`loc`), a direction vector
|
|
938
|
+
(:attr:`direction`), a total length (:attr:`length`), and the number of points
|
|
939
|
+
(:attr:`num_points`) along the line.
|
|
940
|
+
|
|
941
|
+
Notes
|
|
942
|
+
-----
|
|
943
|
+
- The distance between points is :attr:`length` ``/ (`` :attr:`num_points` ``- 1)``.
|
|
944
|
+
- The direction vector is normalized to ensure consistency.
|
|
945
|
+
|
|
946
|
+
Examples
|
|
947
|
+
--------
|
|
948
|
+
Create a line grid with 5 points along the x-axis, starting at (0, 0, 0), with a length of 4
|
|
949
|
+
meters:
|
|
950
|
+
|
|
951
|
+
>>> import acoular as ac
|
|
952
|
+
>>> grid = ac.LineGrid()
|
|
953
|
+
>>> grid.loc = (0.0, 0.0, 0.0)
|
|
954
|
+
>>> grid.direction = (1.0, 0.0, 0.0)
|
|
955
|
+
>>> grid.length = 4
|
|
956
|
+
>>> grid.num_points = 5
|
|
957
|
+
>>> grid.pos
|
|
958
|
+
array([[0., 1., 2., 3., 4.],
|
|
959
|
+
[0., 0., 0., 0., 0.],
|
|
960
|
+
[0., 0., 0., 0., 0.]])
|
|
961
|
+
"""
|
|
727
962
|
|
|
728
|
-
#:
|
|
963
|
+
#: Starting point of the grid in 3D space. Default is ``(0.0, 0.0, 0.0)``.
|
|
729
964
|
loc = Tuple((0.0, 0.0, 0.0))
|
|
730
965
|
|
|
731
|
-
#:
|
|
966
|
+
#: A vector defining the orientation of the line in 3D space. Default is ``(1.0, 0.0, 0.0)``.
|
|
732
967
|
direction = Tuple((1.0, 0.0, 0.0), desc='Line orientation ')
|
|
733
968
|
|
|
734
|
-
#:
|
|
969
|
+
#: Total length of the line. Default is ``1.0``.
|
|
735
970
|
length = Float(1, desc='length of the line source')
|
|
736
971
|
|
|
737
|
-
#:
|
|
972
|
+
#: Number of grid points along the line. Default is ``1``.
|
|
738
973
|
num_points = Int(1, desc='length of the line source')
|
|
739
974
|
|
|
740
|
-
#:
|
|
741
|
-
#:
|
|
975
|
+
#: The total number of grid points. Automatically updated when other grid-defining attributes
|
|
976
|
+
#: are set. (read-only)
|
|
742
977
|
size = Property(desc='overall number of grid points')
|
|
743
978
|
|
|
744
|
-
#:
|
|
745
|
-
#:
|
|
979
|
+
#: A (3, :attr:`size`) array containing the x, y, and z positions
|
|
980
|
+
#: of the grid points. (read-only)
|
|
746
981
|
pos = Property(desc='x, y, z positions of grid points')
|
|
747
982
|
|
|
983
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
748
984
|
digest = Property(
|
|
749
985
|
depends_on=['loc', 'direction', 'length', 'num_points', 'size'],
|
|
750
986
|
)
|
|
@@ -753,13 +989,10 @@ class LineGrid(Grid):
|
|
|
753
989
|
def _get_digest(self):
|
|
754
990
|
return digest(self)
|
|
755
991
|
|
|
756
|
-
# 'digest' is a placeholder for other properties in derived classes,
|
|
757
|
-
# necessary to trigger the depends on mechanism
|
|
758
992
|
@property_depends_on(['num_points'])
|
|
759
993
|
def _get_size(self):
|
|
760
994
|
return self.pos.shape[-1]
|
|
761
995
|
|
|
762
|
-
# 'digest' is a placeholder for other properties in derived classes
|
|
763
996
|
@property_depends_on(['num_points'])
|
|
764
997
|
def _get_shape(self):
|
|
765
998
|
return self.pos.shape[-1]
|
|
@@ -777,21 +1010,47 @@ class LineGrid(Grid):
|
|
|
777
1010
|
|
|
778
1011
|
@deprecated_alias({'gpos': 'pos'}, read_only=True)
|
|
779
1012
|
class MergeGrid(Grid):
|
|
780
|
-
"""
|
|
1013
|
+
"""
|
|
1014
|
+
Base class for merging multiple grid geometries.
|
|
1015
|
+
|
|
1016
|
+
The `MergeGrid` class allows the combination of multiple grid geometries into a single unified
|
|
1017
|
+
grid. Each input grid is assigned a subdomain in the resulting grid, and all properties, such as
|
|
1018
|
+
positions and identifiers, are appropriately merged.
|
|
1019
|
+
|
|
1020
|
+
Notes
|
|
1021
|
+
-----
|
|
1022
|
+
- The merged grid eliminates duplicate points based on their positions.
|
|
1023
|
+
- Each subgrid retains its original grid properties, such as digest and size.
|
|
1024
|
+
|
|
1025
|
+
Examples
|
|
1026
|
+
--------
|
|
1027
|
+
Merging two simple grids:
|
|
1028
|
+
|
|
1029
|
+
>>> import acoular as ac
|
|
1030
|
+
>>> grid1 = ac.LineGrid(loc=(0, 0, 0), direction=(1, 0, 0), length=1, num_points=3)
|
|
1031
|
+
>>> grid2 = ac.LineGrid(loc=(0, 0, 0), direction=(0, 1, 0), length=1, num_points=3)
|
|
1032
|
+
>>> merged_grid = ac.MergeGrid()
|
|
1033
|
+
>>> merged_grid.grids = [grid1, grid2]
|
|
1034
|
+
>>> merged_grid.size
|
|
1035
|
+
5
|
|
1036
|
+
>>> merged_grid.pos
|
|
1037
|
+
array([[0. , 0. , 0. , 0.5, 1. ],
|
|
1038
|
+
[0. , 0.5, 1. , 0. , 0. ],
|
|
1039
|
+
[0. , 0. , 0. , 0. , 0. ]])
|
|
1040
|
+
"""
|
|
781
1041
|
|
|
782
|
-
#:
|
|
783
|
-
#:
|
|
784
|
-
#: other grid defining properties are set
|
|
1042
|
+
#: A list of :class:`Grid` objects to be merged. Each grid is treated as a subdomain in the
|
|
1043
|
+
#: resulting merged grid.
|
|
785
1044
|
grids = List(desc='list of grids')
|
|
786
1045
|
|
|
1046
|
+
#: A list of unique digests for each grid being merged. (read-only)
|
|
787
1047
|
grid_digest = Str(desc='digest of the merged grids')
|
|
788
1048
|
|
|
1049
|
+
#: Names of subgrids corresponding to each point in the merged grid. (read-only)
|
|
789
1050
|
subgrids = Property(desc='names of subgrids for each point')
|
|
790
1051
|
|
|
791
|
-
|
|
792
|
-
digest = Property(
|
|
793
|
-
depends_on=['grids', 'grid_digest'],
|
|
794
|
-
)
|
|
1052
|
+
#: A unique identifier for the grid, based on its properties. (read-only)
|
|
1053
|
+
digest = Property(depends_on=['grids', 'grid_digest'])
|
|
795
1054
|
|
|
796
1055
|
@cached_property
|
|
797
1056
|
def _get_digest(self):
|
|
@@ -801,13 +1060,10 @@ class MergeGrid(Grid):
|
|
|
801
1060
|
def _set_sourcesdigest(self, event): # noqa ARG002
|
|
802
1061
|
self.grid_digest = ldigest(self.grids)
|
|
803
1062
|
|
|
804
|
-
# 'digest' is a placeholder for other properties in derived classes,
|
|
805
|
-
# necessary to trigger the depends on mechanism
|
|
806
1063
|
@property_depends_on(['digest'])
|
|
807
1064
|
def _get_size(self):
|
|
808
1065
|
return self.pos.shape[-1]
|
|
809
1066
|
|
|
810
|
-
# 'digest' is a placeholder for other properties in derived classes
|
|
811
1067
|
@property_depends_on(['digest'])
|
|
812
1068
|
def _get_shape(self):
|
|
813
1069
|
return self.pos.shape[-1]
|
|
@@ -828,90 +1084,140 @@ class MergeGrid(Grid):
|
|
|
828
1084
|
|
|
829
1085
|
|
|
830
1086
|
class Sector(ABCHasStrictTraits):
|
|
831
|
-
"""
|
|
1087
|
+
"""
|
|
1088
|
+
Abstract base class for all sector types.
|
|
1089
|
+
|
|
1090
|
+
The :class:`Sector` class defines the common interface for all sector implementations. It serves
|
|
1091
|
+
as the base class for creating diverse sector geometries, each capable of determining whether
|
|
1092
|
+
specific grid points fall within its bounds.
|
|
1093
|
+
|
|
1094
|
+
When used directly, this class represents a sector encompassing the entire grid, meaning all
|
|
1095
|
+
positions are considered valid.
|
|
1096
|
+
|
|
1097
|
+
Notes
|
|
1098
|
+
-----
|
|
1099
|
+
This class is designed to be subclassed. Derived classes should override the :meth:`contains`
|
|
1100
|
+
method to implement specific sector geometries (e.g., circular, rectangular, or polygon shapes).
|
|
832
1101
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1102
|
+
Examples
|
|
1103
|
+
--------
|
|
1104
|
+
Load example data and set different Sectors for integration in the
|
|
1105
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
836
1106
|
"""
|
|
837
1107
|
|
|
838
1108
|
def contains(self, pos):
|
|
839
|
-
"""
|
|
840
|
-
|
|
841
|
-
|
|
1109
|
+
"""
|
|
1110
|
+
Check whether the given coordinates lie within the sector's bounds.
|
|
1111
|
+
|
|
1112
|
+
This method determines if each column of the input array :attr:`pos` corresponds to a point
|
|
1113
|
+
that falls within the sector. For this base class, all points are considered within the
|
|
1114
|
+
sector.
|
|
842
1115
|
|
|
843
1116
|
Parameters
|
|
844
1117
|
----------
|
|
845
|
-
pos :
|
|
846
|
-
|
|
847
|
-
grid
|
|
1118
|
+
pos : :class:`numpy.ndarray`
|
|
1119
|
+
A 2D array with shape `(3, N)`, where `N` is the number of grid points. Each column
|
|
1120
|
+
represents the ``x, y, z`` coordinates of a grid point.
|
|
848
1121
|
|
|
849
1122
|
Returns
|
|
850
1123
|
-------
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1124
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1125
|
+
A 1D array of length `N`, where each entry indicates whether the corresponding
|
|
1126
|
+
column in :attr:`pos` lies within the sector's bounds.
|
|
1127
|
+
|
|
1128
|
+
Examples
|
|
1129
|
+
--------
|
|
1130
|
+
>>> import numpy as np
|
|
1131
|
+
>>> import acoular as ac
|
|
1132
|
+
>>> sector = ac.Sector()
|
|
1133
|
+
>>> positions = np.array([[0, 1], [0, 0], [0, 0]]) # Two grid points
|
|
1134
|
+
>>> sector.contains(positions)
|
|
1135
|
+
array([ True, True])
|
|
855
1136
|
"""
|
|
856
1137
|
return ones(pos.shape[1], dtype=bool)
|
|
857
1138
|
|
|
858
1139
|
|
|
859
1140
|
class SingleSector(Sector):
|
|
860
|
-
"""
|
|
1141
|
+
"""
|
|
1142
|
+
Base class for single sector types.
|
|
861
1143
|
|
|
862
|
-
Defines the common interface for all single sector classes. This class
|
|
863
|
-
|
|
864
|
-
|
|
1144
|
+
Defines the common interface for all single sector classes. This class can serve as a base for
|
|
1145
|
+
various single sector implementations. When used directly, it defines a sector that encompasses
|
|
1146
|
+
the whole grid. It includes attributes for handling border inclusion, tolerance for sector
|
|
1147
|
+
borders, and default behavior when no points are inside the sector.
|
|
1148
|
+
|
|
1149
|
+
Examples
|
|
1150
|
+
--------
|
|
1151
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1152
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
865
1153
|
"""
|
|
866
1154
|
|
|
867
|
-
#:
|
|
1155
|
+
#: If ``True``, grid points lying on the sector border are included in the sector. Default is
|
|
1156
|
+
#: ``True``.
|
|
868
1157
|
include_border = Bool(True, desc='include points on the border')
|
|
869
1158
|
|
|
870
|
-
#:
|
|
1159
|
+
#: The absolute tolerance to apply when determining if a grid point lies on the sector border.
|
|
1160
|
+
#: Default is ``1e-12``.
|
|
871
1161
|
abs_tol = Float(1e-12, desc='absolute tolerance for sector border')
|
|
872
1162
|
|
|
873
|
-
#:
|
|
874
|
-
#:
|
|
875
|
-
|
|
1163
|
+
#: If ``True``, the ``contains`` method (as in :meth:`RectSector.contains`,
|
|
1164
|
+
#: :meth:`RectSector3D.contains`, :meth:`CircSector.contains`, and :meth:`PolySector.contains`)
|
|
1165
|
+
#: returns the nearest grid point if no grid points are inside the sector. Default is ``True``.
|
|
1166
|
+
default_nearest = Bool(True, desc='``contains`` method return nearest grid point to center if none inside sector')
|
|
876
1167
|
|
|
877
1168
|
|
|
878
1169
|
class RectSector(SingleSector):
|
|
879
|
-
"""
|
|
1170
|
+
"""
|
|
1171
|
+
Class for defining a rectangular sector.
|
|
880
1172
|
|
|
881
|
-
|
|
882
|
-
|
|
1173
|
+
Defines a rectangular sector either for 2D grids (rectangle in the XY-plane) or for 3D grids
|
|
1174
|
+
(rectangular cylindrical sector parallel to the z-axis). The sector is bounded by the
|
|
1175
|
+
specified :attr:`x_min`, :attr:`x_max`, :attr:`y_min`, and :attr:`y_max` positions, defining the
|
|
1176
|
+
lower and upper bounds of the rectangle along the x and y axes.
|
|
1177
|
+
|
|
1178
|
+
Examples
|
|
1179
|
+
--------
|
|
1180
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1181
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
883
1182
|
"""
|
|
884
1183
|
|
|
885
|
-
#: The
|
|
1184
|
+
#: The minimum x position of the rectangle. Default is ``-1.0``.
|
|
886
1185
|
x_min = Float(-1.0, desc='minimum x position of the rectangle')
|
|
887
1186
|
|
|
888
|
-
#: The
|
|
1187
|
+
#: The maximum x position of the rectangle. Default is ``1.0``.
|
|
889
1188
|
x_max = Float(1.0, desc='maximum x position of the rectangle')
|
|
890
1189
|
|
|
891
|
-
#: The
|
|
1190
|
+
#: The minimum y position of the rectangle. Default is ``-1.0``.
|
|
892
1191
|
y_min = Float(-1.0, desc='minimum y position of the rectangle')
|
|
893
1192
|
|
|
894
|
-
#: The
|
|
1193
|
+
#: The maximum y position of the rectangle. Default is ``1.0``.
|
|
895
1194
|
y_max = Float(1.0, desc='maximum y position of the rectangle')
|
|
896
1195
|
|
|
897
1196
|
def contains(self, pos):
|
|
898
|
-
"""
|
|
899
|
-
rectangular sector.
|
|
900
|
-
|
|
901
|
-
is
|
|
1197
|
+
"""
|
|
1198
|
+
Check if the coordinates in a given array lie within the rectangular sector.
|
|
1199
|
+
|
|
1200
|
+
If no coordinate is inside, the nearest one to the rectangle center is returned if
|
|
1201
|
+
:attr:`~SingleSector.default_nearest` is ``True``.
|
|
902
1202
|
|
|
903
1203
|
Parameters
|
|
904
1204
|
----------
|
|
905
|
-
pos : array of floats
|
|
906
|
-
|
|
907
|
-
grid positions
|
|
1205
|
+
pos : array of :class:`floats<float>`
|
|
1206
|
+
A `(3, N)` array containing the positions of `N` grid points.
|
|
908
1207
|
|
|
909
1208
|
Returns
|
|
910
1209
|
-------
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
given sector
|
|
914
|
-
|
|
1210
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1211
|
+
An array of shape (N,) indicating which of the given positions
|
|
1212
|
+
lie within the given sector.
|
|
1213
|
+
|
|
1214
|
+
Examples
|
|
1215
|
+
--------
|
|
1216
|
+
>>> import acoular as ac
|
|
1217
|
+
>>> grid = ac.RectGrid(increment=2)
|
|
1218
|
+
>>> sec = ac.RectSector(x_min=0, y_min=0)
|
|
1219
|
+
>>> sec.contains(grid.pos)
|
|
1220
|
+
array([False, False, False, True])
|
|
915
1221
|
"""
|
|
916
1222
|
# make sure xmin is minimum etc
|
|
917
1223
|
xmin = min(self.x_min, self.x_max)
|
|
@@ -947,35 +1253,53 @@ class RectSector(SingleSector):
|
|
|
947
1253
|
|
|
948
1254
|
|
|
949
1255
|
class RectSector3D(RectSector):
|
|
950
|
-
"""
|
|
1256
|
+
"""
|
|
1257
|
+
Class for defining a cuboid sector.
|
|
1258
|
+
|
|
1259
|
+
This class extends the :class:`RectSector` class to define a cuboid sector, which can be used
|
|
1260
|
+
for 3D grids. The cuboid sector is defined by its bounds along the x, y, and z axes.
|
|
951
1261
|
|
|
952
|
-
|
|
1262
|
+
Examples
|
|
1263
|
+
--------
|
|
1264
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1265
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
953
1266
|
"""
|
|
954
1267
|
|
|
955
|
-
#: The lower z position of the cuboid
|
|
1268
|
+
#: The lower z position of the cuboid. Default is ``-1.0``.
|
|
956
1269
|
z_min = Float(-1.0, desc='minimum z position of the cuboid')
|
|
957
1270
|
|
|
958
|
-
#: The upper z position of the cuboid
|
|
1271
|
+
#: The upper z position of the cuboid. Default is ``1.0``.
|
|
959
1272
|
z_max = Float(1.0, desc='maximum z position of the cuboid')
|
|
960
1273
|
|
|
961
1274
|
def contains(self, pos):
|
|
962
|
-
"""
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1275
|
+
"""
|
|
1276
|
+
Check if the coordinates in a given array lie within the cuboid sector.
|
|
1277
|
+
|
|
1278
|
+
The method checks if the points in the provided position array are within the cuboid
|
|
1279
|
+
defined by the bounds along the x, y, and z axes. If no point is inside the sector, and if
|
|
1280
|
+
:attr:`~SingleSector.default_nearest` is ``True``, the nearest point to the center of the
|
|
1281
|
+
cuboid is returned.
|
|
966
1282
|
|
|
967
1283
|
Parameters
|
|
968
1284
|
----------
|
|
969
|
-
pos : array of floats
|
|
970
|
-
|
|
971
|
-
|
|
1285
|
+
pos : array of :class:`floats<float>`
|
|
1286
|
+
A (3, N) array containing the positions of N grid points, where each point is
|
|
1287
|
+
represented by its x, y, and z coordinates.
|
|
972
1288
|
|
|
973
1289
|
Returns
|
|
974
1290
|
-------
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1291
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1292
|
+
A boolean array of shape shape ``(N,)`` indicating which of the given positions lie
|
|
1293
|
+
within the cuboid sector. ``True`` if the grid point is inside the cuboid,
|
|
1294
|
+
otherwise ``False``.
|
|
1295
|
+
|
|
1296
|
+
Examples
|
|
1297
|
+
--------
|
|
1298
|
+
>>> import acoular as ac
|
|
1299
|
+
>>> grid = ac.RectGrid3D(increment=2)
|
|
1300
|
+
>>> sec = ac.RectSector3D(x_min=0, y_min=0, z_min=0)
|
|
1301
|
+
>>> sec.contains(grid.pos)
|
|
1302
|
+
array([False, False, False, False, False, False, False, True])
|
|
979
1303
|
"""
|
|
980
1304
|
# make sure xmin is minimum etc
|
|
981
1305
|
xmin = min(self.x_min, self.x_max)
|
|
@@ -1017,39 +1341,61 @@ class RectSector3D(RectSector):
|
|
|
1017
1341
|
|
|
1018
1342
|
|
|
1019
1343
|
class CircSector(SingleSector):
|
|
1020
|
-
"""
|
|
1344
|
+
"""
|
|
1345
|
+
Class for defining a circular sector.
|
|
1021
1346
|
|
|
1022
|
-
|
|
1023
|
-
for 3D grids
|
|
1347
|
+
Defines a circular sector, which can be used for both 2D grids (as a circle in the XY-plane) or
|
|
1348
|
+
for 3D grids (as a cylindrical sector parallel to the z-axis). The sector is defined by its
|
|
1349
|
+
center position (:attr:`x`, :attr:`y`) and its radius :attr:`r`.
|
|
1350
|
+
|
|
1351
|
+
Examples
|
|
1352
|
+
--------
|
|
1353
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1354
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
1024
1355
|
"""
|
|
1025
1356
|
|
|
1026
|
-
#: x position of the circle center
|
|
1357
|
+
#: The x position of the circle center. Default is ``0.0``.
|
|
1027
1358
|
x = Float(0.0, desc='x position of the circle center')
|
|
1028
1359
|
|
|
1029
|
-
#: y position of the circle center
|
|
1360
|
+
#: The y position of the circle center. Default is ``0.0``.
|
|
1030
1361
|
y = Float(0.0, desc='y position of the circle center')
|
|
1031
1362
|
|
|
1032
|
-
#:
|
|
1363
|
+
#: Radius of the circle. Default is ``1.0``.
|
|
1033
1364
|
r = Float(1.0, desc='radius of the circle')
|
|
1034
1365
|
|
|
1035
1366
|
def contains(self, pos):
|
|
1036
|
-
"""
|
|
1037
|
-
circular sector.
|
|
1038
|
-
|
|
1039
|
-
|
|
1367
|
+
"""
|
|
1368
|
+
Check if the coordinates in a given array lie within the circular sector.
|
|
1369
|
+
|
|
1370
|
+
The method calculates the squared distance of each point from the center of the circle and
|
|
1371
|
+
checks if it lies within the sector, considering the sector's radius :attr:`r`. If no point
|
|
1372
|
+
is inside and :attr:`~SingleSector.default_nearest` is ``True``, the nearest point outside
|
|
1373
|
+
the sector will be returned.
|
|
1040
1374
|
|
|
1041
1375
|
Parameters
|
|
1042
1376
|
----------
|
|
1043
|
-
pos : array of floats
|
|
1044
|
-
|
|
1045
|
-
|
|
1377
|
+
pos : array of :class:`floats<float>`
|
|
1378
|
+
A (3, N) array containing the positions of N grid points, where each point is
|
|
1379
|
+
represented by its x, y, and z coordinates.
|
|
1046
1380
|
|
|
1047
1381
|
Returns
|
|
1048
1382
|
-------
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1383
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1384
|
+
A boolean array of shape shape (N,) indicating which of the given positions lie within
|
|
1385
|
+
the circular sector. ``True`` if the grid point is inside the circular sector,
|
|
1386
|
+
otherwise ``False``.
|
|
1387
|
+
|
|
1388
|
+
Examples
|
|
1389
|
+
--------
|
|
1390
|
+
>>> import acoular as ac
|
|
1391
|
+
>>> grid = ac.RectGrid(increment=1)
|
|
1392
|
+
>>> grid.pos
|
|
1393
|
+
array([[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
1394
|
+
[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
1395
|
+
[ 1., 1., 1., 1., 1., 1., 1., 1., 1.]])
|
|
1396
|
+
>>> sec = ac.CircSector(x=1, y=1, r=0.5)
|
|
1397
|
+
>>> sec.contains(grid.pos)
|
|
1398
|
+
array([False, False, False, False, False, False, False, False, True])
|
|
1053
1399
|
"""
|
|
1054
1400
|
dr2 = (pos[0, :] - self.x) ** 2 + (pos[1, :] - self.y) ** 2
|
|
1055
1401
|
# which points are in the circle?
|
|
@@ -1063,31 +1409,56 @@ class CircSector(SingleSector):
|
|
|
1063
1409
|
|
|
1064
1410
|
|
|
1065
1411
|
class PolySector(SingleSector):
|
|
1066
|
-
"""
|
|
1412
|
+
"""
|
|
1413
|
+
Class for defining a polygon sector.
|
|
1414
|
+
|
|
1415
|
+
Inherits from :class:`SingleSector` and provides functionality to define a polygonal sector on a
|
|
1416
|
+
2D grid.
|
|
1067
1417
|
|
|
1068
|
-
|
|
1418
|
+
Notes
|
|
1419
|
+
-----
|
|
1420
|
+
The polygon is specified by the :class:`Polygon` class.
|
|
1421
|
+
|
|
1422
|
+
Examples
|
|
1423
|
+
--------
|
|
1424
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1425
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
1069
1426
|
"""
|
|
1070
1427
|
|
|
1071
|
-
|
|
1428
|
+
#: List of coordinates representing the polygon's vertices. The coordinates must define a closed
|
|
1429
|
+
#: polygon like ``x1, y1, x2, y2, ... xn, yn``.
|
|
1072
1430
|
edges = List(Float)
|
|
1073
1431
|
|
|
1074
1432
|
def contains(self, pos):
|
|
1075
|
-
"""
|
|
1076
|
-
|
|
1077
|
-
|
|
1433
|
+
"""
|
|
1434
|
+
Check if the coordinates in a given array lie within the polygon sector.
|
|
1435
|
+
|
|
1436
|
+
If no coordinate is inside, the nearest one to the rectangle center is returned if
|
|
1437
|
+
:attr:`~SingleSector.default_nearest` is ``True``.
|
|
1078
1438
|
|
|
1079
1439
|
Parameters
|
|
1080
1440
|
----------
|
|
1081
|
-
pos : array of floats
|
|
1082
|
-
|
|
1083
|
-
|
|
1441
|
+
pos : array of :class:`floats<float>`
|
|
1442
|
+
A (3, N) array containing the positions of N grid points where each point is represented
|
|
1443
|
+
by its x, y, and z coordinates.
|
|
1084
1444
|
|
|
1085
1445
|
Returns
|
|
1086
1446
|
-------
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1447
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1448
|
+
A boolean array of shape `(N,)` indicating which of the given positions lie within the
|
|
1449
|
+
polygon sector. ``True`` if the grid point is inside the polygon, otherwise ``False``.
|
|
1450
|
+
|
|
1451
|
+
Examples
|
|
1452
|
+
--------
|
|
1453
|
+
>>> import acoular as ac
|
|
1454
|
+
>>> grid = ac.RectGrid(increment=1)
|
|
1455
|
+
>>> grid.pos
|
|
1456
|
+
array([[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
1457
|
+
[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
1458
|
+
[ 1., 1., 1., 1., 1., 1., 1., 1., 1.]])
|
|
1459
|
+
>>> sec = ac.PolySector(edges=[0, 0, 1, 0, 1, 1, 0, 1])
|
|
1460
|
+
>>> sec.contains(grid.pos)
|
|
1461
|
+
array([False, False, False, False, True, True, False, True, True])
|
|
1091
1462
|
"""
|
|
1092
1463
|
poly = Polygon(array(self.edges).reshape(-1, 2)[:, 0], array(self.edges).reshape(-1, 2)[:, 1])
|
|
1093
1464
|
dists = poly.is_inside(pos[0, :], pos[1, :])
|
|
@@ -1102,71 +1473,111 @@ class PolySector(SingleSector):
|
|
|
1102
1473
|
|
|
1103
1474
|
|
|
1104
1475
|
class ConvexSector(SingleSector):
|
|
1105
|
-
"""
|
|
1476
|
+
"""
|
|
1477
|
+
Class for defining a convex hull sector.
|
|
1478
|
+
|
|
1479
|
+
This class defines a convex hull sector for 2D grids. The sector is created using a list of edge
|
|
1480
|
+
coordinates :attr:`edges` which represent the vertices of a polygon. The convex hull is the
|
|
1481
|
+
smallest convex shape that contains all the given vertices.
|
|
1106
1482
|
|
|
1107
|
-
|
|
1483
|
+
Examples
|
|
1484
|
+
--------
|
|
1485
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1486
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
1108
1487
|
"""
|
|
1109
1488
|
|
|
1110
|
-
|
|
1489
|
+
#: List of edge coordinates that define the convex hull. The coordinates must define a closed
|
|
1490
|
+
#: polygon that forms the convex hull like `x1, y1, x2, y2, ... xn, yn`.
|
|
1111
1491
|
edges = List(Float)
|
|
1112
1492
|
|
|
1113
1493
|
def contains(self, pos):
|
|
1114
|
-
"""
|
|
1115
|
-
convex sector.
|
|
1116
|
-
|
|
1117
|
-
is
|
|
1494
|
+
"""
|
|
1495
|
+
Check if the coordinates in a given array lie within the convex sector.
|
|
1496
|
+
|
|
1497
|
+
If no coordinate is inside, the nearest one to the rectangle center is returned if
|
|
1498
|
+
:attr:`~SingleSector.default_nearest` is ``True``.
|
|
1118
1499
|
|
|
1119
1500
|
Parameters
|
|
1120
1501
|
----------
|
|
1121
|
-
pos : array of floats
|
|
1122
|
-
Array
|
|
1123
|
-
grid positions
|
|
1502
|
+
pos : array of :class:`floats<float>`
|
|
1503
|
+
Array containing the positions of N grid points, shape `(3, N)`.
|
|
1124
1504
|
|
|
1125
1505
|
Returns
|
|
1126
1506
|
-------
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
given sector
|
|
1130
|
-
|
|
1507
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1508
|
+
An array of shape `(N,)` indicating which of the given positions
|
|
1509
|
+
lie within the given sector.
|
|
1510
|
+
|
|
1511
|
+
Examples
|
|
1512
|
+
--------
|
|
1513
|
+
>>> import acoular as ac
|
|
1514
|
+
>>> grid = ac.RectGrid(increment=1)
|
|
1515
|
+
>>> grid.pos
|
|
1516
|
+
array([[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
1517
|
+
[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
1518
|
+
[ 1., 1., 1., 1., 1., 1., 1., 1., 1.]])
|
|
1519
|
+
>>> sec = ac.ConvexSector(edges=[0, 0, 1, 0, 1, 1, 0, 1])
|
|
1520
|
+
>>> sec.contains(grid.pos)
|
|
1521
|
+
array([False, False, False, False, True, True, False, True, True])
|
|
1131
1522
|
"""
|
|
1132
1523
|
inds = in_hull(pos[:2, :].T, array(self.edges).reshape(-1, 2), border=self.include_border, tol=self.abs_tol)
|
|
1133
1524
|
|
|
1134
1525
|
# if none inside, take nearest
|
|
1135
1526
|
if ~inds.any() and self.default_nearest:
|
|
1136
|
-
dr2 = array(self.edges).reshape(-1, 2).mean(0)
|
|
1527
|
+
dr2 = array(self.edges).reshape(-1, 2).mean(0) # Use the centroid of the polygon as the "center"
|
|
1137
1528
|
inds[argmin(dr2)] = True
|
|
1138
1529
|
|
|
1139
1530
|
return inds
|
|
1140
1531
|
|
|
1141
1532
|
|
|
1142
1533
|
class MultiSector(Sector):
|
|
1143
|
-
"""
|
|
1534
|
+
"""
|
|
1535
|
+
Class for defining a sector consisting of multiple sectors.
|
|
1144
1536
|
|
|
1145
|
-
|
|
1146
|
-
and returns the points contained in each sector.
|
|
1537
|
+
This class allows the combination of several individual sectors into one.
|
|
1147
1538
|
|
|
1539
|
+
Examples
|
|
1540
|
+
--------
|
|
1541
|
+
Load example data and set diffrent Sectors for intergration in the
|
|
1542
|
+
:ref:`sector integration example<sector_integration_example>`.
|
|
1148
1543
|
"""
|
|
1149
1544
|
|
|
1150
|
-
#: List of :class:`
|
|
1151
|
-
#: to be mixed.
|
|
1545
|
+
#: List of :class:`Sector` objects to be mixed, each defining a different sector.
|
|
1152
1546
|
sectors = List(Instance(Sector))
|
|
1153
1547
|
|
|
1154
1548
|
def contains(self, pos):
|
|
1155
|
-
"""
|
|
1156
|
-
of the sub-sectors.
|
|
1549
|
+
"""
|
|
1550
|
+
Check if the coordinates in a given array lie within any of the sub-sectors.
|
|
1551
|
+
|
|
1552
|
+
This method iterates over the list of sectors, checking if each point in the given position
|
|
1553
|
+
array lies within any of the defined sectors.
|
|
1157
1554
|
|
|
1158
1555
|
Parameters
|
|
1159
1556
|
----------
|
|
1160
|
-
pos : array of floats
|
|
1161
|
-
|
|
1162
|
-
|
|
1557
|
+
pos : array of :class:`floats<float>`
|
|
1558
|
+
A (3, N) array containing the positions of N grid points, where each point is
|
|
1559
|
+
represented by its x, y, and z coordinates.
|
|
1163
1560
|
|
|
1164
1561
|
Returns
|
|
1165
1562
|
-------
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
sectors
|
|
1169
|
-
|
|
1563
|
+
:class:`numpy.ndarray` of :class:`bools<bool>`
|
|
1564
|
+
A boolean array of shape `(N,)` indicating which of the given positions lie within any
|
|
1565
|
+
of the defined sectors. ``True`` if the grid point is inside the circular sector,
|
|
1566
|
+
``False`` if otherwise.
|
|
1567
|
+
|
|
1568
|
+
Examples
|
|
1569
|
+
--------
|
|
1570
|
+
>>> import acoular as ac
|
|
1571
|
+
>>> grid = ac.RectGrid(increment=1)
|
|
1572
|
+
>>> grid.pos
|
|
1573
|
+
array([[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
1574
|
+
[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
1575
|
+
[ 1., 1., 1., 1., 1., 1., 1., 1., 1.]])
|
|
1576
|
+
>>> sec1 = ac.RectSector(x_min=0, y_min=0)
|
|
1577
|
+
>>> sec2 = ac.CircSector(x=1, y=1, r=0.5)
|
|
1578
|
+
>>> multi_sec = ac.MultiSector(sectors=[sec1, sec2])
|
|
1579
|
+
>>> multi_sec.contains(grid.pos)
|
|
1580
|
+
array([False, False, False, False, True, True, False, True, True])
|
|
1170
1581
|
"""
|
|
1171
1582
|
# initialize with only "False" entries
|
|
1172
1583
|
inds = zeros(pos.shape[1], dtype=bool)
|