emerge 0.6.11__py3-none-any.whl → 1.0.1__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.

emerge/__init__.py CHANGED
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "0.6.11"
21
+ __version__ = "1.0.1"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -27,7 +27,7 @@ __version__ = "0.6.11"
27
27
  NTHREADS = "1"
28
28
  os.environ["EMERGE_STD_LOGLEVEL"] = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
29
29
  os.environ["EMERGE_FILE_LOGLEVEL"] = os.getenv("EMERGE_FILE_LOGLEVEL", default="DEBUG")
30
- os.environ["OMP_NUM_THREADS"] = os.getenv("OMP_NUM_THREADS", default="4")
30
+ os.environ["OMP_NUM_THREADS"] = os.getenv("OMP_NUM_THREADS", default="1")
31
31
  os.environ["MKL_NUM_THREADS"] = os.getenv("MKL_NUM_THREADS", default="4")
32
32
  os.environ["OPENBLAS_NUM_THREADS"] = NTHREADS
33
33
  os.environ["VECLIB_MAXIMUM_THREADS"] = NTHREADS
emerge/_emerge/bc.py CHANGED
@@ -83,7 +83,7 @@ class BoundaryCondition:
83
83
  raise ValueError(f'All tags must have the same dimension, instead its {tags}')
84
84
  dimension = tags[0][0]
85
85
  if self.dimension is BCDimension.ANY:
86
- logger.info(f'Assigning dimension {BCDimension(dimension)} to {self}')
86
+ logger.info(f'Assigning {self} to dimension{BCDimension(dimension)}')
87
87
  self.dimension = BCDimension(dimension)
88
88
  elif self.dimension != BCDimension(dimension):
89
89
  raise ValueError(f'Current boundary condition has dimension {self.dimension}, but tags have dimension {BCDimension(dimension)}')
@@ -131,6 +131,13 @@ class BoundaryConditionSet:
131
131
  self.boundary_conditions: list[BoundaryCondition] = []
132
132
  self._initialized: bool = False
133
133
 
134
+ def cleanup(self) -> None:
135
+ """ Removes non assigned boundary conditions"""
136
+ logger.trace("Cleaning up boundary conditions.")
137
+ toremove = [bc for bc in self.boundary_conditions if len(bc.tags)==0]
138
+ logger.trace(f"Removing: {toremove}")
139
+ self.boundary_conditions = [bc for bc in self.boundary_conditions if len(bc.tags)>0]
140
+
134
141
  def _construct_bc(self, constructor: type) -> type:
135
142
  def constr(*args, **kwargs):
136
143
  obj = constructor(*args, **kwargs)
@@ -201,8 +208,6 @@ class BoundaryConditionSet:
201
208
 
202
209
  bc.add_tags(bc.selection.dimtags)
203
210
 
204
- logger.info('Excluding other possible boundary conditions')
205
-
206
211
  for existing_bc in self.boundary_conditions:
207
212
  excluded = existing_bc.exclude_bc(bc)
208
213
  if excluded:
