emerge 1.0.0__tar.gz → 1.0.1__tar.gz

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 (115) hide show
  1. {emerge-1.0.0 → emerge-1.0.1}/.bumpversion.toml +1 -1
  2. {emerge-1.0.0 → emerge-1.0.1}/PKG-INFO +3 -1
  3. {emerge-1.0.0 → emerge-1.0.1}/emerge/__init__.py +2 -2
  4. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/__init__.py +1 -1
  5. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/pcb.py +145 -66
  6. emerge-1.0.1/emerge/_emerge/geo/pcb_tools/dxf.py +360 -0
  7. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/polybased.py +21 -15
  8. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/shapes.py +31 -16
  9. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geometry.py +120 -21
  10. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mesh3d.py +39 -12
  11. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/periodic.py +19 -17
  12. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/microwave_bc.py +17 -4
  13. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/microwave_data.py +3 -0
  14. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/pyvista/display.py +9 -1
  15. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/simple_plots.py +4 -1
  16. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/selection.py +10 -8
  17. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/simmodel.py +67 -33
  18. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/solver.py +9 -2
  19. emerge-1.0.1/emerge/beta/dxf.py +1 -0
  20. {emerge-1.0.0 → emerge-1.0.1}/emerge/lib.py +3 -0
  21. emerge-1.0.1/emerge/materials/__init__.py +1 -0
  22. emerge-1.0.1/emerge/materials/isola.py +294 -0
  23. emerge-1.0.1/emerge/materials/rogers.py +58 -0
  24. {emerge-1.0.0 → emerge-1.0.1}/examples/demo10_sgh.py +3 -3
  25. {emerge-1.0.0 → emerge-1.0.1}/examples/demo11_lumped_element_filter.py +1 -1
  26. {emerge-1.0.0 → emerge-1.0.1}/examples/demo12_mode_alignment.py +1 -1
  27. {emerge-1.0.0 → emerge-1.0.1}/examples/demo13_helix_antenna.py +1 -1
  28. {emerge-1.0.0 → emerge-1.0.1}/examples/demo14_boundary_selection.py +1 -1
  29. {emerge-1.0.0 → emerge-1.0.1}/examples/demo1_stepped_imp_filter.py +1 -3
  30. {emerge-1.0.0 → emerge-1.0.1}/examples/demo2_combline_filter.py +1 -1
  31. {emerge-1.0.0 → emerge-1.0.1}/examples/demo3_coupled_line_filter.py +1 -1
  32. {emerge-1.0.0 → emerge-1.0.1}/examples/demo4_patch_antenna.py +1 -1
  33. {emerge-1.0.0 → emerge-1.0.1}/examples/demo5_revolve.py +4 -4
  34. {emerge-1.0.0 → emerge-1.0.1}/examples/demo6_striplines_with_vias.py +1 -1
  35. {emerge-1.0.0 → emerge-1.0.1}/examples/demo7_periodic_cells.py +1 -1
  36. {emerge-1.0.0 → emerge-1.0.1}/examples/demo8_waveguide_bpf_synthesis.py +2 -2
  37. {emerge-1.0.0 → emerge-1.0.1}/examples/demo9_dielectric_resonator.py +1 -1
  38. {emerge-1.0.0 → emerge-1.0.1}/pyproject.toml +5 -1
  39. {emerge-1.0.0 → emerge-1.0.1}/uv.lock +64 -2
  40. {emerge-1.0.0 → emerge-1.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {emerge-1.0.0 → emerge-1.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  42. {emerge-1.0.0 → emerge-1.0.1}/.gitignore +0 -0
  43. {emerge-1.0.0 → emerge-1.0.1}/.python-version +0 -0
  44. {emerge-1.0.0 → emerge-1.0.1}/LICENSE +0 -0
  45. {emerge-1.0.0 → emerge-1.0.1}/README.md +0 -0
  46. {emerge-1.0.0 → emerge-1.0.1}/THIRD_PARTY_LICENSES.md +0 -0
  47. {emerge-1.0.0 → emerge-1.0.1}/UMFPACK_Install_windows.md +0 -0
  48. {emerge-1.0.0 → emerge-1.0.1}/UMFPACK_installer_windows.py +0 -0
  49. {emerge-1.0.0 → emerge-1.0.1}/emerge/__main__.py +0 -0
  50. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/__init__.py +0 -0
  51. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/_cache_check.py +0 -0
  52. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/bc.py +0 -0
  53. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/cacherun.py +0 -0
  54. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/const.py +0 -0
  55. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/coord.py +0 -0
  56. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/cs.py +0 -0
  57. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/dataset.py +0 -0
  58. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/__init__.py +0 -0
  59. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/femdata.py +0 -0
  60. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/index_interp.py +0 -0
  61. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/ned2_interp.py +0 -0
  62. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/nedelec2.py +0 -0
  63. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/elements/nedleg2.py +0 -0
  64. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/horn.py +0 -0
  65. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/modeler.py +0 -0
  66. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/operations.py +0 -0
  67. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  68. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  69. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/pmlbox.py +0 -0
  70. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo/step.py +0 -0
  71. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/geo2d.py +0 -0
  72. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/howto.py +0 -0
  73. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/logsettings.py +0 -0
  74. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/material.py +0 -0
  75. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mesher.py +0 -0
  76. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mth/common_functions.py +0 -0
  77. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mth/integrals.py +0 -0
  78. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mth/optimized.py +0 -0
  79. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/mth/pairing.py +0 -0
  80. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/__init__.py +0 -0
  81. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/__init__.py +0 -0
  82. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  83. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/assembler.py +0 -0
  84. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
  85. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
  86. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
  87. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
  88. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
  89. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/microwave_3d.py +0 -0
  90. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  91. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
  92. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/sc.py +0 -0
  93. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  94. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  95. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  96. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/__init__.py +0 -0
  97. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/display.py +0 -0
  98. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  99. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  100. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  101. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/plot.py +0 -0
  102. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/projects/__init__.py +0 -0
  103. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/projects/_gen_base.txt +0 -0
  104. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/projects/_load_base.txt +0 -0
  105. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/projects/generate_project.py +0 -0
  106. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/settings.py +0 -0
  107. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/simulation_data.py +0 -0
  108. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  109. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  110. {emerge-1.0.0 → emerge-1.0.1}/emerge/_emerge/system.py +0 -0
  111. {emerge-1.0.0 → emerge-1.0.1}/emerge/cli.py +0 -0
  112. {emerge-1.0.0 → emerge-1.0.1}/emerge/ext.py +0 -0
  113. {emerge-1.0.0 → emerge-1.0.1}/emerge/plot.py +0 -0
  114. {emerge-1.0.0 → emerge-1.0.1}/emerge/pyvista.py +0 -0
  115. {emerge-1.0.0 → emerge-1.0.1}/src/__init__.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.0.0"
2
+ current_version = "1.0.1"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  search = "{current_version}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: An open source EM FEM simulator in Python
5
5
  Project-URL: Homepage, https://github.com/FennisRobert/EMerge
6
6
  Project-URL: Issues, https://github.com/FennisRobert/EMerge/issues
@@ -20,6 +20,8 @@ Provides-Extra: cudss
20
20
  Requires-Dist: cupy-cuda12x; extra == 'cudss'
21
21
  Requires-Dist: nvidia-cudss-cu12; extra == 'cudss'
22
22
  Requires-Dist: nvmath-python[cu12]; extra == 'cudss'
23
+ Provides-Extra: dxf
24
+ Requires-Dist: ezdxf; extra == 'dxf'
23
25
  Provides-Extra: umfpack
24
26
  Requires-Dist: scikit-umfpack; (sys_platform != 'win32') and extra == 'umfpack'
25
27
  Description-Content-Type: text/markdown
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "1.0.0"
21
+ __version__ = "1.0.1"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -27,7 +27,7 @@ __version__ = "1.0.0"
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
@@ -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
@@ -20,7 +20,7 @@ from __future__ import annotations
20
20
  from ..cs import CoordinateSystem, GCS, Axis
21
21
  from ..geometry import GeoPolygon, GeoVolume, GeoSurface
22
22
  from ..material import Material, AIR, COPPER, PEC
23
- from .shapes import Box, Plate, Cylinder
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 = PEC):
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'
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)])
333
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,11 +933,21 @@ 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,
@@ -908,20 +955,51 @@ class PCB:
908
955
  material: Material = AIR,
