acoular 25.1__py3-none-any.whl → 25.3.post1__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/grids.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements support for two- and threedimensional grids.
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 not isinstance(hull, Delaunay):
91
- hull = Delaunay(hull)
87
+ Test if points in ``p`` are in ``hull``, in- or excluding the border.
92
88
 
93
- if border:
94
- return hull.find_simplex(p, tol=tol) >= 0
95
- return hull.find_simplex(p, tol=tol) > 0
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
- def _det(xvert, yvert):
99
- """Compute twice the area of the triangle defined by points with using
100
- determinant formula.
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
- xvert -- A vector of nodal x-coords (array-like).
105
- yvert -- A vector of nodal y-coords (array-like).
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
- Output parameters:
108
- Twice the area of the triangle defined by the points.
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
- _det is positive if points define polygon in anticlockwise order.
113
- _det is negative if points define polygon in clockwise order.
114
- _det is zero if at least two of the points are concident or if
115
- all points are collinear.
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
- """Polygon object.
127
- Input parameters:
128
- x -- A sequence of nodal x-coords.
129
- y -- A sequence of nodal y-coords.
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
- """Check if point is inside a general polygon.
190
+ """
191
+ Check if a point or set of points are inside the polygon.
151
192
 
152
- Input parameters:
153
- xpoint -- The x-coord of the point to be tested.
154
- ypoint -- The y-coords of the point to be tested.
155
- smalld -- A small float number.
193
+ Parameters
194
+ ----------
195
+ xpoint : :class:`float` or array_like
196
+ Array of x-coordinates of the points to be tested.
156
197
 
157
- xpoint and ypoint could be scalars or array-like sequences.
198
+ ypoint : :class:`float` or array_like
199
+ Array of y-coordinates of the points to be tested.
158
200
 
159
- Output parameters:
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
- mindst -- The distance from the point to the nearest point of the
162
- polygon.
163
- If mindst < 0 then point is outside the polygon.
164
- If mindst = 0 then point in on a side of the polygon.
165
- If mindst > 0 then point is inside the polygon.
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
- An improved version of the algorithm of Nordbeck and Rydstedt.
170
- REF: SLOAN, S.W. (1985): A point-in-polygon program. Adv. Eng.
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
- """Virtual base class for grid geometries.
306
+ """
307
+ Abstract base class for grid geometries.
262
308
 
263
- Defines the common interface for all grid classes and
264
- provides facilities to query grid properties and related data. This class
265
- may be used as a base for specialized grid implementations. It should not
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
- #: Overall number of grid points. Readonly; is set automatically when
270
- #: other grid defining properties are set
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
- #: Shape of grid. Readonly, gives the shape as tuple, useful for cartesian
274
- #: grids
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
- #: Grid positions as (3, :attr:`size`) array of floats, without invalid
278
- #: microphones; readonly.
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
- # internal identifier
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
- """Returns the digest of the grid object."""
331
+ """Generate a unique digest for the grid."""
287
332
 
288
- # 'digest' is a placeholder for other properties in derived classes,
289
- # necessary to trigger the depends on mechanism
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
- """Returns the number of grid points."""
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
- """Returns the shape of the grid as a Tuple."""
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
- """Returns the grid positions as (3, size) array of floats."""
349
+ """Return the grid positions as array of floats, shape (3, :attr:`size`)."""
305
350
 
306
351
  def subdomain(self, sector):
307
- """Queries the indices for a subdomain in the grid.
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
- 2-tuple of arrays of integers or of numpy slice objects
319
- The indices that can be used to mask/select the grid subdomain from
320
- an array with the same shape as the grid.
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
- """Provides a cartesian 2D grid for the beamforming results.
381
+ """
382
+ Provides a 2D Cartesian grid for beamforming results.
333
383
 
334
- The grid has square or nearly square cells and is on a plane perpendicular
335
- to the z-axis. It is defined by lower and upper x- and y-limits and the
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, defaults to -1.
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, defaults to 1.
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, defaults to -1.
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, defaults to 1.
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 co-ordinate that defines the grid, defaults to 1.
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 cell side length for the grid, defaults to 0.1.
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, readonly.
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, readonly.
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
- # internal identifier
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
- """Queries the indices for a grid point near a certain co-ordinate.
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, y : float
421
- The co-ordinates for which the indices are queried.
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
- 2-tuple of integers
426
- The indices that give the grid point nearest to the given x, y
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
- """Queries the indices for a subdomain in the grid.
485
+ """
486
+ Find the indices of a subdomain in the grid.
442
487
 