@@ -0,0 +1,79 @@
1
+ # inside your module, e.g. utils.py
2
+ import os
3
+ import sys
4
+
5
+ import ast
6
+ import textwrap
7
+ from pathlib import Path
8
+
9
+
10
+ def _is_plain_check_run(test: ast.AST) -> bool:
11
+ """True only for a bare call to check_run() as the entire if-test."""
12
+ if not isinstance(test, ast.Call) or test.args or test.keywords:
13
+ return False
14
+ f = test.func
15
+ return (
16
+ isinstance(f, ast.Attribute) and f.attr == "cache_run"
17
+ ) or (
18
+ isinstance(f, ast.Name) and f.id == "cache_run"
19
+ )
20
+
21
+ def get_run_block_str(source: str) -> str:
22
+ """
23
+ Return all source text before the first `if check_run():` / `if *.check_run():`.
24
+ Returns None if no matching if-statement exists.
25
+ """
26
+ tree = ast.parse(source)
27
+ candidates = [n for n in ast.walk(tree) if isinstance(n, ast.If) and _is_plain_check_run(n.test)]
28
+ if not candidates:
29
+ return None
30
+ first = min(candidates, key=lambda n: n.lineno)
31
+ lines = source.splitlines(keepends=True)
32
+ return "".join(lines[: first.lineno - 1])
33
+
34
+
35
+ def get_build_block_str(source: str) -> str | None:
36
+ """Return the code string inside the first `if *.checkrun(...):` block, or None."""
37
+ tree = ast.parse(source)
38
+ for node in ast.walk(tree):
39
+ if isinstance(node, ast.If) and isinstance(node.test, ast.Call):
40
+ f = node.test.func
41
+ called = (isinstance(f, ast.Attribute) and f.attr == "cache_build") or \
42
+ (isinstance(f, ast.Name) and f.id == "cache_build")
43
+ if called and node.body:
44
+ start = node.body[0].lineno - 1 # 0-based start line
45
+ end = node.body[-1].end_lineno # 1-based end line (inclusive)
46
+ lines = source.splitlines()
47
+ block = "\n".join(lines[start:end])
48
+ return textwrap.dedent(block)
49
+ return None
50
+
51
+ def entry_script_path():
52
+ main = sys.modules.get("__main__")
53
+ # Most normal runs: python path/to/app.py
54
+ if main and hasattr(main, "__file__"):
55
+ return os.path.abspath(main.__file__)
56
+ # Fallbacks (e.g. python -m pkg.mod)
57
+ if sys.argv and sys.argv[0] not in ("", "-c"):
58
+ return os.path.abspath(sys.argv[0])
59
+ # Interactive sessions, notebooks, or embedded interpreters
60
+ return None
61
+
62
+
63
+ def get_build_section() -> str:
64
+ """ Returns the string section inside the check_build() if statement"""
65
+ name = entry_script_path()
66
+
67
+ lines = get_build_block_str(Path(name).read_text())
68
+ return lines
69
+
70
+ def get_run_section() -> str:
71
+ """Return sthe string section before the check_run() if statement
72
+
73
+ Returns:
74
+ str: _description_
75
+ """
76
+ name = entry_script_path()
77
+
78
+ lines = get_run_block_str(Path(name).read_text())
79
+ return lines
@@ -15,7 +15,7 @@
15
15
  # along with this program; if not, see
16
16
  # <https://www.gnu.org/licenses/>.
17
17
 
18
- from .pcb import PCB
18
+ from .pcb import PCB, PCBLayer
19
19
  from .pmlbox import pmlbox
20
20
  from .horn import Horn
21
21
  from .shapes import Cylinder, CoaxCylinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment, Cone
emerge/_emerge/geo/pcb.py CHANGED
@@ -19,8 +19,8 @@ from __future__ import annotations
19
19
 
20
20
  from ..cs import CoordinateSystem, GCS, Axis
21
21
  from ..geometry import GeoPolygon, GeoVolume, GeoSurface
22
- from ..material import Material, AIR, COPPER
23
- from .shapes import Box, Plate, Cylinder
22
+ from ..material import Material, AIR, COPPER, PEC
23
+ from .shapes import Box, Plate, Cylinder, Alignment
24
24
  from .polybased import XYPolygon
25
25
  from .operations import change_coordinate_system, unite
26
26
  from .pcb_tools.macro import parse_macro