909
956
  trace_material: Material = PEC,
910
957
  layers: int = 2,
958
+ stack: list[PCBLayer] = None,
959
+ name: str | None = None,
960
+ trace_thickness: float | None = None,
911
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
+ """
912
975
 
913
976
  self.thickness: float = thickness
914
- 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
+
915
991
  self.material: Material = material
916
992
  self.trace_material: Material = trace_material
917
993
  self.width: float | None = None
918
994
  self.length: float | None = None
919
995
  self.origin: np.ndarray = np.array([0.,0.,0.])
996
+
920
997
  self.paths: list[StripPath] = []
921
998
  self.polies: list[PCBPoly] = []
922
999
 
923
1000
  self.lumped_ports: list[StripLine] = []
924
1001
  self.lumped_elements: list[GeoPolygon] = []
1002
+ self.trace_thickness: float | None = trace_thickness
925
1003
 
926
1004
  self.unit: float = unit
927
1005
 
@@ -946,6 +1024,8 @@ class PCB:
946
1024
 
947
1025
  self.calc: PCBCalculator = PCBCalculator(self.thickness, self._zs, self.material, self.unit)
948
1026
 
1027
+ self.name: str = _NAME_MANAGER(name, self._DEFNAME)
1028
+
949
1029
  @property
950
1030
  def trace(self) -> GeoPolygon:
951
1031
  tags = []
@@ -1029,6 +1109,9 @@ class PCB:
1029
1109
  if name in self.stored_striplines:
1030
1110
  return self.stored_striplines[name]
1031
1111
  else:
1112
+ for poly in self.polies:
1113
+ if poly.name==name:
1114
+ return poly
1032
1115
  raise ValueError(f'There is no stripline or coordinate under the name of {name}')
1033
1116
 
1034
1117
  def __call__(self, path_nr: int) -> StripPath:
@@ -1085,7 +1168,8 @@ class PCB:
1085
1168
  width: float | None = None,
1086
1169
  height: float | None = None,
1087
1170
  origin: tuple[float, float] | None = None,
1088
- alignment: Literal['corner','center'] = 'corner') -> GeoSurface:
1171
+ alignment: Alignment = Alignment.CORNER,
1172
+ name: str | None = None) -> GeoSurface:
1089
1173
  """Generates a generic rectangular plate in the XY grid.
