emerge 0.5.1__py3-none-any.whl → 0.5.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.

Potentially problematic release.


This version of emerge might be problematic. Click here for more details.

Files changed (54) hide show
  1. emerge/_emerge/bc.py +14 -20
  2. emerge/_emerge/const.py +5 -0
  3. emerge/_emerge/cs.py +2 -2
  4. emerge/_emerge/elements/femdata.py +14 -14
  5. emerge/_emerge/elements/index_interp.py +1 -1
  6. emerge/_emerge/elements/ned2_interp.py +1 -1
  7. emerge/_emerge/elements/nedelec2.py +4 -4
  8. emerge/_emerge/elements/nedleg2.py +10 -10
  9. emerge/_emerge/geo/horn.py +1 -1
  10. emerge/_emerge/geo/modeler.py +18 -19
  11. emerge/_emerge/geo/operations.py +13 -10
  12. emerge/_emerge/geo/pcb.py +180 -82
  13. emerge/_emerge/geo/pcb_tools/calculator.py +2 -2
  14. emerge/_emerge/geo/pcb_tools/macro.py +14 -13
  15. emerge/_emerge/geo/pmlbox.py +1 -1
  16. emerge/_emerge/geometry.py +47 -33
  17. emerge/_emerge/logsettings.py +15 -16
  18. emerge/_emerge/material.py +15 -11
  19. emerge/_emerge/mesh3d.py +81 -59
  20. emerge/_emerge/mesher.py +26 -21
  21. emerge/_emerge/mth/integrals.py +1 -1
  22. emerge/_emerge/mth/pairing.py +2 -2
  23. emerge/_emerge/periodic.py +34 -31
  24. emerge/_emerge/physics/microwave/adaptive_freq.py +15 -16
  25. emerge/_emerge/physics/microwave/assembly/assembler.py +120 -93
  26. emerge/_emerge/physics/microwave/assembly/curlcurl.py +1 -8
  27. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
  28. emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
  29. emerge/_emerge/physics/microwave/microwave_3d.py +71 -44
  30. emerge/_emerge/physics/microwave/microwave_bc.py +206 -117
  31. emerge/_emerge/physics/microwave/microwave_data.py +36 -38
  32. emerge/_emerge/physics/microwave/sc.py +26 -26
  33. emerge/_emerge/physics/microwave/simjob.py +20 -15
  34. emerge/_emerge/physics/microwave/sparam.py +12 -12
  35. emerge/_emerge/physics/microwave/touchstone.py +1 -1
  36. emerge/_emerge/plot/display.py +12 -6
  37. emerge/_emerge/plot/pyvista/display.py +44 -39
  38. emerge/_emerge/plot/pyvista/display_settings.py +1 -1
  39. emerge/_emerge/plot/simple_plots.py +15 -15
  40. emerge/_emerge/selection.py +35 -39
  41. emerge/_emerge/simmodel.py +41 -47
  42. emerge/_emerge/simulation_data.py +24 -15
  43. emerge/_emerge/solve_interfaces/cudss_interface.py +238 -0
  44. emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
  45. emerge/_emerge/solver.py +314 -136
  46. emerge/cli.py +1 -1
  47. emerge/lib.py +245 -248
  48. {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/METADATA +5 -1
  49. emerge-0.5.3.dist-info/RECORD +83 -0
  50. emerge/_emerge/plot/grapher.py +0 -93
  51. emerge-0.5.1.dist-info/RECORD +0 -82
  52. {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/WHEEL +0 -0
  53. {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/entry_points.txt +0 -0
  54. {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -22,7 +22,7 @@ from ...selection import FaceSelection, DomainSelection, EdgeSelection, Selectio
22
22
  from ...physics.microwave.microwave_bc import PortBC, ModalPort
23
23
  import numpy as np
24
24
  import pyvista as pv
25
- from typing import Iterable, Literal, Callable
25
+ from typing import Iterable, Literal, Callable, Any
26
26
  from ..display import BaseDisplay
27
27
  from .display_settings import PVDisplaySettings
28
28
  from matplotlib.colors import ListedColormap
@@ -155,7 +155,7 @@ def _select(obj: GeoObject | Selection) -> Selection:
155
155
  return obj.select
156
156
  return obj
157
157
 
158
- def _merge(lst: list[GeoObject | Selection]) -> Selection:
158
+ def _merge(lst: Iterable[GeoObject | Selection]) -> Selection:
159
159
  selections = [_select(item) for item in lst]
160
160
  dim = selections[0].dim
161
161
  all_tags = []
@@ -168,6 +168,8 @@ def _merge(lst: list[GeoObject | Selection]) -> Selection:
168
168
  return FaceSelection(all_tags)
169
169
  elif dim==3:
170
170
  return DomainSelection(all_tags)
171
+ else:
172
+ return Selection(all_tags)
171
173
 
172
174
  class _AnimObject:
173
175
  """ A private class containing the required information for plot items in a view
@@ -199,7 +201,7 @@ class PVDisplay(BaseDisplay):
199
201
  self._stop: bool = False
200
202
  self._objs: list[_AnimObject] = []
201
203
  self._do_animate: bool = False
202
- self._Nsteps: int = None
204
+ self._Nsteps: int = 0
203
205
  self._fps: int = 25
204
206
  self._ruler: ScreenRuler = ScreenRuler(self, 0.001)
205
207
  self._selector: ScreenSelector = ScreenSelector(self)
@@ -208,10 +210,11 @@ class PVDisplay(BaseDisplay):
208
210
 
209
211
  self._plot = pv.Plotter()
210
212
 
211
- self._plot.add_key_event("m", self.activate_ruler)
212
- self._plot.add_key_event("f", self.activate_object)
213
+ self._plot.add_key_event("m", self.activate_ruler) # type: ignore
214
+ self._plot.add_key_event("f", self.activate_object) # type: ignore
213
215
 
214
216
  self._ctr: int = 0
217
+
215
218
  def activate_ruler(self):
216
219
  self._plot.disable_picking()
217
220
  self._selector.turn_off()
@@ -310,7 +313,7 @@ class PVDisplay(BaseDisplay):
310
313
  points = self._mesh.nodes.T
311
314
  return pv.UnstructuredGrid(cells, celltypes, points)
312
315
 
313
- def mesh(self, obj: GeoObject | Selection | Iterable) -> pv.UnstructuredGrid:
316
+ def mesh(self, obj: GeoObject | Selection | Iterable) -> pv.UnstructuredGrid | None:
314
317
  if isinstance(obj, Iterable):
315
318
  obj = _merge(obj)
316
319
  else:
@@ -320,11 +323,13 @@ class PVDisplay(BaseDisplay):
320
323
  return self.mesh_volume(obj)
321
324
  elif isinstance(obj, FaceSelection):
322
325
  return self.mesh_surface(obj)
326
+ else:
327
+ return None
323
328
 
324
329
  ## OBLIGATORY METHODS
325
- def add_object(self, obj: GeoObject | Selection | Iterable, *args, **kwargs):
326
- kwargs = setdefault(kwargs, color=obj.color_rgb, opacity=obj.opacity, silhouette=True)
327
- self._plot.add_mesh(self.mesh(obj), pickable=True, *args, **kwargs)
330
+ def add_object(self, obj: GeoObject | Selection, *args, **kwargs):
331
+ kwargs = setdefault(kwargs, color=obj.color_rgb, opacity=obj.opacity, silhouette=True, pickable=True)
332
+ self._plot.add_mesh(self.mesh(obj), *args, **kwargs)
328
333
 
329
334
  def add_scatter(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray):
330
335
  """Adds a scatter point cloud
@@ -342,8 +347,8 @@ class PVDisplay(BaseDisplay):
342
347
  dv=(0,0,0),
343
348
  XYZ=None,
344
349
  field: Literal['E','H'] = 'E',
345
- k0: float = None,
346
- mode_number: int = None) -> pv.UnstructuredGrid:
350
+ k0: float | None = None,
351
+ mode_number: int | None = None) -> None:
347
352
 
348
353
  if XYZ:
349
354
  X,Y,Z = XYZ
@@ -408,10 +413,10 @@ class PVDisplay(BaseDisplay):
408
413
  field: np.ndarray,
409
414
  scale: Literal['lin','log','symlog'] = 'lin',
410
415
  cmap: cmap_names = 'coolwarm',
411
- clim: tuple[float, float] = None,
416
+ clim: tuple[float, float] | None = None,
412
417
  opacity: float = 1.0,
413
418
  symmetrize: bool = True,
414
- _fieldname: str = None,
419
+ _fieldname: str | None = None,
415
420
  **kwargs,):
416
421
  """Add a surface plot to the display
417
422
  The X,Y,Z coordinates must be a 2D grid of data points. The field must be a real field with the same size.
@@ -460,11 +465,11 @@ class PVDisplay(BaseDisplay):
460
465
  kwargs = setdefault(kwargs, cmap=cmap, clim=clim, opacity=opacity, pickable=False, multi_colors=True)
461
466
  actor = self._plot.add_mesh(grid, scalars=name, **kwargs)
462
467
 
463
- if self._animate:
468
+ if self._do_animate:
464
469
  def on_update(obj: _AnimObject, phi: complex):
465
470
  field = obj.T(np.real(obj.field*phi))
466
471
  obj.grid['anim'] = field
467
- self._objs.append(_AnimObject(field_flat, T, grid, actor, on_update))
472
+ self._objs.append(_AnimObject(field_flat, T, grid, actor, on_update)) # type: ignore
468
473
 
469
474
 
470
475
  def add_title(self, title: str) -> None:
@@ -481,14 +486,14 @@ class PVDisplay(BaseDisplay):
481
486
  def add_text(self, text: str,
482
487
  color: str = 'black',
483
488
  position: Literal['lower_left', 'lower_right', 'upper_left', 'upper_right', 'lower_edge', 'upper_edge', 'right_edge', 'left_edge']='upper_right',
484
- abs_position: tuple[float, float, float] = None):
489
+ abs_position: tuple[float, float, float] | None = None):
485
490
  viewport = False
486
491
  if abs_position is not None:
487
- position = abs_position
492
+ final_position = abs_position
488
493
  viewport = True
489
494
  self._plot.add_text(
490
495
  text,
491
- position=position,
496
+ position=final_position,
492
497
  color=color,
493
498
  font_size=18,
494
499
  viewport=viewport)
@@ -496,7 +501,7 @@ class PVDisplay(BaseDisplay):
496
501
  def add_quiver(self, x: np.ndarray, y: np.ndarray, z: np.ndarray,
497
502
  dx: np.ndarray, dy: np.ndarray, dz: np.ndarray,
498
503
  scale: float = 1,
499
- color: tuple[float, float, float] = None,
504
+ color: tuple[float, float, float] | None = None,
500
505
  scalemode: Literal['lin','log'] = 'lin'):
501
506
  """Add a quiver plot to the display
502
507
 
@@ -562,18 +567,18 @@ class PVDisplay(BaseDisplay):
562
567
  grid = pv.StructuredGrid(X,Y,Z)
563
568
  field = V.flatten(order='F')
564
569
  grid['anim'] = np.real(field)
565
- levels = np.linspace(vmin, vmax, Nlevels)
570
+ levels = list(np.linspace(vmin, vmax, Nlevels))
566
571
  contour = grid.contour(isosurfaces=levels)
567
572
  actor = self._plot.add_mesh(contour, opacity=0.25, cmap=cmap, pickable=False)
568
573
 
569
- if self._animate:
574
+ if self._do_animate:
570
575
  def on_update(obj: _AnimObject, phi: complex):
571
576
  new_vals = np.real(obj.field * phi)
572
577
  obj.grid['anim'] = new_vals
573
578
  new_contour = obj.grid.contour(isosurfaces=levels)
574
- obj.actor.GetMapper().SetInputData(new_contour)
579
+ obj.actor.GetMapper().SetInputData(new_contour) # type: ignore
575
580
 
576
- self._objs.append(_AnimObject(field, lambda x: x, grid, actor, on_update))
581
+ self._objs.append(_AnimObject(field, lambda x: x, grid, actor, on_update)) # type: ignore
577
582
 
578
583
  def _add_aux_items(self) -> None:
579
584
  saved_camera = {
@@ -591,8 +596,8 @@ class PVDisplay(BaseDisplay):
591
596
  plane = pv.Plane(
592
597
  center=(0, 0, 0),
593
598
  direction=(1, 0, 0), # normal vector pointing along +X
594
- i_size=length,
595
- j_size=length,
599
+ i_size=length, # type: ignore
600
+ j_size=length, # type: ignore
596
601
  i_resolution=1,
597
602
  j_resolution=1
598
603
  )
@@ -617,8 +622,8 @@ class PVDisplay(BaseDisplay):
617
622
  plane = pv.Plane(
618
623
  center=(0, 0, 0),
619
624
  direction=(0, 1, 0), # normal vector pointing along +X
620
- i_size=length,
621
- j_size=length,
625
+ i_size=length, # type: ignore
626
+ j_size=length, # type: ignore
622
627
  i_resolution=1,
623
628
  j_resolution=1
624
629
  )
@@ -642,8 +647,8 @@ class PVDisplay(BaseDisplay):
642
647
  plane = pv.Plane(
643
648
  center=(0, 0, 0),
644
649
  direction=(0, 0, 1), # normal vector pointing along +X
645
- i_size=length,
646
- j_size=length,
650
+ i_size=length, # type: ignore
651
+ j_size=length, # type: ignore
647
652
  i_resolution=1,
648
653
  j_resolution=1
649
654
  )
@@ -776,11 +781,11 @@ class PVDisplay(BaseDisplay):
776
781
 
777
782
  if self.set.add_light:
778
783
  light = pv.Light()
779
- light.set_direction_angle(*self.set.light_angle)
784
+ light.set_direction_angle(*self.set.light_angle) # type: ignore
780
785
  self._plot.add_light(light)
781
786
 
782
- self._plot.set_background(self.set.background_bottom, top=self.set.background_top)
783
- self._plot.add_axes()
787
+ self._plot.set_background(self.set.background_bottom, top=self.set.background_top) # type: ignore
788
+ self._plot.add_axes() # type: ignore
784
789
 
785
790
  self._plot.camera.position = saved_camera["position"]
786
791
  self._plot.camera.focal_point = saved_camera["focal_point"]
@@ -837,7 +842,7 @@ class ScreenSelector:
837
842
  celltypes = np.full(ntris, fill_value=pv.CellType.TRIANGLE, dtype=np.uint8)
838
843
  points = self.disp._mesh.nodes.T
839
844
  grid = pv.UnstructuredGrid(cells, celltypes, points)
840
- grid._tag = key
845
+ grid._tag = key # type: ignore
841
846
  self.grids.append(grid)
842
847
  self.surfs[key] = points[nodes,:].T
843
848
 
@@ -856,14 +861,14 @@ class ScreenSelector:
856
861
  meany = np.mean(ys)
857
862
  meanz = np.mean(zs)
858
863
  data = (meanx, meany, meanz, min(xs), min(ys), min(zs), max(xs), max(ys), max(zs))
859
- encoded = encode_data(data)
864
+ encoded = encode_data(data) #type: ignore
860
865
  print(f'Face code key={key}: ', encoded)
861
866
 
862
867
  self.disp._plot.enable_mesh_picking(callback, style='surface', left_clicking=True, use_actor=True)
863
868
 
864
869
  def turn_off(self) -> None:
865
870
  for actor in self.select_actors:
866
- self.disp._plot.remove_actor(actor)
871
+ self.disp._plot.remove_actor(actor) # type: ignore
867
872
  self.select_actors = []
868
873
  for actor in self.original_actors:
869
874
  if isinstance(actor, pv.Text):
@@ -876,9 +881,9 @@ class ScreenRuler:
876
881
  def __init__(self, display: PVDisplay, min_length: float):
877
882
  self.disp: PVDisplay = display
878
883
  self.points: list[tuple] = [(0,0,0),(0,0,0)]
879
- self.text: pv.Text = None
880
- self.ruler = None
881
- self.state = False
884
+ self.text: pv.Text | None = None
885
+ self.ruler: Any = None
886
+ self.state: bool = False
882
887
  self.min_length: float = min_length
883
888
 
884
889
  @freeze
@@ -916,7 +921,7 @@ class ScreenRuler:
916
921
 
917
922
  def set_ruler(self) -> None:
918
923
  if self.ruler is None:
919
- self.ruler = self.disp._plot.add_ruler(self.points[0], self.points[1], title=f'{1000*self.dist:.2f}mm')
924
+ self.ruler = self.disp._plot.add_ruler(self.points[0], self.points[1], title=f'{1000*self.dist:.2f}mm') # type: ignore
920
925
  else:
921
926
  p1 = self.ruler.GetPositionCoordinate()
922
927
  p2 = self.ruler.GetPosition2Coordinate()
@@ -17,7 +17,7 @@ class PVDisplaySettings:
17
17
  self.show_zgrid: bool = True
18
18
  self.grid_line_width: float = 1.0
19
19
  self.add_light: bool = False
20
- self.light_angle: float = (20, -20)
20
+ self.light_angle: tuple[float, float] = (20., -20.)
21
21
  self.cast_shadows: bool = True
22
22
  self.background_bottom: str = "#c0d2e8"
23
23
  self.background_top: str = "#ffffff"
@@ -36,7 +36,7 @@ ggplot_styles = {
36
36
 
37
37
  plt.rcParams.update(ggplot_styles)
38
38
 
39
- def _gen_grid(xs: tuple, ys: tuple, N = 201) -> list[np.ndarray]:
39
+ def _gen_grid(xs: tuple, ys: tuple, N = 201) -> list[tuple[np.ndarray, np.ndarray]]:
40
40
  """Generate a grid of lines for the Smith Chart
41
41
 
42
42
  Args:
@@ -279,18 +279,18 @@ def smith(f: np.ndarray, S: np.ndarray) -> None:
279
279
  def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
280
280
  dblim=[-40, 5],
281
281
  xunit="GHz",
282
- levelindicator: int | float =None,
282
+ levelindicator: int | float | None = None,
283
283
  noise_floor=-150,
284
- fill_areas: list[tuple]= None,
285
- spec_area: list[tuple[float]] = None,
284
+ fill_areas: list[tuple] | None = None,
285
+ spec_area: list[tuple[float,...]] | None = None,
286
286
  unwrap_phase=False,
287
287
  logx: bool = False,
288
- labels: list[str] = None,
289
- linestyles: list[str] = None,
290
- colorcycle: list[int] = None,
291
- filename: str = None,
288
+ labels: list[str] | None = None,
289
+ linestyles: list[str] | None = None,
290
+ colorcycle: list[int] | None = None,
291
+ filename: str | None = None,
292
292
  show_plot: bool = True,
293
- figdata: tuple = None) -> None:
293
+ figdata: tuple | None = None) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
294
294
  """Plot S-parameters in dB and phase
295
295
 
296
296
  Args:
@@ -321,7 +321,7 @@ def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
321
321
  if colorcycle is None:
322
322
  colorcycle = [i for i, S in enumerate(S)]
323
323
 
324
- unitdivider = {"MHz": 1e6, "GHz": 1e9, "kHz": 1e3}
324
+ unitdivider: dict[str, float] = {"MHz": 1e6, "GHz": 1e9, "kHz": 1e3}
325
325
  fnew = f / unitdivider[xunit]
326
326
 
327
327
  if figdata is None:
@@ -372,14 +372,14 @@ def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
372
372
  # Configure magnitude plot (ax_mag)
373
373
  ax_mag.set_ylabel("Magnitude (dB)")
374
374
  ax_mag.set_xlabel(f"Frequency ({xunit})")
375
- ax_mag.axis([min(fnew), max(fnew), dblim[0], max(maxy*1.1,dblim[1])])
375
+ ax_mag.axis([min(fnew), max(fnew), dblim[0], max(maxy*1.1,dblim[1])]) # type: ignore
376
376
  ax_mag.axhline(y=0, color="k", linewidth=1)
377
377
  ax_mag.xaxis.set_minor_locator(tck.AutoMinorLocator(2))
378
378
  ax_mag.yaxis.set_minor_locator(tck.AutoMinorLocator(2))
379
379
  # Configure phase plot (ax_phase)
380
380
  ax_phase.set_ylabel("Phase (degrees)")
381
381
  ax_phase.set_xlabel(f"Frequency ({xunit})")
382
- ax_phase.axis([min(fnew), max(fnew), minphase, maxphase])
382
+ ax_phase.axis([min(fnew), max(fnew), minphase, maxphase]) # type: ignore
383
383
  ax_phase.xaxis.set_minor_locator(tck.AutoMinorLocator(2))
384
384
  ax_phase.yaxis.set_minor_locator(tck.AutoMinorLocator(2))
385
385
  if logx:
@@ -532,9 +532,9 @@ def plot_ff_polar(
532
532
  markers = _broadcast(markers, None) if markers is not None else [None] * n_series
533
533
 
534
534
  fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
535
- ax.set_theta_zero_location(zero_location)
536
- ax.set_theta_direction(-1 if clockwise else 1)
537
- ax.set_rlabel_position(rlabel_angle)
535
+ ax.set_theta_zero_location(zero_location) # type: ignore
536
+ ax.set_theta_direction(-1 if clockwise else 1) # type: ignore
537
+ ax.set_rlabel_position(rlabel_angle) # type: ignore
538
538
 
539
539
  for i, Ei in enumerate(E_list):
540
540
  mag = np.abs(Ei)
@@ -16,13 +16,13 @@
16
16
  # <https://www.gnu.org/licenses/>.
17
17
 
18
18
  from __future__ import annotations
19
- import gmsh
19
+ import gmsh # type: ignore
20
20
  import numpy as np
21
- from scipy.spatial import ConvexHull
21
+ from scipy.spatial import ConvexHull # type: ignore
22
22
  from .cs import Axis, CoordinateSystem, _parse_vector, Plane
23
- from typing import Callable, TypeVar
23
+ from typing import Callable, TypeVar, Iterable, Any
24
24
 
25
- def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, np.ndarray]:
25
+ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, Any]:
26
26
  """Tries to find a rectangle as convex-hull of a set of points with a given normal vector.
27
27
 
28
28
  Args:
@@ -66,9 +66,9 @@ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, np
66
66
  xmax, ymax = rot.max(axis=0)
67
67
  area = (xmax-xmin)*(ymax-ymin)
68
68
  if area < best[1]:
69
- best = (theta, area, (xmin,xmax,ymin,ymax), R)
69
+ best = (theta, area, (xmin,xmax,ymin,ymax), R) # type: ignore
70
70
 
71
- theta, _, (xmin,xmax,ymin,ymax), R = best
71
+ theta, _, (xmin,xmax,ymin,ymax), R = best # type: ignore
72
72
 
73
73
  # 6. rectangle axes in 3D
74
74
  u = np.cos(-theta)*e_x + np.sin(-theta)*e_y
@@ -172,9 +172,9 @@ class Selection:
172
172
 
173
173
  """
174
174
  dim: int = -1
175
- def __init__(self, tags: list[int] = None):
175
+ def __init__(self, tags: list[int] | set[int] | tuple[int] | None = None):
176
176
 
177
- self._tags: set[int] = {}
177
+ self._tags: set[int] = set()
178
178
 
179
179
  if tags is not None:
180
180
  if not isinstance(tags, (list,set,tuple)):
@@ -182,7 +182,7 @@ class Selection:
182
182
  self._tags = set(tags)
183
183
 
184
184
  @staticmethod
185
- def from_dim_tags(dim: int, tags: list[int]) -> DomainSelection | PointSelection | FaceSelection | EdgeSelection:
185
+ def from_dim_tags(dim: int, tags: list[int] | set[int]) -> Selection:
186
186
  if dim==0:
187
187
  return PointSelection(tags)
188
188
  elif dim==1:
@@ -198,7 +198,7 @@ class Selection:
198
198
  return list(self._tags)
199
199
 
200
200
  @property
201
- def color_rgb(self) -> tuple[int,int,int]:
201
+ def color_rgb(self) -> tuple[float, float, float]:
202
202
  return (0.5,0.5,1.0)
203
203
 
204
204
  @property
@@ -225,14 +225,14 @@ class Selection:
225
225
  return [gmsh.model.occ.getCenterOfMass(self.dim, tag) for tag in self.tags]
226
226
 
227
227
  @property
228
- def points(self) -> np.ndarray | list[np.ndarray]:
228
+ def points(self) -> list[np.ndarray]:
229
229
  '''A list of 3D coordinates of all nodes comprising the selection.'''
230
230
  points = gmsh.model.get_boundary(self.dimtags, recursive=True)
231
231
  coordinates = [gmsh.model.getValue(*p, []) for p in points]
232
232
  return coordinates
233
233
 
234
234
  @property
235
- def bounding_box(self) -> tuple[np.ndarray, np.ndarray]:
235
+ def bounding_box(self) -> tuple[Iterable, Iterable]:
236
236
  if len(self.tags)==1:
237
237
  return gmsh.model.occ.getBoundingBox(self.dim, self.tags[0])
238
238
  else:
@@ -248,7 +248,7 @@ class Selection:
248
248
  maxz = max(maxz, z1)
249
249
  return (minx, miny, minz), (maxx, maxy, maxz)
250
250
 
251
- def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
251
+ def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
252
252
  """Exclude points by evaluating a function(x,y,z)-> bool
253
253
 
254
254
  This modifies the selection such that the selection does not contain elements
@@ -266,10 +266,10 @@ class Selection:
266
266
  norm = axis.np
267
267
  include2 = [abs(gmsh.model.getNormal(tag, np.array([0,0]))@norm)<0.9 for tag in self.tags]
268
268
  include = [i1 for i1, i2 in zip(include, include2) if i1 and i2]
269
- self._tags = [t for incl, t in zip(include, self._tags) if incl]
269
+ self._tags = set([t for incl, t in zip(include, self._tags) if incl])
270
270
  return self
271
271
 
272
- def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
272
+ def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
273
273
  """Include points by evaluating a function(x,y,z)-> bool
274
274
 
275
275
  This modifies the selection such that the selection does not contain elements
@@ -287,7 +287,7 @@ class Selection:
287
287
  norm = axis.np
288
288
  include2 = [(gmsh.model.getNormal(tag, np.array([0,0]))@norm)>0.99 for tag in self.tags]
289
289
  include1 = [i1 for i1, i2 in zip(include1, include2) if i1 and i2]
290
- self._tags = [t for incl, t in zip(include1, self._tags) if incl]
290
+ self._tags = set([t for incl, t in zip(include1, self._tags) if incl])
291
291
  return self
292
292
 
293
293
  def __operable__(self, other: Selection) -> None:
@@ -295,30 +295,28 @@ class Selection:
295
295
  raise ValueError(f'Selection dimensions must be equal. Trying to operate on dim {self.dim} and {other.dim}')
296
296
  pass
297
297
 
298
- def __add__(self, other: TSelection) -> TSelection:
298
+ def __add__(self, other: Selection) -> Selection:
299
299
  self.__operable__(other)
300
- return Selection.from_dim_tags(self.dim, self._tags + other._tags)
300
+ return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
301
301
 
302
- def __and__(self, other: TSelection) -> TSelection:
302
+ def __and__(self, other: Selection) -> Selection:
303
303
  self.__operable__(other)
304
304
  return Selection.from_dim_tags(self.dim, self._tags.intersection(other._tags))
305
305
 
306
- def __or__(self, other: TSelection) -> TSelection:
306
+ def __or__(self, other: Selection) -> Selection:
307
307
  self.__operable__(other)
308
308
  return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
309
309
 
310
- def __sub__(self, other: TSelection) -> TSelection:
310
+ def __sub__(self, other: Selection) -> Selection:
311
311
  self.__operable__(other)
312
312
  return Selection.from_dim_tags(self.dim, self._tags.difference(other.tags))
313
-
314
-
315
313
 
316
314
  class PointSelection(Selection):
317
315
  """A Class representing a selection of points.
318
316
 
319
317
  """
320
318
  dim: int = 0
321
- def __init__(self, tags: list[int] = None):
319
+ def __init__(self, tags: list[int] | set [int] | None = None):
322
320
  super().__init__(tags)
323
321
 
324
322
  class EdgeSelection(Selection):
@@ -326,7 +324,7 @@ class EdgeSelection(Selection):
326
324
 
327
325
  """
328
326
  dim: int = 1
329
- def __init__(self, tags: list[int] = None):
327
+ def __init__(self, tags: list[int] | set [int] | None = None):
330
328
  super().__init__(tags)
331
329
 
332
330
  class FaceSelection(Selection):
@@ -334,7 +332,7 @@ class FaceSelection(Selection):
334
332
 
335
333
  """
336
334
  dim: int = 2
337
- def __init__(self, tags: list[int] = None):
335
+ def __init__(self, tags: list[int] | set [int] | None = None):
338
336
  super().__init__(tags)
339
337
 
340
338
  @property
@@ -355,14 +353,14 @@ class FaceSelection(Selection):
355
353
 
356
354
  pts3d = self.points
357
355
  normal = self.normal
358
- data = align_rectangle_frame(pts3d, normal)
356
+ data = align_rectangle_frame(np.array(pts3d), normal)
359
357
  plane = data['axes'][:2]
360
358
  origin = data['origin']
361
359
 
362
360
  cs = CoordinateSystem(Axis(plane[0]), Axis(plane[1]), Axis(data['axes'][2]), origin)
363
361
 
364
- size1 = np.linalg.norm(data['corners'][1] - data['corners'][0])
365
- size2 = np.linalg.norm(data['corners'][2] - data['corners'][0])
362
+ size1 = float(np.linalg.norm(data['corners'][1] - data['corners'][0]))
363
+ size2 = float(np.linalg.norm(data['corners'][2] - data['corners'][0]))
366
364
 
367
365
  if size1>size2:
368
366
  cs.swapxy()
@@ -382,7 +380,7 @@ class FaceSelection(Selection):
382
380
  a NxN numpy array of Y coordinates.
383
381
  Z: np.ndarray
384
382
  a NxN numpy array of Z coordinates'''
385
- coordset = []
383
+ coordset: list[tuple[np.ndarray, np.ndarray, np.ndarray]] = []
386
384
  for tag in self.tags:
387
385
  tags, coord, param = gmsh.model.mesh.getNodes(2, tag, includeBoundary=True)
388
386
 
@@ -419,17 +417,15 @@ class FaceSelection(Selection):
419
417
  Y = [c[1] for c in coordset]
420
418
  Z = [c[2] for c in coordset]
421
419
  if len(X) == 1:
422
- X = X[0]
423
- Y = Y[0]
424
- Z = Z[0]
425
- return X, Y, Z
420
+ return X[0], Y[0], Z[0]
421
+ return np.array(X), np.array(Y), np.array(Z)
426
422
 
427
423
  class DomainSelection(Selection):
428
424
  """A Class representing a selection of domains.
429
425
 
430
426
  """
431
427
  dim: int = 3
432
- def __init__(self, tags: list[int] = None):
428
+ def __init__(self, tags: list[int] | set[int] | None = None):
433
429
  super().__init__(tags)
434
430
 
435
431
  SELECT_CLASS: dict[int, type[Selection]] = {
@@ -501,7 +497,7 @@ class Selector:
501
497
  x: float,
502
498
  y: float,
503
499
  z: float,
504
- vector: tuple[float, float, float] | np.ndarray | Axis) -> FaceSelection | EdgeSelection | DomainSelection:
500
+ vector: tuple[float, float, float] | np.ndarray | Axis) -> Selection:
505
501
  '''Returns a list of selections that are in the layer defined by the plane normal vector and the point (x,y,z)
506
502
 
507
503
  The layer is specified by two infinite planes normal to the provided vector. The first plane is originated
@@ -529,7 +525,7 @@ class Selector:
529
525
  output = []
530
526
  for i, c in enumerate(coords):
531
527
  c_local = c - np.array([x,y,z])
532
- if 0 < np.dot(vector, c_local) < L:
528
+ if 0 < np.dot(vector, c_local) < L: # type: ignore
533
529
  output.append(dimtags[i][1])
534
530
  return SELECT_CLASS[self._current_dim](output)
535
531
 
@@ -565,8 +561,8 @@ class Selector:
565
561
  normals = [gmsh.model.get_normal(t, (0,0)) for d,t, in dimtags]
566
562
  tags = []
567
563
  for (d,t), o, n in zip(dimtags, coords, normals):
568
- normdist = np.abs((o-orig)@norm)
569
- dotnorm = np.abs(n@norm)
564
+ normdist = np.abs((o-orig) @ norm)
565
+ dotnorm = np.abs(n @ norm)
570
566
  if normdist < tolerance and dotnorm > 1-tolerance:
571
567
  tags.append(t)
572
568
  return FaceSelection(tags)