@@ -49,6 +49,26 @@ SIZE_NAMES = Literal['0402','0603','1005','1608','2012','3216','3225','4532','50
49
49
  _SMD_SIZE_DICT = {x: (float(x[:2])*0.05, float(x[2:])*0.1) for x in ['0402','0603','1005','1608','2012','3216','3225','4532','5025','6332']}
50
50
 
51
51
 
52
+ class _PCB_NAME_MANAGER:
53
+
54
+ def __init__(self):
55
+ self.names: set[str] = set()
56
+
57
+ def __call__(self, name: str | None, classname: str | None = None) -> str:
58
+ if name is None:
59
+ return self(classname)
60
+
61
+ if name not in self.names:
62
+ self.names.add(name)
63
+ return name
64
+ for i in range(1_000_000):
65
+ newname = f'{name}_{i}'
66
+ if newname not in self.names:
67
+ self.names.add(newname)
68
+ return newname
69
+
70
+
71
+ _NAME_MANAGER = _PCB_NAME_MANAGER()
52
72
  ############################################################
53
73
  # FUNCTIONS #
54
74
  ############################################################
@@ -80,21 +100,6 @@ def _rot_mat(angle: float) -> np.ndarray:
80
100
  ############################################################
81
101
 
82
102
 
83
- class PCBPoly:
84
-
85
- def __init__(self,
86
- xs: list[float],
87
- ys: list[float],
88
- z: float = 0,
89
- material: Material = COPPER):
90
- self.xs: list[float] = xs
91
- self.ys: list[float] = ys
92
- self.z: float = z
93
- self.material: Material = material
94
-
95
- @property
96
- def xys(self) -> list[tuple[float, float]]:
97
- return list([(x,y) for x,y in zip(self.xs, self.ys)])
98
103
 
99
104
  @dataclass
100
105
  class Via:
@@ -106,7 +111,7 @@ class Via:
106
111
  segments: int
107
112
 
108
113
  class RouteElement:
109
-
114
+ _DEFNAME: str = 'RouteElement'
110
115
  def __init__(self):
111
116
  self.width: float = None
112
117
  self.x: float = None
@@ -138,7 +143,7 @@ class RouteElement:
138
143
  return approx(self.x, other.x) and approx(self.y, other.y) and (1-abs(np.sum(self.direction*other.direction)))<1e-8
139
144
 
140
145
  class StripLine(RouteElement):
141
-
146
+ _DEFNAME: str = 'StripLine'
142
147
  def __init__(self,
143
148
  x: float,
144
149
  y: float,
@@ -161,7 +166,7 @@ class StripLine(RouteElement):
161
166
  return [(self.x - self.width/2 * self.dirright[0], self.y - self.width/2 * self.dirright[1])]
162
167
 
163
168
  class StripTurn(RouteElement):
164
-
169
+ _DEFNAME: str = 'StripTurn'
165
170
  def __init__(self,
166
171
  x: float,
167
172
  y: float,
@@ -234,6 +239,7 @@ class StripTurn(RouteElement):
234
239
  return [(x1, y1), (x2, y2), (xend, yend)]
235
240
  else:
236
241
  raise RouteException(f'Trying to route a StripTurn with an unknown corner type: {self.corner_type}')
242
+
237
243
  @property
238
244
  def left(self) -> list[tuple[float, float]]:
239
245
  if self.angle < 0:
@@ -330,17 +336,48 @@ class StripCurve(StripTurn):
330
336
 
331
337
  return points[::-1]
332
338
 
339
+ class PCBPoly:
340
+ _DEFNAME: str = 'Poly'
333
341
 
342
+ def __init__(self,
343
+ xs: list[float],
344
+ ys: list[float],
345
+ z: float = 0,
346
+ material: Material = PEC,
347
+ name: str | None = None):
348
+ self.xs: list[float] = xs
349
+ self.ys: list[float] = ys
350
+ self.z: float = z
351
+ self.material: Material = material
352
+ self.name: str = _NAME_MANAGER(name, self._DEFNAME)
353
+
354
+ @property
355
+ def xys(self) -> list[tuple[float, float]]:
356
+ return list([(x,y) for x,y in zip(self.xs, self.ys)])
357
+
358
+ def segment(self, index: int) -> StripLine:
359
+ N = len(self.xs)
360
+ x1 = self.xs[index%N]
361
+ x2 = self.xs[(index+1)%N]
362
+ y1 = self.ys[index%N]
363
+ y2 = self.ys[(index+1)%N]
364
+ z = self.z
365
+ W = ((x2-x1)**2 + (y2-y1)**2)**(0.5)
366
+ wdir = ((y2-y1)/W, -(x2-x1)/W)
367
+
368
+ return StripLine((x2+x1)/2, (y1+y2)/2, W, wdir)
334
369
  ############################################################
335
370
  # THE STRIP PATH CLASS #
336
371
  ############################################################
337
372
 
338
373
  class StripPath:
339
-
340
- def __init__(self, pcb: PCB):
374
+ _DEFNAME: str = 'Path'
375
+
376
+ def __init__(self, pcb: PCB, name: str | None = None):
341
377
  self.pcb: PCB = pcb
342
378
  self.path: list[RouteElement] = []
343
379
  self.z: float = 0
380
+ self.name: str = _NAME_MANAGER(name, self._DEFNAME)
344
381
 
345
382
  def _has(self, element: RouteElement) -> bool:
346
383
  if element in self.path:
@@ -896,36 +933,82 @@ class StripPath:
896
933
  self.path.append(RouteElement())
897
934
  return self.path[element_nr]
898
935
 
936
+ class PCBLayer:
937
+
938
+ def __init__(self,
939
+ thickness: float,
940
+ material: Material):
941
+ self.th: float = thickness
942
+ self.mat: Material = material
943
+
899
944
  ############################################################
900
945
  # PCB DESIGN CLASS #
901
946
  ############################################################
902
947
 
903
948
  class PCB:
949
+ _DEFNAME: str = 'PCB'
950
+
904
951
  def __init__(self,
905
952
  thickness: float,
906
953
  unit: float = 0.001,
907
954
  cs: CoordinateSystem | None = None,
908
955
  material: Material = AIR,
956
+ trace_material: Material = PEC,
909
957
  layers: int = 2,
958
+ stack: list[PCBLayer] = None,
959
+ name: str | None = None,
960
+ trace_thickness: float | None = None,
910
961
  ):
962
+ """Creates a new PCB layout class instance
963
+
964
+ Args:
965
+ thickness (float): The total PCB thickness
966
+ unit (float, optional): The units used for all dimensions. Defaults to 0.001 (mm).
967
+ cs (CoordinateSystem | None, optional): The coordinate system to place the PCB in (XY). Defaults to None.
968
+ material (Material, optional): The dielectric material. Defaults to AIR.
969
+ trace_material (Material, optional): The trace material. Defaults to PEC.
970
+ layers (int, optional): The number of copper layers. Defaults to 2.
971
+ stack (list[PCBLayer], optional): Optional list of PCBLayer classes for multilayer PCB with different dielectrics. Defaults to None.
972
+ name (str | None, optional): The PCB object name. Defaults to None.
973
+ trace_thickness (float | None, optional): The conductor trace thickness if important. Defaults to None.
974
+ """
911
975
 
912
976
  self.thickness: float = thickness
913
- self._zs: np.ndarray = np.linspace(-self.thickness, 0, layers)
977
+ self._stack: list[PCBLayer] = []
978
+
979
+ if stack is not None:
980
+ self._stack = stack
981
+ ths = [ly.th for ly in stack]
982
+ zbot = -sum(ths)
983
+ self._zs = np.concatenate([np.array([zbot,]), zbot + np.cumsum(np.array(ths))])
984
+ self.thickness = sum(ths)
985
+ else:
986
+ self._zs: np.ndarray = np.linspace(-self.thickness, 0, layers)
987
+ ths = np.diff(self._zs)
988
+ self._stack = [PCBLayer(th, material) for th in ths]
989
+
990
+
914
991
  self.material: Material = material
992
+ self.trace_material: Material = trace_material
915
993
  self.width: float | None = None
916
994
  self.length: float | None = None
917
995
  self.origin: np.ndarray = np.array([0.,0.,0.])
996
+
918
997
  self.paths: list[StripPath] = []
919
998
  self.polies: list[PCBPoly] = []
920
999
 
921
1000
  self.lumped_ports: list[StripLine] = []
922
1001
  self.lumped_elements: list[GeoPolygon] = []
1002
+ self.trace_thickness: float | None = trace_thickness
923
1003
 
924
1004
  self.unit: float = unit
925
1005
 
926
1006
  self.cs: CoordinateSystem = cs
927
1007
  if self.cs is None:
928
1008
  self.cs = GCS
1009
+
1010
+ self.dielectric_priority: int = 11
1011
+ self.via_priority: int = 12
929
1012
 
930
1013
  self.traces: list[GeoPolygon] = []
931
1014
  self.ports: list[GeoPolygon] = []
@@ -941,6 +1024,8 @@ class PCB:
941
1024
 
942
1025
  self.calc: PCBCalculator = PCBCalculator(self.thickness, self._zs, self.material, self.unit)
943
1026
 
1027
+ self.name: str = _NAME_MANAGER(name, self._DEFNAME)
1028
+
944
1029
  @property
945
1030
  def trace(self) -> GeoPolygon:
946
1031
  tags = []
@@ -1024,6 +1109,9 @@ class PCB:
1024
1109
  if name in self.stored_striplines:
1025
1110
  return self.stored_striplines[name]
1026
1111
  else:
1112
+ for poly in self.polies:
1113
+ if poly.name==name:
1114
+ return poly
1027
1115
  raise ValueError(f'There is no stripline or coordinate under the name of {name}')
1028
1116
 
1029
1117
  def __call__(self, path_nr: int) -> StripPath:
@@ -1080,7 +1168,8 @@ class PCB:
1080
1168
  width: float | None = None,
1081
1169
  height: float | None = None,
1082
1170
  origin: tuple[float, float] | None = None,
1083
- alignment: Literal['corner','center'] = 'corner') -> GeoSurface:
1171
+ alignment: Alignment = Alignment.CORNER,
1172
+ name: str | None = None) -> GeoSurface:
1084
1173
  """Generates a generic rectangular plate in the XY grid.
1085
1174
  If no size is provided, it defaults to the entire PCB size assuming that the bounds are determined.
1086
1175
 
@@ -1103,19 +1192,19 @@ class PCB:
1103
1192
 
1104
1193
  origin: tuple[float, ...] = origin + (z*self.unit, ) # type: ignore
1105
1194
 
1106
- if alignment == 'center':
1195
+ if alignment is Alignment.CENTER:
1107
1196
  origin = (origin[0] - width*self.unit/2,
1108
1197
  origin[1] - height*self.unit/2,
1109
1198
  origin[2])
1110
1199
 
1111
- plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0)) # type: ignore
1200
+ plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0), name=name) # type: ignore
1201
+ plane._store('thickness', self.thickness)
1112
1202
  plane = change_coordinate_system(plane, self.cs) # type: ignore
1113
- plane.set_material(COPPER)
1203
+ plane.set_material(self.trace_material)
1114
1204
  return plane # type: ignore
1115
1205
 
1116
1206
  def generate_pcb(self,
1117
1207
  split_z: bool = True,
1118
- layer_tolerance: float = 1e-6,
1119
1208
  merge: bool = True) -> GeoVolume:
1120
1209
  """Generate the PCB Block object
1121
1210
 
@@ -1123,37 +1212,38 @@ class PCB:
1123
1212
  GeoVolume: The PCB Block
1124
1213
  """
1125
1214
  x0, y0, z0 = self.origin*self.unit
1126
-
1127
- if split_z:
1128
- zvalues = sorted(list(set(self.zs + [-self.thickness, 0.0])))
1129
- zvalues_isolated = [zvalues[0],]
1130
- for z in zvalues[1:]:
1131
- if (z-zvalues_isolated[-1]) <= layer_tolerance:
1132
- continue
1133
- zvalues_isolated.append(z)
1215
+
1216
+ Nmats = len(set([layer.mat.name for layer in self._stack]))
1217
+
1218
+ if split_z and self._zs.shape[0]>2 or Nmats > 1:
1219
+
1134
1220
  boxes: list[GeoVolume] = []
1135
- for z1, z2 in zip(zvalues_isolated[:-1],zvalues_isolated[1:]):
1221
+ for i, (z1, z2, layer) in enumerate(zip(self._zs[:-1],self._zs[1:],self._stack)):
1136
1222
  h = z2-z1
1137
1223
  box = Box(self.width*self.unit,
1138
1224
  self.length*self.unit,
1139
1225
  h*self.unit,
1140
- position=(x0, y0, z0+z1*self.unit))
1141
- box.material = self.material
1226
+ position=(x0, y0, z0+z1*self.unit),
1227
+ name=f'{self.name}_layer{i}')
1228
+ box.material = layer.mat
1142
1229
  box = change_coordinate_system(box, self.cs)
1230
+ box.prio_set(self.dielectric_priority)
1143
1231
  boxes.append(box)
1144
- if merge:
1145
- return GeoVolume.merged(boxes) # type: ignore
1232
+ if merge and Nmats == 1:
1233
+ return GeoVolume.merged(boxes).prio_set(self.dielectric_priority) # type: ignore
1146
1234
  return boxes # type: ignore
1147
1235
 
1148
1236
  box = Box(self.width*self.unit,
1149
1237
  self.length*self.unit,
1150
1238
  self.thickness*self.unit,
1151
- position=(x0,y0,z0-self.thickness*self.unit))
1152
- box.material = self.material
1239
+ position=(x0,y0,z0-self.thickness*self.unit),
1240
+ name=f'{self.name}_diel')
1241
+ box.material = self._stack[0].mat
1242
+ box.prio_set(self.dielectric_priority)
1153
1243
  box = change_coordinate_system(box, self.cs)
1154
1244
  return box # type: ignore
1155
1245
 
1156
- def generate_air(self, height: float) -> GeoVolume:
1246
+ def generate_air(self, height: float, name: str = 'PCBAirbox') -> GeoVolume:
1157
1247
  """Generate the Air Block object
1158
1248
 
1159
1249
  This requires that the width, depth and origin are deterimed. This
@@ -1166,7 +1256,8 @@ class PCB:
1166
1256
  box = Box(self.width*self.unit,
1167
1257
  self.length*self.unit,
1168
1258
  height*self.unit,
1169
- position=(x0,y0,z0))
1259
+ position=(x0,y0,z0),
1260
+ name=name)
1170
1261
  box = change_coordinate_system(box, self.cs)
