emerge 0.5.0__py3-none-any.whl → 0.5.2__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 (47) hide show
  1. emerge/_emerge/bc.py +11 -8
  2. emerge/_emerge/cs.py +2 -2
  3. emerge/_emerge/elements/femdata.py +14 -14
  4. emerge/_emerge/elements/index_interp.py +1 -1
  5. emerge/_emerge/elements/ned2_interp.py +1 -1
  6. emerge/_emerge/elements/nedelec2.py +4 -4
  7. emerge/_emerge/elements/nedleg2.py +9 -9
  8. emerge/_emerge/geo/horn.py +1 -1
  9. emerge/_emerge/geo/modeler.py +18 -19
  10. emerge/_emerge/geo/operations.py +13 -10
  11. emerge/_emerge/geo/pcb.py +70 -69
  12. emerge/_emerge/geo/pcb_tools/macro.py +14 -13
  13. emerge/_emerge/geo/pmlbox.py +1 -1
  14. emerge/_emerge/geometry.py +46 -32
  15. emerge/_emerge/logsettings.py +3 -3
  16. emerge/_emerge/material.py +11 -11
  17. emerge/_emerge/mesh3d.py +81 -59
  18. emerge/_emerge/mesher.py +26 -21
  19. emerge/_emerge/mth/pairing.py +2 -2
  20. emerge/_emerge/periodic.py +34 -31
  21. emerge/_emerge/physics/microwave/adaptive_freq.py +14 -11
  22. emerge/_emerge/physics/microwave/assembly/assembler.py +61 -57
  23. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
  24. emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
  25. emerge/_emerge/physics/microwave/microwave_3d.py +40 -20
  26. emerge/_emerge/physics/microwave/microwave_bc.py +114 -95
  27. emerge/_emerge/physics/microwave/microwave_data.py +33 -33
  28. emerge/_emerge/physics/microwave/simjob.py +12 -12
  29. emerge/_emerge/physics/microwave/sparam.py +12 -12
  30. emerge/_emerge/physics/microwave/touchstone.py +1 -1
  31. emerge/_emerge/plot/display.py +12 -6
  32. emerge/_emerge/plot/pyvista/display.py +44 -39
  33. emerge/_emerge/plot/pyvista/display_settings.py +1 -1
  34. emerge/_emerge/plot/simple_plots.py +15 -15
  35. emerge/_emerge/selection.py +35 -39
  36. emerge/_emerge/simmodel.py +29 -39
  37. emerge/_emerge/simulation_data.py +19 -14
  38. emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
  39. emerge/_emerge/solver.py +52 -52
  40. emerge/lib.py +243 -243
  41. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/METADATA +1 -1
  42. emerge-0.5.2.dist-info/RECORD +81 -0
  43. emerge/_emerge/plot/grapher.py +0 -93
  44. emerge-0.5.0.dist-info/RECORD +0 -82
  45. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/WHEEL +0 -0
  46. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/entry_points.txt +0 -0
  47. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/licenses/LICENSE +0 -0
emerge/_emerge/geo/pcb.py CHANGED
@@ -18,7 +18,6 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  import numpy as np
21
- from scipy.optimize import root, fmin
22
21
  import gmsh
23
22
 
24
23
  from ..cs import CoordinateSystem, GCS, Axis
@@ -30,7 +29,7 @@ from .operations import change_coordinate_system
30
29
  from .pcb_tools.macro import parse_macro
31
30
  from .pcb_tools.calculator import PCBCalculator
32
31
  from loguru import logger
33
- from typing import Literal, Callable
32
+ from typing import Literal, Callable, overload
34
33
  from dataclasses import dataclass
35
34
  import math
36
35
 
@@ -107,6 +106,10 @@ class RouteElement:
107
106
  self.direction: np.ndarray = None
108
107
  self.dirright: np.ndarray = None
109
108
 
109
+ @property
110
+ def xy(self) -> tuple[float, float]:
111
+ return self.x, self.y
112
+
110
113
  @property
111
114
  def nr(self) -> tuple[float,float]:
112
115
  return (self.x + self.dirright[0]*self.width/2, self.y + self.dirright[1]*self.width/2)
@@ -117,11 +120,11 @@ class RouteElement:
117
120
 
118
121
  @property