1090
1174
  If no size is provided, it defaults to the entire PCB size assuming that the bounds are determined.
1091
1175
 
@@ -1108,19 +1192,19 @@ class PCB:
1108
1192
 
1109
1193
  origin: tuple[float, ...] = origin + (z*self.unit, ) # type: ignore
1110
1194
 
1111
- if alignment == 'center':
1195
+ if alignment is Alignment.CENTER:
1112
1196
  origin = (origin[0] - width*self.unit/2,
1113
1197
  origin[1] - height*self.unit/2,
1114
1198
  origin[2])
1115
1199
 
1116
- 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)
1117
1202
  plane = change_coordinate_system(plane, self.cs) # type: ignore
1118
1203
  plane.set_material(self.trace_material)
1119
1204
  return plane # type: ignore
1120
1205
 
1121
1206
  def generate_pcb(self,
1122
1207
  split_z: bool = True,
1123
- layer_tolerance: float = 1e-6,
1124
1208
  merge: bool = True) -> GeoVolume:
1125
1209
  """Generate the PCB Block object
1126
1210
 
@@ -1128,39 +1212,38 @@ class PCB:
1128
1212
  GeoVolume: The PCB Block
1129
1213
  """
1130
1214
  x0, y0, z0 = self.origin*self.unit
1131
-
1132
- if split_z:
1133
- zvalues = sorted(list(set(self.zs + [-self.thickness, 0.0])))
1134
- zvalues_isolated = [zvalues[0],]
1135
- for z in zvalues[1:]:
1136
- if (z-zvalues_isolated[-1]) <= layer_tolerance:
1137
- continue
1138
- 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
+
1139
1220
  boxes: list[GeoVolume] = []