1171
1262
  return box # type: ignore
1172
1263
 
@@ -1175,7 +1266,8 @@ class PCB:
1175
1266
  y: float,
1176
1267
  width: float,
1177
1268
  direction: tuple[float, float],
1178
- z: float = 0) -> StripPath:
1269
+ z: float = 0,
1270
+ name: str | None = None) -> StripPath:
1179
1271
  """Start a new trace
1180
1272
 
1181
1273
  The trace is started at the provided x,y, coordinates with a width "width".
@@ -1194,12 +1286,12 @@ class PCB:
1194
1286
  >>> PCB.new(...).straight(...).turn(...).straight(...) etc.
1195
1287
 
1196
1288
  """
1197
- path = StripPath(self)
1289
+ path = StripPath(self, name=name)
1198
1290
  path.init(x, y, width, direction, z=z)
1199
1291
  self.paths.append(path)
1200
1292
  return path
1201
1293
 
1202
- def lumped_port(self, stripline: StripLine, z_ground: float | None = None) -> GeoPolygon:
1294
+ def lumped_port(self, stripline: StripLine, z_ground: float | None = None, name: str | None = 'LumpedPort') -> GeoPolygon:
1203
1295
  """Generate a lumped-port object to be created.
1204
1296
 
1205
1297
  Args:
@@ -1231,7 +1323,7 @@ class PCB:
1231
1323
 
1232
1324
  tag_wire = gmsh.model.occ.addWire(ltags)
1233
1325
  planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
1234
- poly = GeoPolygon([planetag,])
1326
+ poly = GeoPolygon([planetag,], name='name')
1235
1327
  poly._aux_data['width'] = stripline.width*self.unit
1236
1328
  poly._aux_data['height'] = height*self.unit
1237
1329
  poly._aux_data['vdir'] = self.cs.zax
@@ -1239,19 +1331,18 @@ class PCB:
1239
1331
 
1240
1332
  return poly
1241
1333
 
1242
- def _lumped_element(self, poly: XYPolygon, function: Callable, width: float, length: float) -> None:
1243
-
1244
- geopoly = poly._finalize(self.cs)
1334
+ def _lumped_element(self, poly: XYPolygon, function: Callable, width: float, length: float, name: str | None = 'LumpedElement') -> None:
1335
+ geopoly = poly._finalize(self.cs, name=name)
1245
1336
  geopoly._aux_data['func'] = function
1246
1337
  geopoly._aux_data['width'] = width
1247
1338
  geopoly._aux_data['height'] = length
1248
1339
  self.lumped_elements.append(geopoly)
1249
1340
 
1250
-
1251
1341
  def modal_port(self,
1252
1342
  point: StripLine,
1253
1343
  height: float,
1254
1344
  width_multiplier: float = 5.0,
1345
+ name: str | None = 'ModalPort'
1255
1346
  ) -> GeoSurface:
1256
1347
  """Generate a wave-port as a GeoSurface.