443
- Allows either rectangular, circular or polygonial subdomains.
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
- x1, y1, x2, y2, ... : float
450
- If three parameters are given, then a circular sector is assumed
451
- that is given by its center (x1, y1) and the radius x2.
452
- If four parameters are given, then a rectangular sector is
453
- assumed that is given by two corners (x1, y1) and (x2, y2).
454
- If more parameters are given, the subdomain is assumed to have
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
- 2-tuple of arrays of integers or of numpy slice objects
460
- The indices that can be used to mask/select the grid subdomain from
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
- """The extension of the grid in pylab.imshow compatible form.
543
+ """
544
+ Return the grid's extension in :obj:`matplotlib.pyplot.imshow` compatible form.
504
545
 
505
546
  Returns
506
547
  -------
507
- 4-tuple of floats
508
- The extent of the grid as a tuple of x_min, x_max, y_min, y_max)
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
- """Provides a cartesian 3D grid for the beamforming results.
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, defaults to -1.
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, defaults to 1.
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, readonly.
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, readonly.
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 x-axis, readonly.
534
- nzsteps = Property(desc='number of grid points along x-axis')
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
- #: increments in all 3 dimensions) or a (3,) array of floats with
541
- #: respective increments in x,y, and z-direction (in m).
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
- # internal identifier
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
- """Queries the indices for a grid point near a certain co-ordinate.
664
+ """
665
+ Return the indices for a grid point near a certain coordinate.
617
666
 
618
- This can be used to query results or co-ordinates at/near a certain
619
- co-ordinate.
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 co-ordinates for which the indices is queried.
672
+ x, y, z : :class:`float`
673
+ The coordinates for which the indices is queried.
625
674
 
626
675
  Returns
627
676
  -------
628
- 3-tuple of integers
629
- The indices that give the grid point nearest to the given x, y, z
630
- co-ordinates from an array with the same shape as the grid.
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
- """Queries the indices for a subdomain in the grid.
717
+ """
718
+ Return the indices for a subdomain in the grid.
653
719
 
654
- Allows box-shaped subdomains. This can be used to
655
- mask or to query results from a certain sector or subdomain.
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
- (x1,y1,z1) and (x2,y2,z2).
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-tuple of numpy slice objects
666
- The indices that can be used to mask/select the grid subdomain from
667
- an array with the same shape as the grid.
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
- """Loads a 3D grid from xml file."""
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
- # internal identifier
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
- """Import the the grid point locations from .xml file when :attr:`file` changes."""
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
- """Class for Line grid geometries."""
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
- #: Staring point of the Grid
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
- #: Vector to define the orientation of the line source
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
- #: Vector to define the length of the line source in meter
969
+ #: Total length of the line. Default is ``1.0``.
735
970
  length = Float(1, desc='length of the line source')
736
971
 
737
- #:number of grid points.
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
- #: Overall number of grid points. Readonly; is set automatically when
741
- #: other grid defining properties are set
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
- #: Grid positions as (3, :attr:`size`) array of floats, without invalid
745
- #: microphones; readonly.
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
- """Base class for merging different grid geometries."""
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
- #: List of Grids to be merged
783
- #: each grid gets a new subdomain in the new grid
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
- # internal identifier
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
- """Base class for all sector types.
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
- Defines the common interface for all tbdsector classes. This class
834
- may be used as a base for diverse sector implementations. If used
835
- directly, it implements a sector encompassing the whole grid.
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
- """Queries whether the coordinates in a given array lie within the
840
- defined sector.
841
- For this sector type, any position is valid.
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 : array of floats
846
- Array with the shape 3x[number of gridpoints] containing the
847
- grid positions
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
- array of bools with as many entries as columns in pos
852
- Array indicating which of the given positions lie within the
853
- given sector
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
- """Base class for single sector types.
1141
+ """
1142
+ Base class for single sector types.
861
1143
 
862
- Defines the common interface for all single sector classes. This class
863
- may be used as a base for diverse single sector implementations. If used
864
- directly, it implements a sector encompassing the whole grid.
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
- #: Boolean flag, if 'True' (default), grid points lying on the sector border are included.
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
- #: Absolute tolerance for sector border
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
- #: Boolean flag, if 'True' (default), the nearest grid point is returned if None is inside the
874
- #: sector.
875
- default_nearest = Bool(True, desc='return nearest grid point to center of none inside sector')
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
- """Class for defining a rectangular sector.
1170
+ """
1171
+ Class for defining a rectangular sector.
880
1172
 
881
- Can be used for 2D Grids for defining a rectangular sector or
882
- for 3D grids for a rectangular cylinder sector parallel to the z-axis.
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 lower x position of the rectangle
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 upper x position of the rectangle
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 lower y position of the rectangle
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 upper y position of the rectangle
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
- """Queries whether the coordinates in a given array lie within the
899
- rectangular sector.
900
- If no coordinate is inside, the nearest one to the rectangle center
901
- is returned if :attr:`~Sector.default_nearest` is True.
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
- Array with the shape 3x[number of gridpoints] containing the
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
- array of bools with as many entries as columns in pos
912
- Array indicating which of the given positions lie within the
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
- """Class for defining a cuboid sector.
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
- Can be used for 3D Grids for defining a cuboid sector.
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
- """Queries whether the coordinates in a given array lie within the
963
- rectangular sector.
964
- If no coordinate is inside, the nearest one to the rectangle center
965
- is returned if :attr:`~Sector.default_nearest` is True.
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
- Array with the shape 3x[number of gridpoints] containing the
971
- grid positions
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
- array of bools with as many entries as columns in pos
976
- Array indicating which of the given positions lie within the
977
- given sector
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
- """Class for defining a circular sector.
1344
+ """
1345
+ Class for defining a circular sector.
1021
1346
 
