acoular 25.4__py3-none-any.whl → 25.10__py3-none-any.whl

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