1257
1348
 
@@ -1277,7 +1368,7 @@ class PCB:
1277
1368
  ax1 = np.array([ds[0], ds[1], 0])*self.unit*point.width*width_multiplier
1278
1369
  ax2 = np.array([0,0,1])*height*self.unit
1279
1370
 
1280
- plate = Plate(np.array([x0,y0,z0])*self.unit, ax1, ax2)
1371
+ plate = Plate(np.array([x0,y0,z0])*self.unit, ax1, ax2, name=name)
1281
1372
  plate = change_coordinate_system(plate, self.cs)
1282
1373
  return plate # type: ignore
1283
1374
 
@@ -1304,7 +1395,8 @@ class PCB:
1304
1395
  xg, yg, zg = self.cs.in_global_cs(x0, y0, z0)
1305
1396
  cs = CoordinateSystem(self.cs.xax, self.cs.yax, self.cs.zax, np.array([xg, yg, zg]))
1306
1397
  cyl = Cylinder(via.radius*self.unit, (via.z2-via.z1)*self.unit, cs, via.segments)
1307
- cyl.material = COPPER
1398
+ cyl.material = self.trace_material
1399
+ cyl.prio_set(self.via_priority)
1308
1400
  vias.append(cyl)
1309
1401
  if merge:
1310
1402
 