1140
- 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)):
1141
1222
  h = z2-z1
1142
1223
  box = Box(self.width*self.unit,
1143
1224
  self.length*self.unit,
1144
1225
  h*self.unit,
1145
- position=(x0, y0, z0+z1*self.unit))
1146
- box.material = self.material
1226
+ position=(x0, y0, z0+z1*self.unit),
1227
+ name=f'{self.name}_layer{i}')
1228
+ box.material = layer.mat
1147
1229
  box = change_coordinate_system(box, self.cs)
1148
1230
  box.prio_set(self.dielectric_priority)
1149
1231
  boxes.append(box)
1150
- if merge:
1232
+ if merge and Nmats == 1:
1151
1233
  return GeoVolume.merged(boxes).prio_set(self.dielectric_priority) # type: ignore
1152
1234
  return boxes # type: ignore
1153
1235
 
1154
1236
  box = Box(self.width*self.unit,
1155
1237
  self.length*self.unit,
1156
1238
  self.thickness*self.unit,
1157
- position=(x0,y0,z0-self.thickness*self.unit))
1158
- 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
1159
1242
  box.prio_set(self.dielectric_priority)
1160
1243
  box = change_coordinate_system(box, self.cs)
1161
1244
  return box # type: ignore
1162
1245
 
1163
- def generate_air(self, height: float) -> GeoVolume:
1246
+ def generate_air(self, height: float, name: str = 'PCBAirbox') -> GeoVolume:
1164
1247
  """Generate the Air Block object
1165
1248
 
1166
1249
  This requires that the width, depth and origin are deterimed. This
@@ -1173,7 +1256,8 @@ class PCB:
1173
1256
  box = Box(self.width*self.unit,
1174
1257
  self.length*self.unit,
1175
1258
  height*self.unit,
1176
- position=(x0,y0,z0))
1259
+ position=(x0,y0,z0),
1260
+ name=name)
1177
1261
  box = change_coordinate_system(box, self.cs)
1178
1262
  return box # type: ignore
1179
1263
 
@@ -1182,7 +1266,8 @@ class PCB:
1182
1266
  y: float,
1183
1267
  width: float,
1184
1268
  direction: tuple[float, float],
1185
- z: float = 0) -> StripPath:
1269
+ z: float = 0,
1270
+ name: str | None = None) -> StripPath:
1186
1271
  """Start a new trace
1187
1272
 
1188
1273
  The trace is started at the provided x,y, coordinates with a width "width".
@@ -1201,12 +1286,12 @@ class PCB:
1201
1286
  >>> PCB.new(...).straight(...).turn(...).straight(...) etc.
1202
1287
 
1203
1288
  """
1204
- path = StripPath(self)
1289
+ path = StripPath(self, name=name)
1205
1290
  path.init(x, y, width, direction, z=z)
1206
1291
  self.paths.append(path)
1207
1292
  return path
1208
1293
 
1209
- 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:
1210
1295
  """Generate a lumped-port object to be created.
1211
1296
 
1212
1297
  Args:
@@ -1238,7 +1323,7 @@ class PCB:
1238
1323
 
1239
1324
  tag_wire = gmsh.model.occ.addWire(ltags)
1240
1325
  planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
1241
- poly = GeoPolygon([planetag,])
1326
+ poly = GeoPolygon([planetag,], name='name')
1242
1327
  poly._aux_data['width'] = stripline.width*self.unit