1022
- Can be used for 2D Grids for defining a circular sector or
1023
- for 3D grids for a cylindrical sector parallel to the z-axis.
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
- #: radius of the circle
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
- """Queries whether the coordinates in a given array lie within the
1037
- circular sector.
1038
- If no coordinate is inside, the nearest one outside is returned
1039
- if :attr:`~Sector.default_nearest` is True.
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
- Array with the shape 3x[number of gridpoints] containing the
1045
- grid positions
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
- array of bools with as many entries as columns in pos
1050
- Array indicating which of the given positions lie within the
1051
- given sector
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
- """Class for defining a polygon sector.
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
- Can be used for 2D Grids for defining a polygon sector.
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
- # x1, y1, x2, y2, ... xn, yn :
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
- """Queries whether the coordinates in a given array lie within the polygon sector. If no
1076
- coordinate is inside, the nearest one to the rectangle center is returned if
1077
- :attr:`~Sector.default_nearest` is True.
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
- Array with the shape 3x[number of gridpoints] containing the
1083
- grid positions
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
- array of bools with as many entries as columns in pos
1088
- Array indicating which of the given positions lie within the
1089
- given sector
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
- """Class for defining a convex hull sector.
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
- Can be used for 2D Grids for defining a convex hull sector.
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
- # x1, y1, x2, y2, ... xn, yn :
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
- """Queries whether the coordinates in a given array lie within the
1115
- convex sector.
1116
- If no coordinate is inside, the nearest one to the rectangle center
1117
- is returned if :attr:`~Sector.default_nearest` is True.
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 with the shape 3x[number of gridpoints] containing the
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
- array of bools with as many entries as columns in pos
1128
- Array indicating which of the given positions lie within the
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
- """Class for defining a sector consisting of multiple sectors.
1534
+ """
1535
+ Class for defining a sector consisting of multiple sectors.
1144
1536
 
1145
- Can be used to sum over different sectors. Takes a list of sectors
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:`acoular.grids.Sector` objects
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
- """Queries whether the coordinates in a given array lie within any
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
- Array with the shape 3x[number of gridpoints] containing the
1162
- grid positions
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
- array of bools with as many entries as columns in pos
1167
- Array indicating which of the given positions lie within the
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)