@@ -1315,7 +1407,8 @@ class PCB:
1315
1407
  xs: list[float],
1316
1408
  ys: list[float],
1317
1409
  z: float = 0,
1318
- material: Material = COPPER) -> None:
1410
+ material: Material = None,
1411
+ name: str | None = None) -> None:
1319
1412
  """Add a custom polygon to the PCB
1320
1413
 
1321
1414
  Args:
@@ -1324,9 +1417,14 @@ class PCB:
1324
1417
  z (float, optional): The z-height. Defaults to 0.
1325
1418
  material (Material, optional): The material. Defaults to COPPER.
1326
1419
  """
1327
- self.polies.append(PCBPoly(xs, ys, z, material))
1420
+ if material is None:
1421
+ material = self.trace_material
1422
+ poly = PCBPoly(xs, ys, z, material,name=name)
1423
+
1424
+ self.polies.append(poly)
1425
+
1328
1426
 
1329
- def _gen_poly(self, xys: list[tuple[float, float]], z: float) -> GeoPolygon:
1427
+ def _gen_poly(self, xys: list[tuple[float, float]], z: float, name: str | None = None) -> GeoPolygon:
1330
1428
  """ Generates a GeoPoly out of a list of (x,y) coordinate tuples"""
1331
1429
  ptags = []