119
122
  def right(self) -> list[tuple[float, float]]:
120
- pass
123
+ raise NotImplementedError()
121
124
 
122
125
  @property
123
126
  def left(self) -> list[tuple[float, float]]:
124
- pass
127
+ raise NotImplementedError()
125
128
 
126
129
  def __eq__(self, other: RouteElement) -> bool:
127
130
  return approx(self.x, other.x) and approx(self.y, other.y) and (1-abs(np.sum(self.direction*other.direction)))<1e-8
@@ -158,7 +161,7 @@ class StripTurn(RouteElement):
158
161
  direction: tuple[float, float],
159
162
  angle: float,
160
163
  corner_type: str = 'round',
161
- champher_distance: float = None):
164
+ champher_distance: float | None = None):
162
165
  self.xold: float = x
163
166
  self.yold: float = y
164
167
  self.width: float = width
@@ -221,7 +224,8 @@ class StripTurn(RouteElement):
221
224
  y2 = yend - dist * self.direction[1]
222
225
 
223
226
  return [(x1, y1), (x2, y2), (xend, yend)]
224
-
227
+ else:
228
+ raise RouteException(f'Trying to route a StripTurn with an unknown corner type: {self.corner_type}')
225
229
  @property
226
230
  def left(self) -> list[tuple[float, float]]:
227
231
  if self.angle < 0:
@@ -258,6 +262,8 @@ class StripTurn(RouteElement):
258
262
  y2 = yend - dist * self.direction[1]
259
263
 
260
264
  return [(xend, yend), (x2, y2), (x1, y1)]
265
+ else:
266
+ raise RouteException(f'Trying to route a StripTurn with an unknown corner type: {self.corner_type}')
261
267
 
262
268
  ############################################################
263
269
  # THE STRIP PATH CLASS #
@@ -316,7 +322,7 @@ class StripPath:
316
322
  return self
317
323
 
318
324
  def straight(self, distance:
319
- float, width: float = None,
325
+ float, width: float | None = None,
320
326
  dx: float = 0,
321
327
  dy: float = 0) -> StripPath:
322
328
  """Add A straight section to the stripline.
@@ -381,7 +387,7 @@ class StripPath:
381
387
  return self
382
388
 
383
389
  def turn(self, angle: float,
384
- width: float = None,
390
+ width: float | None = None,
385
391
  corner_type: Literal['champher','square'] = 'champher') -> StripPath:
386
392
  """Adds a turn to the strip path.
387
393
 
@@ -419,13 +425,12 @@ class StripPath:
419
425
  Returns:
420
426
  StripPath: The current StripPath object.
421
427
  """
422
- self.pcb.store(name, self.end.x, self.end.y)
423
428
  self.pcb.stored_striplines[name] = self.end
424
429
  return self
425
430
 
426
431
  def split(self,
427
- direction: tuple[float, float] = None,
428
- width: float = None) -> StripPath:
432
+ direction: tuple[float, float] | None= None,
433
+ width: float | None= None) -> StripPath:
429
434
  """Split the current path in N new paths given by a new departure direction
430
435
 
431
436
  Args:
@@ -529,9 +534,9 @@ class StripPath:
529
534
  znew: float,
530
535
  radius: float,
531
536
  proceed: bool = True,
532
- direction: tuple[float, float] = None,
533
- width: float = None,
534
- extra: float = None,
537
+ direction: tuple[float, float] | None = None,
538
+ width: float | None = None,
539
+ extra: float | None = None,
535
540
  segments: int = 6) -> StripPath:
536
541
  """Adds a via to the circuit
537
542
 
@@ -576,13 +581,13 @@ class StripPath:
576
581
  return self
577
582
 
578
583
  def jump(self,
579
- dx: float = None,
580
- dy: float = None,
581
- width: float = None,
582
- direction: tuple[float, float] = None,
583
- gap: float = None,
584
- side: Literal['left','right'] = None,
585
- reverse: float = None) -> StripPath:
584
+ dx: float | None = None,
585
+ dy: float | None = None,
586
+ width: float | None = None,
587
+ direction: tuple[float, float] | None = None,
588
+ gap: float | None = None,
589
+ side: Literal['left','right'] | None = None,
590
+ reverse: float | None = None) -> StripPath:
586
591
  """Add an unconnected jump to the currenet stripline.