1243
1328
  poly._aux_data['height'] = height*self.unit
1244
1329
  poly._aux_data['vdir'] = self.cs.zax
@@ -1246,19 +1331,18 @@ class PCB:
1246
1331
 
1247
1332
  return poly
1248
1333
 
1249
- def _lumped_element(self, poly: XYPolygon, function: Callable, width: float, length: float) -> None:
1250
-
1251
- 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)
1252
1336
  geopoly._aux_data['func'] = function
1253
1337
  geopoly._aux_data['width'] = width
1254
1338
  geopoly._aux_data['height'] = length
1255
1339
  self.lumped_elements.append(geopoly)
1256
1340
 
1257
-
1258
1341
  def modal_port(self,
1259
1342
  point: StripLine,
1260
1343
  height: float,
1261
1344
  width_multiplier: float = 5.0,
1345
+ name: str | None = 'ModalPort'
1262
1346
  ) -> GeoSurface:
1263
1347
  """Generate a wave-port as a GeoSurface.
1264
1348
 
@@ -1284,7 +1368,7 @@ class PCB:
1284
1368
  ax1 = np.array([ds[0], ds[1], 0])*self.unit*point.width*width_multiplier
1285
1369
  ax2 = np.array([0,0,1])*height*self.unit
1286
1370
 
1287
- 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)
1288
1372
  plate = change_coordinate_system(plate, self.cs)
1289
1373
  return plate # type: ignore
1290
1374
 
@@ -1323,7 +1407,8 @@ class PCB:
1323
1407
  xs: list[float],
1324
1408
  ys: list[float],
1325
1409
  z: float = 0,
1326
- material: Material = None) -> None:
1410
+ material: Material = None,
1411
+ name: str | None = None) -> None:
1327
1412
  """Add a custom polygon to the PCB
1328
1413
 
1329
1414
  Args:
@@ -1334,9 +1419,12 @@ class PCB:
1334
1419
  """
1335
1420
  if material is None:
1336
1421
  material = self.trace_material
1337
- self.polies.append(PCBPoly(xs, ys, z, material))
1422
+ poly = PCBPoly(xs, ys, z, material,name=name)
1423
+
1424
+ self.polies.append(poly)
1425
+
1338
1426
 
1339
- 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:
1340
1428
  """ Generates a GeoPoly out of a list of (x,y) coordinate tuples"""
1341
1429
  ptags = []
1342
1430
  for x,y in xys:
@@ -1350,7 +1438,8 @@ class PCB:
1350
1438
 
1351
1439
  tag_wire = gmsh.model.occ.addWire(ltags)
1352
1440
  planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
1353
- poly = GeoPolygon([planetag,])
1441
+ poly = GeoPolygon([planetag,], name=name)
1442
+ poly._store('thickness', self.thickness)
1354
1443
  return poly
1355
1444
 
1356
1445
  @overload
@@ -1400,7 +1489,7 @@ class PCB:
1400
1489
 
1401
1490
  for pcbpoly in self.polies:
1402
1491
  self.zs.append(pcbpoly.z)
1403
- poly = self._gen_poly(pcbpoly.xys, pcbpoly.z)
1492
+ poly = self._gen_poly(pcbpoly.xys, pcbpoly.z, name=pcbpoly.name)
1404
1493
  poly.material = pcbpoly.material
1405
1494
  polys.append(poly)
1406
1495
  xs, ys = zip(*pcbpoly.xys)
@@ -1417,14 +1506,4 @@ class PCB:
1417
1506
  polys = unite(*polys)
1418
1507
 
1419
1508
  return polys
1420
-
1421
- ############################################################
1422
- # DEPRICATED #
1423
- ############################################################
1424
-
1425
- class PCBLayouter(PCB):
1426
1509
 
1427
- def __init__(self, *args, **kwargs):
1428
- logger.warning('PCBLayouter will be depricated. Use PCB instead.')
1429
- super().__init__(*args, **kwargs)
1430
-