1332
1430
  for x,y in xys:
@@ -1340,7 +1438,8 @@ class PCB:
1340
1438
 
1341
1439
  tag_wire = gmsh.model.occ.addWire(ltags)
1342
1440
  planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
1343
- poly = GeoPolygon([planetag,])
1441
+ poly = GeoPolygon([planetag,], name=name)
1442
+ poly._store('thickness', self.thickness)
1344
1443
  return poly
1345
1444
 
1346
1445
  @overload
@@ -1385,12 +1484,12 @@ class PCB:
1385
1484
  ally.append(y)
1386
1485
 
1387
1486
  poly = self._gen_poly(xys2, z)
1388
- poly.material = COPPER
1487
+ poly.material = self.trace_material
1389
1488
  polys.append(poly)
1390
1489
 
1391
1490
  for pcbpoly in self.polies:
1392
1491
  self.zs.append(pcbpoly.z)
1393
- poly = self._gen_poly(pcbpoly.xys, pcbpoly.z)
1492
+ poly = self._gen_poly(pcbpoly.xys, pcbpoly.z, name=pcbpoly.name)
1394
1493
  poly.material = pcbpoly.material
1395
1494
  polys.append(poly)
1396
1495
  xs, ys = zip(*pcbpoly.xys)
@@ -1407,14 +1506,4 @@ class PCB:
1407
1506
  polys = unite(*polys)
1408
1507
 
1409
1508
  return polys
1410
-
1411
- ############################################################
1412
- # DEPRICATED #
1413
- ############################################################
1414
-
1415
- class PCBLayouter(PCB):
1416
1509
 
1417
- def __init__(self, *args, **kwargs):
1418
- logger.warning('PCBLayouter will be depricated. Use PCB instead.')
1419
- super().__init__(*args, **kwargs)
1420
-