587
592
 
588
593
  The last stripline path will be terminated and a new one will be started based on the
@@ -630,8 +635,8 @@ class StripPath:
630
635
  return self.pcb.new(x, y, width, direction)
631
636
 
632
637
  def to(self, dest: tuple[float, float],
633
- arrival_dir: tuple[float, float] = None,
634
- arrival_margin: float = None,
638
+ arrival_dir: tuple[float, float] | None = None,
639
+ arrival_margin: float | None= None,
635
640
  angle_step: float = 90):
636
641
  """
637
642
  Extend the path from current end point to dest (x, y).
@@ -672,7 +677,7 @@ class StripPath:
672
677
  dt = max_t / 1000.0 # resolution of search
673
678
  found = False
674
679
  desired_q = None
675
- cand_dx = cand_dy = 0.0
680
+ #cand_dx = cand_dy = 0.0
676
681
 
677
682
  while t <= max_t:
678
683
  # candidate intercept point
@@ -700,8 +705,8 @@ class StripPath:
700
705
  raise RuntimeError("Could not find an intercept angle matching quantization")
701
706
 
702
707
  # 1) Perform initial quantized turn
703
- if abs(desired_q) > atol:
704
- self.turn(-desired_q)
708
+ if abs(desired_q) > atol: # type: ignore
709
+ self.turn(-desired_q) # type: ignore
705
710
  x0 = self.end.x
706
711
  y0 = self.end.y
707
712
  # compute new heading vector after turn
@@ -752,7 +757,7 @@ class StripPath:
752
757
 
753
758
  return self
754
759
 
755
- def macro(self, path: str, width: float = None, start_dir: tuple[float, float] = None) -> StripPath:
760
+ def macro(self, path: str, width: float | None = None, start_dir: tuple[float, float] | None = None) -> StripPath:
756
761
  r"""Parse an EMerge macro command string
757
762
 
758
763
  The start direction by default is the abslute current heading. If a specified heading is provided
@@ -803,7 +808,7 @@ class PCB:
803
808
  def __init__(self,
804
809
  thickness: float,
805
810
  unit: float = 0.001,
806
- cs: CoordinateSystem = None,
811
+ cs: CoordinateSystem | None = None,
807
812
  material: Material = AIR,
808
813
  layers: int = 2,
809
814
  ):
@@ -811,16 +816,16 @@ class PCB:
811
816
  self.thickness: float = thickness
812
817
  self._zs: np.ndarray = np.linspace(-self.thickness, 0, layers)
813
818
  self.material: Material = material
814
- self.width: float = None
815
- self.length: float = None
816
- self.origin: np.ndarray = None
819
+ self.width: float | None = None
820
+ self.length: float | None = None
821
+ self.origin: np.ndarray = np.array([0.,0.,0.])
817
822
  self.paths: list[StripPath] = []
818
823
  self.polies: list[PCBPoly] = []
819
824
 
820
825
  self.lumped_ports: list[StripLine] = []
821
826
  self.lumped_elements: list[GeoPolygon] = []
822
827
 
823
- self.unit = unit
828
+ self.unit: float = unit
824
829
 
825
830
  self.cs: CoordinateSystem = cs
826
831
  if self.cs is None:
@@ -864,7 +869,7 @@ class PCB:
864
869
  """
865
870
  return self._zs[layer-1]
866
871
 
867
- def _get_z(self, element: RouteElement) -> float:
872
+ def _get_z(self, element: RouteElement) -> float :
868
873
  """Return the z-height of a given Route Element
869
874
 
870
875
  Args:
@@ -876,38 +881,23 @@ class PCB:
876
881
  for path in self.paths:
877
882
  if path._has(element):
878
883
  return path.z
879
- return None
880
-
881
- def store(self, name: str, x: float, y:float):
882
- """Store the x,y coordinate pair one label provided by name
884
+ raise RouteException('Requesting z-height of route element that is not contained in a path.')
883
885
 
884
- Args:
885
- name (str): The corodinate label name
886
- x (float): The x-coordinate
887
- y (float): The y-coordinate
888
- """
889
- self.stored_coords[name] = (x,y)
890
-
891
- def load(self, name: str) -> tuple[float, float] | StripLine:
886
+ def load(self, name: str) -> StripLine:
892
887
  """Acquire the x,y, coordinate associated with the label name.
893
888
 
894
889
  Args:
895
890
  name (str): The name of the x,y coordinate
896
891
 
897
892
  """
898
- if name in self.stored_striplines and name in self.stored_coords:
899
- logger.warning(f'There is both a coordinate and stripline under the name {name}.')
900
- return self.stored_striplines[name]
901
- elif name in self.stored_striplines:
893
+ if name in self.stored_striplines:
902
894
  return self.stored_striplines[name]
903
- elif name in self.stored_coords:
904
- return self.stored_coords[name]
905
895
  else:
906
896
  raise ValueError(f'There is no stripline or coordinate under the name of {name}')
907
897
 
908
898
  def __call__(self, path_nr: int) -> StripPath:
909
899
  if path_nr >= len(self.paths):
910
- self.paths.append(StripPath())
900
+ self.paths.append(StripPath(self))
911
901
  return self.paths[path_nr]
912
902
 
913
903
  def determine_bounds(self,
@@ -956,9 +946,9 @@ class PCB:
956
946
 
957
947
  def plane(self,
958
948
  z: float,
959
- width: float = None,
960
- height: float = None,
961
- origin: tuple[float, float] = None,
949
+ width: float | None = None,
950
+ height: float | None = None,
951
+ origin: tuple[float, float] | None = None,
962
952
  alignment: Literal['corner','center'] = 'corner') -> GeoSurface:
963
953
  """Generates a generic rectangular plate in the XY grid.
964
954
  If no size is provided, it defaults to the entire PCB size assuming that the bounds are determined.
@@ -974,17 +964,22 @@ class PCB:
974
964
  GeoSurface: _description_
975
965
  """
976
966
  if width is None or height is None or origin is None:
967
+ if self.width is None or self.length is None or self.origin:
968
+ raise RouteException('Cannot define a plane with no possible definition of its size.')
977
969
  width = self.width
978
970
  height = self.length
979
971
  origin = (self.origin[0]*self.unit, self.origin[1]*self.unit)
980
- origin = origin + (z*self.unit, )
972
+
973
+ origin: tuple[float, ...] = origin + (z*self.unit, ) # type: ignore
981
974
 
982
975
  if alignment == 'center':
983
- origin = (origin[0] - width*self.unit/2, origin[1]-height*self.unit/2, origin[2])
976
+ origin = (origin[0] - width*self.unit/2,
977
+ origin[1] - height*self.unit/2,
978
+ origin[2])
984
979
 
985
- plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0))
986
- plane = change_coordinate_system(plane, self.cs)
987
- return plane
980
+ plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0)) # type: ignore
981
+ plane = change_coordinate_system(plane, self.cs) # type: ignore
982
+ return plane # type: ignore
988
983
 
989
984
  def gen_pcb(self,
990
985
  split_z: bool = True,
@@ -1004,7 +999,7 @@ class PCB:
1004
999
  if (z-zvalues_isolated[-1]) <= layer_tolerance:
1005
1000
  continue
1006
1001
  zvalues_isolated.append(z)
1007
- boxes = []
1002
+ boxes: list[GeoVolume] = []
1008
1003
  for z1, z2 in zip(zvalues_isolated[:-1],zvalues_isolated[1:]):
1009
1004
  h = z2-z1
1010
1005
  box = Box(self.width*self.unit,
@@ -1015,8 +1010,8 @@ class PCB:
1015
1010
  box = change_coordinate_system(box, self.cs)
1016
1011
  boxes.append(box)
1017
1012
  if merge:
1018
- return GeoVolume.merged(boxes)
1019
- return boxes
1013
+ return GeoVolume.merged(boxes) # type: ignore
1014
+ return boxes # type: ignore
1020
1015
 
1021
1016
  box = Box(self.width*self.unit,
1022
1017
  self.length*self.unit,
@@ -1024,7 +1019,7 @@ class PCB:
1024
1019
  position=(x0,y0,z0-self.thickness*self.unit))
1025
1020
  box.material = self.material
1026
1021
  box = change_coordinate_system(box, self.cs)
1027
- return box
1022
+ return box # type: ignore
1028
1023
 
1029
1024
  def gen_air(self, height: float) -> GeoVolume:
1030
1025
  """Generate the Air Block object
@@ -1041,7 +1036,7 @@ class PCB:
1041
1036
  height*self.unit,
1042
1037
  position=(x0,y0,z0))
1043
1038
  box = change_coordinate_system(box, self.cs)
1044
- return box
1039
+ return box # type: ignore
1045
1040
 
1046
1041
  def new(self,
1047
1042
  x: float,
@@ -1072,7 +1067,7 @@ class PCB:
1072
1067
  self.paths.append(path)
1073
1068
  return path
1074
1069
 
1075
- def lumped_port(self, stripline: StripLine, z_ground: float = None) -> GeoPolygon:
1070
+ def lumped_port(self, stripline: StripLine, z_ground: float | None = None) -> GeoPolygon:
1076
1071
  """Generate a lumped-port object to be created.
1077
1072
 
1078
1073
  Args:
@@ -1152,7 +1147,7 @@ class PCB:
1152
1147
 
1153
1148
  plate = Plate(np.array([x0,y0,z0])*self.unit, ax1, ax2)
1154
1149
  plate = change_coordinate_system(plate, self.cs)
1155
- return plate
1150
+ return plate # type: ignore
1156
1151
 
1157
1152
  def generate_vias(self, merge=False) -> list[Cyllinder] | Cyllinder:
1158
1153
  """Generates the via objects.
@@ -1175,14 +1170,14 @@ class PCB:
1175
1170
  vias.append(cyl)
1176
1171
  if merge:
1177
1172
 
1178
- return GeoVolume.merged(vias)
1173
+ return GeoVolume.merged(vias) # type: ignore
1179
1174
  return vias
1180
1175
 
1181
1176
  def add_poly(self,
1182
1177
  xs: list[float],
1183
1178
  ys: list[float],
1184
1179
  z: float = 0,
1185
- material: Material = COPPER):
1180
+ material: Material = COPPER) -> None:
1186
1181
  """Add a custom polygon to the PCB
1187
1182
 
1188
1183
  Args:
@@ -1210,6 +1205,12 @@ class PCB:
1210
1205
  poly = GeoPolygon([planetag,])
1211
1206
  return poly
1212
1207
 
1208
+ @overload
1209
+ def compile_paths(self, merge: Literal[True]) -> GeoSurface: ...
1210
+
1211
+ @overload
1212
+ def compile_paths(self, merge: Literal[False] = ...) -> list[GeoSurface]: ...
1213
+
1213
1214
  def compile_paths(self, merge: bool = False) -> list[GeoPolygon] | GeoSurface:
1214
1215
  """Compiles the striplines and returns a list of polygons or asingle one.
1215
1216
 
@@ -25,8 +25,8 @@ def rotation_angle(a: tuple[float, float], b: tuple[float, float]) -> float:
25
25
  @dataclass
26
26
  class Instruction:
27
27
  instr: str
28
- args: tuple[int]
29
- kwargs: dict[str,float] = None
28
+ args: tuple[int | float, ...]
29
+ kwargs: dict[str,float] | None = None
30
30
 
31
31
  def __post_init__(self):
32
32
  if self.kwargs is None:
@@ -37,33 +37,34 @@ char_class = ''.join(re.escape(c) for c in symbols)
37
37
 
38
38
  pattern = re.compile(rf'([{char_class}])([\d\,\.\-]+)')
39
39
 
40
- def parse_macro(pathstring: str, width: int, direction: tuple[float, float]) -> list[Instruction]:
40
+ def parse_macro(pathstring: str, width: int | float, direction: tuple[float, float]) -> list[Instruction]:
41
41
  instructions = pattern.findall(pathstring.replace(' ',''))
42
42
 
43
43
  oi = []
44
44
  for com, val in instructions:
45
45
  if ',' in val:
46
- ival, width = [float(x) for x in val.split(',')]
46
+ val_list: list[float] = [float(x) for x in val.split(',')]
47
+ ival, width = val_list[0], val_list[1]
47
48
  else:
48
49
  ival = float(val)
49
50
  if com == '=':
50
- oi.append(Instruction('straight',(ival,),{'width': width}))
51
+ oi.append(Instruction('straight',(ival,),{'width': width})) #type: ignore
51
52
  elif com == '>':
52
- oi.append(Instruction('turn',(rotation_angle(direction,(1,0)),) ))
53
+ oi.append(Instruction('turn',(rotation_angle(direction, (1., 0.)),) ))
53
54
  oi.append(Instruction('straight',(ival,),{'width': width}))
54
- direction = (1,0)
55
+ direction = (1. ,0. )
55
56
  elif com == '<':
56
- oi.append(Instruction('turn',(rotation_angle(direction,(-1,0)),) ))
57
+ oi.append(Instruction('turn',(rotation_angle(direction, (-1., 0.)),) ))
57
58
  oi.append(Instruction('straight',(ival,),{'width': width}))
58
- direction = (-1,0)
59
+ direction = (-1.,0.)
59
60
  elif com == 'v':
60
- oi.append(Instruction('turn',(rotation_angle(direction,(0,-1)),) ))
61
+ oi.append(Instruction('turn',(rotation_angle(direction, (0.,-1.)),) ))
61
62
  oi.append(Instruction('straight',(ival,),{'width': width}))
62
- direction = (0,-1)
63
+ direction = (0.,-1.)
63
64
  elif com == '^':
64
- oi.append(Instruction('turn',(rotation_angle(direction,(0,1)),) ))
65
+ oi.append(Instruction('turn',(rotation_angle(direction, (0.,1.)),) ))
65
66
  oi.append(Instruction('straight',(ival,),{'width': width}))
66
- direction = (0,1)
67
+ direction = (0.,1.)
67
68
  elif com == '\\':
68
69
  oi.append(Instruction('turn',(90,) ))
69
70
  oi.append(Instruction('straight',(ival,),{'width': width}))
@@ -106,7 +106,7 @@ def _add_pml_layer(center: tuple[float, float, float],
106
106
  plate = Plate(np.array([p0x-tW/2, p0y-tD/2, p0z-dz*thickness/2 + dz*(n+1)*thl]), ax1, ax2)
107
107
  planes.append(plate)
108
108
 
109
- pml_box.material = Material(_neff=np.sqrt(material.er*material.ur), _fer=ermat, _fur=urmat)
109
+ pml_box.material = Material(_neff=np.sqrt(material.er*material.ur), _fer=ermat, _fur=urmat, color='#bbbbff', opacity=0.1)
110
110
  pml_box.max_meshsize = thickness/N_mesh_layers
111
111
  pml_box._embeddings = planes
112
112
 
@@ -16,13 +16,15 @@
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
  from .material import Material, AIR
21
- from .selection import FaceSelection, DomainSelection, EdgeSelection, PointSelection
21
+ from .selection import FaceSelection, DomainSelection, EdgeSelection, PointSelection, Selection
22
22
  from loguru import logger
23
- from typing import Literal, Any
23
+ from typing import Literal, Any, Iterable, TypeVar
24
24
  import numpy as np
25
25
 
26
+
27
+
26
28
  def _map_tags(tags: list[int], mapping: dict[int, list[int]]):
27
29
  new_tags = []
28
30
  for tag in tags:
@@ -48,12 +50,12 @@ class _GeometryManager:
48
50
  self.geometry_list: dict[str, list[GeoObject]] = dict()
49
51
  self.active: str = ''
50
52
 
51
- def all_geometries(self, model: str = None) -> list[GeoObject]:
53
+ def all_geometries(self, model: str | None = None) -> list[GeoObject]:
52
54
  if model is None:
53
55
  model = self.active
54
56
  return [geo for geo in self.geometry_list[model] if geo._exists]
55
57
 
56
- def submit_geometry(self, geo: GeoObject, model: str = None) -> None:
58
+ def submit_geometry(self, geo: GeoObject, model: str | None = None) -> None:
57
59
  if model is None:
58
60
  model = self.active
59
61
  self.geometry_list[model].append(geo)
@@ -135,7 +137,7 @@ class _FacePointer:
135
137
  def translate(self, dx, dy, dz):
136
138
  self.o = self.o + np.array([dx, dy, dz])
137
139
 
138
- def mirror(self, c0: np.ndarray, pln: np.ndarray):
140
+ def mirror(self, c0: np.ndarray, pln: np.ndarray) -> None:
139
141
  """
140
142
  Reflect self.o and self.n across the plane passing through c0
141
143
  with normal pln.
@@ -205,9 +207,11 @@ class GeoObject:
205
207
  """A generalization of any OpenCASCADE entity described by a dimension and a set of tags.
206
208
  """
207
209
  dim: int = -1
208
- def __init__(self):
210
+ def __init__(self, tags: list[int] | None = None):
211
+ if tags is None:
212
+ tags = []
209
213
  self.old_tags: list[int] = []
210
- self.tags: list[int] = []
214
+ self.tags: list[int] = tags
211
215
  self.material: Material = AIR
212
216
  self.mesh_multiplier: float = 1.0
213
217
  self.max_meshsize: float = 1e9
@@ -225,7 +229,7 @@ class GeoObject:
225
229
  _GEOMANAGER.submit_geometry(self)
226
230
 
227
231
  @property
228
- def color_rgb(self) -> tuple[int,int,int]:
232
+ def color_rgb(self) -> tuple[float, float, float]:
229
233
  return self.material.color_rgb
230
234
 
231
235
  @property
@@ -233,7 +237,7 @@ class GeoObject:
233
237
  return self.material.opacity
234
238
 
235
239
  @property
236
- def select(self) -> FaceSelection | DomainSelection | EdgeSelection | None:
240
+ def select(self) -> Selection:
237
241
  '''Returns a corresponding Face/Domain or Edge Selection object'''
238
242
  if self.dim==1:
239
243
  return EdgeSelection(self.tags)
@@ -241,11 +245,14 @@ class GeoObject:
241
245
  return FaceSelection(self.tags)
242
246
  elif self.dim==3:
243
247
  return DomainSelection(self.tags)
248
+ else:
249
+ return Selection(self.tags)
244
250
 
245
251
  @staticmethod
246
- def merged(objects: list[GeoObject]) -> list[GeoObject]:
252
+ def merged(objects: list[GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject]) -> list[GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject] | GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject:
247
253
  dim = objects[0].dim
248
254
  tags = []
255
+ out: GeoObject | None = None
249
256
  for obj in objects:
250
257
  tags.extend(obj.tags)
251
258
  if dim==2:
@@ -260,8 +267,8 @@ class GeoObject:
260
267
  def __repr__(self) -> str:
261
268
  return f'{self.__class__.__name__}({self.dim},{self.tags})'
262
269
 
263
- def _data(self, *labels) -> tuple[Any]:
264
- return tuple([self._aux_data.get(lab, None) for lab in labels])
270
+ def _data(self, *labels) -> tuple[Any | None, ...]:
271
+ return tuple([self._aux_data[lab] for lab in labels])
265
272
 
266
273
  def _add_face_pointer(self,
267
274
  name: str,
@@ -325,7 +332,7 @@ class GeoObject:
325
332
  self._tools.update(obj._tools)
326
333
  return self
327
334
 
328
- def _face_tags(self, name: FaceNames, tool: GeoObject = None) -> list[int]:
335
+ def _face_tags(self, name: FaceNames, tool: GeoObject | None = None) -> list[int]:
329
336
  names = self._all_pointer_names
330
337
  if name not in names:
331
338
  raise ValueError(f'The face {name} does not exist in {self}')
@@ -380,7 +387,7 @@ class GeoObject:
380
387
  self._priority -= 1
381
388
  return self
382
389
 
383
- def outside(self, *exclude: FaceNames, tags: list[int] = None) -> FaceSelection:
390
+ def outside(self, *exclude: FaceNames, tags: list[int] | None = None) -> FaceSelection:
384
391
  """Returns the complete set of outside faces.
385
392
 
386
393
  If implemented, it is possible to exclude a set of faces based on their name
@@ -394,7 +401,7 @@ class GeoObject:
394
401
  dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
395
402
  return FaceSelection([t for d,t in dimtags if t not in tags])
396
403
 
397
- def face(self, name: FaceNames, tool: GeoObject = None) -> FaceSelection:
404
+ def face(self, name: FaceNames, tool: GeoObject | None = None) -> FaceSelection:
398
405
  """Returns the FaceSelection for a given face name.
399
406
 
400
407
  The face name must be defined for the type of geometry.
@@ -422,7 +429,7 @@ class GeoObject:
422
429
  return FaceSelection([t[1] for t in tags])
423
430
  if self.dim == 2:
424
431
  return FaceSelection(self.tags)
425
- if self.dim < 2:
432
+ else:
426
433
  raise ValueError('Can only generate faces for objects of dimension 2 or higher.')
427
434
 
428
435
  @staticmethod
@@ -443,12 +450,13 @@ class GeoVolume(GeoObject):
443
450
  '''GeoVolume is an interface to the GMSH CAD kernel. It does not represent EMerge
444
451
  specific geometry data.'''
445
452
  dim = 3
446
- def __init__(self, tag: int | list[int]):
453
+ def __init__(self, tag: int | Iterable[int]):
447
454
  super().__init__()
448
- if isinstance(tag, list):
449
- self.tags: list[int] = tag
455
+ self.tags: list[int] = []
456
+ if isinstance(tag, Iterable):
457
+ self.tags = list(tag)
450
458
  else:
451
- self.tags: list[int] = [tag,]
459
+ self.tags = [tag,]
452
460
 
453
461
  @property
454
462
  def select(self) -> DomainSelection:
@@ -463,10 +471,12 @@ class GeoPoint(GeoObject):
463
471
 
464
472
  def __init__(self, tag: int | list[int]):
465
473
  super().__init__()
466
- if isinstance(tag, list):
467
- self.tags: list[int] = tag
474
+
475
+ self.tags: list[int] = []
476
+ if isinstance(tag, Iterable):
477
+ self.tags = list(tag)
468
478
  else:
469
- self.tags: list[int] = [tag,]
479
+ self.tags = [tag,]
470
480
 
471
481
  class GeoEdge(GeoObject):
472
482
  dim = 1
@@ -477,10 +487,11 @@ class GeoEdge(GeoObject):
477
487
 
478
488
  def __init__(self, tag: int | list[int]):
479
489
  super().__init__()
480
- if isinstance(tag, list):
481
- self.tags: list[int] = tag
490
+ self.tags: list[int] = []
491
+ if isinstance(tag, Iterable):
492
+ self.tags = list(tag)
482
493
  else:
483
- self.tags: list[int] = [tag,]
494
+ self.tags = [tag,]
484
495
 
485
496
 
486
497
  class GeoSurface(GeoObject):
@@ -494,17 +505,20 @@ class GeoSurface(GeoObject):
494
505
 
495
506
  def __init__(self, tag: int | list[int]):
496
507
  super().__init__()
497
- if isinstance(tag, list):
498
- self.tags: list[int] = tag
508
+ self.tags: list[int] = []
509
+ if isinstance(tag, Iterable):
510
+ self.tags = list(tag)
499
511
  else:
500
- self.tags: list[int] = [tag,]
512
+ self.tags = [tag,]
501
513
 
502
514
  class GeoPolygon(GeoSurface):
503
515
 
504
516
  def __init__(self,
505
517
  tags: list[int]):
506
518
  super().__init__(tags)
507
- self.points: list[int] = None
508
- self.lines: list[int] = None
519
+ self.points: list[int] = []
520
+ self.lines: list[int] = []
509
521
 
510
522
 
523
+ T = TypeVar('T', GeoVolume, GeoEdge, GeoPoint, GeoSurface)
524
+
@@ -51,8 +51,8 @@ class LogController:
51
51
  logger.remove()
52
52
  self.std_handlers: list[int] = []
53
53
  self.file_handlers: list[int] = []
54
- self.level: LLTYPE = 'INFO'
55
- self.file_level: LLTYPE = 'INFO'
54
+ self.level: str = 'INFO'
55
+ self.file_level: str = 'INFO'
56
56
 
57
57
  def set_default(self):
58
58
  value = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
@@ -68,7 +68,7 @@ class LogController:
68
68
  handler = {"sink": sys.stdout,
69
69
  "level": loglevel,
70
70
  "format": FORMAT_DICT.get(loglevel, INFO_FORMAT)}
71
- logger.configure(handlers=[handler])
71
+ logger.configure(handlers=[handler]) # type: ignore
72
72
  self.level = loglevel
73
73
  os.environ["EMERGE_STD_LOGLEVEL"] = loglevel
74
74