emerge 0.6.5__tar.gz → 0.6.6__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 (110) hide show
  1. {emerge-0.6.5 → emerge-0.6.6}/.bumpversion.toml +1 -1
  2. {emerge-0.6.5 → emerge-0.6.6}/PKG-INFO +1 -1
  3. {emerge-0.6.5 → emerge-0.6.6}/emerge/__init__.py +2 -1
  4. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/__init__.py +1 -1
  5. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/operations.py +67 -20
  6. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/pcb.py +17 -12
  7. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/shapes.py +40 -4
  8. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geometry.py +45 -14
  9. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/howto.py +1 -1
  10. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/selection.py +1 -0
  11. {emerge-0.6.5 → emerge-0.6.6}/examples/demo10_sgh.py +2 -2
  12. {emerge-0.6.5 → emerge-0.6.6}/examples/demo11_lumped_element_filter.py +2 -2
  13. {emerge-0.6.5 → emerge-0.6.6}/examples/demo12_mode_alignment.py +1 -1
  14. {emerge-0.6.5 → emerge-0.6.6}/examples/demo13_helix_antenna.py +3 -3
  15. {emerge-0.6.5 → emerge-0.6.6}/examples/demo14_boundary_selection.py +2 -2
  16. {emerge-0.6.5 → emerge-0.6.6}/examples/demo1_stepped_imp_filter.py +1 -1
  17. {emerge-0.6.5 → emerge-0.6.6}/examples/demo2_combline_filter.py +1 -1
  18. {emerge-0.6.5 → emerge-0.6.6}/examples/demo3_coupled_line_filter.py +1 -1
  19. {emerge-0.6.5 → emerge-0.6.6}/examples/demo4_patch_antenna.py +2 -2
  20. {emerge-0.6.5 → emerge-0.6.6}/examples/demo5_revolve.py +1 -1
  21. {emerge-0.6.5 → emerge-0.6.6}/examples/demo6_striplines_with_vias.py +4 -4
  22. {emerge-0.6.5 → emerge-0.6.6}/examples/demo7_periodic_cells.py +1 -1
  23. {emerge-0.6.5 → emerge-0.6.6}/examples/demo8_waveguide_bpf_synthesis.py +1 -1
  24. {emerge-0.6.5 → emerge-0.6.6}/examples/demo9_dielectric_resonator.py +1 -1
  25. {emerge-0.6.5 → emerge-0.6.6}/pyproject.toml +1 -1
  26. {emerge-0.6.5 → emerge-0.6.6}/uv.lock +1 -1
  27. {emerge-0.6.5 → emerge-0.6.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  28. {emerge-0.6.5 → emerge-0.6.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  29. {emerge-0.6.5 → emerge-0.6.6}/.gitignore +0 -0
  30. {emerge-0.6.5 → emerge-0.6.6}/.opt +0 -0
  31. {emerge-0.6.5 → emerge-0.6.6}/.python-version +0 -0
  32. {emerge-0.6.5 → emerge-0.6.6}/LICENSE +0 -0
  33. {emerge-0.6.5 → emerge-0.6.6}/README.md +0 -0
  34. {emerge-0.6.5 → emerge-0.6.6}/THIRD_PARTY_LICENSES.md +0 -0
  35. {emerge-0.6.5 → emerge-0.6.6}/UMFPACK_Install_windows.md +0 -0
  36. {emerge-0.6.5 → emerge-0.6.6}/UMFPACK_installer_windows.py +0 -0
  37. {emerge-0.6.5 → emerge-0.6.6}/emerge/__main__.py +0 -0
  38. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/__init__.py +0 -0
  39. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/_cache_check.py +0 -0
  40. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/bc.py +0 -0
  41. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/const.py +0 -0
  42. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/coord.py +0 -0
  43. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/cs.py +0 -0
  44. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/dataset.py +0 -0
  45. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/__init__.py +0 -0
  46. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/femdata.py +0 -0
  47. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/index_interp.py +0 -0
  48. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/ned2_interp.py +0 -0
  49. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/nedelec2.py +0 -0
  50. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/elements/nedleg2.py +0 -0
  51. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/horn.py +0 -0
  52. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/modeler.py +0 -0
  53. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  54. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  55. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/pmlbox.py +0 -0
  56. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/polybased.py +0 -0
  57. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo/step.py +0 -0
  58. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/geo2d.py +0 -0
  59. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/logsettings.py +0 -0
  60. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/material.py +0 -0
  61. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mesh3d.py +0 -0
  62. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mesher.py +0 -0
  63. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mth/common_functions.py +0 -0
  64. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mth/integrals.py +0 -0
  65. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mth/optimized.py +0 -0
  66. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/mth/pairing.py +0 -0
  67. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/periodic.py +0 -0
  68. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/__init__.py +0 -0
  69. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/__init__.py +0 -0
  70. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  71. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/assembler.py +0 -0
  72. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
  73. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
  74. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
  75. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
  76. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
  77. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/microwave_3d.py +0 -0
  78. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/microwave_bc.py +0 -0
  79. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/microwave_data.py +0 -0
  80. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  81. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
  82. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/sc.py +0 -0
  83. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  84. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  85. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  86. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/__init__.py +0 -0
  87. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/display.py +0 -0
  88. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  89. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  90. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/pyvista/display.py +0 -0
  91. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  92. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot/simple_plots.py +0 -0
  93. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/plot.py +0 -0
  94. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/projects/__init__.py +0 -0
  95. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/projects/_gen_base.txt +0 -0
  96. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/projects/_load_base.txt +0 -0
  97. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/projects/generate_project.py +0 -0
  98. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/simmodel.py +0 -0
  99. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/simulation_data.py +0 -0
  100. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  101. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  102. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/solver.py +0 -0
  103. {emerge-0.6.5 → emerge-0.6.6}/emerge/_emerge/system.py +0 -0
  104. {emerge-0.6.5 → emerge-0.6.6}/emerge/cli.py +0 -0
  105. {emerge-0.6.5 → emerge-0.6.6}/emerge/ext.py +0 -0
  106. {emerge-0.6.5 → emerge-0.6.6}/emerge/lib.py +0 -0
  107. {emerge-0.6.5 → emerge-0.6.6}/emerge/plot.py +0 -0
  108. {emerge-0.6.5 → emerge-0.6.6}/emerge/pyvista.py +0 -0
  109. {emerge-0.6.5 → emerge-0.6.6}/src/__init__.py +0 -0
  110. {emerge-0.6.5 → emerge-0.6.6}/src/_img/logo.jpeg +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "0.6.5"
2
+ current_version = "0.6.6"
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: 0.6.5
3
+ Version: 0.6.6
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
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "0.6.5"
21
+ __version__ = "0.6.6"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -52,6 +52,7 @@ from ._emerge.cs import CoordinateSystem, CS, GCS, Plane, Axis, XAX, YAX, ZAX, X
52
52
  from ._emerge.coord import Line
53
53
  from ._emerge import geo
54
54
  from ._emerge.selection import Selection, FaceSelection, DomainSelection, EdgeSelection
55
+ from ._emerge.geometry import select
55
56
  from ._emerge.mth.common_functions import norm, coax_rout, coax_rin
56
57
  from ._emerge.physics.microwave.sc import stratton_chu
57
58
  from ._emerge.periodic import RectCell, HexCell
@@ -19,6 +19,6 @@ from .pcb import PCB
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
22
- from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect
22
+ from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite
23
23
  from .polybased import XYPolygon, GeoPrism, Disc, Curve
24
24
  from .step import STEPItems
@@ -15,8 +15,8 @@
15
15
  # along with this program; if not, see
16
16
  # <https://www.gnu.org/licenses/>.
17
17
 
18
- from typing import TypeVar
19
- from ..geometry import GeoSurface, GeoVolume, GeoObject, GeoPoint, GeoEdge
18
+ from typing import TypeVar, overload
19
+ from ..geometry import GeoSurface, GeoVolume, GeoObject, GeoPoint, GeoEdge, GeoPolygon
20
20
  from ..cs import CoordinateSystem, GCS
21
21
  import gmsh
22
22
  import numpy as np
@@ -144,7 +144,8 @@ def rotate(main: GeoVolume,
144
144
  c0: tuple[float, float, float],
145
145
  ax: tuple[float, float, float],
146
146
  angle: float,
147
- degree=True) -> GeoVolume:
147
+ make_copy: bool = False,
148
+ degree=True) -> GeoObject:
148
149
  """Rotates a GeoVolume object around an axist defined at a coordinate.
149
150
 
150
151
  Args:
@@ -159,17 +160,23 @@ def rotate(main: GeoVolume,
159
160
  """
160
161
  if degree:
161
162
  angle = angle * np.pi/180
162
- gmsh.model.occ.rotate(main.dimtags, *c0, *ax, -angle)
163
-
163
+
164
+ if make_copy:
165
+ rotate_obj = main.make_copy()
166
+ else:
167
+ rotate_obj = main
168
+
169
+ gmsh.model.occ.rotate(rotate_obj.dimtags, *c0, *ax, -angle)
164
170
  # Rotate the facepointers
165
- for fp in main._all_pointers:
171
+ for fp in rotate_obj._all_pointers:
166
172
  fp.rotate(c0, ax, angle)
167
- return main
173
+ return rotate_obj
168
174
 
169
175
  def translate(main: GeoVolume,
170
176
  dx: float = 0,
171
177
  dy: float = 0,
172
- dz: float = 0) -> GeoVolume:
178
+ dz: float = 0,
179
+ make_copy: bool = False) -> GeoObject:
173
180
  """Translates the GeoVolume object along a given displacement
174
181
 
175
182
  Args:
@@ -177,17 +184,23 @@ def translate(main: GeoVolume,
177
184
  dx (float, optional): The X-displacement in meters. Defaults to 0.
178
185
  dy (float, optional): The Y-displacement in meters. Defaults to 0.
179
186
  dz (float, optional): The Z-displacement in meters. Defaults to 0.
187
+ make_copy (bool, optional): Whether to make a copy first before translating.
180
188
 
181
189
  Returns:
182
- GeoVolume: The translated object
190
+ GeoObject: The translated object
183
191
  """
184
- gmsh.model.occ.translate(main.dimtags, dx, dy, dz)
192
+
193
+ if make_copy:
194
+ trans_obj = main.make_copy()
195
+ else:
196
+ trans_obj = main
197
+ gmsh.model.occ.translate(trans_obj.dimtags, dx, dy, dz)
185
198
 
186
199
  # Rotate the facepointers
187
- for fp in main._all_pointers:
200
+ for fp in trans_obj._all_pointers:
188
201
  fp.translate(dx, dy, dz)
189
202
 
190
- return main
203
+ return trans_obj
191
204
 
192
205
  def mirror(main: GeoObject,
193
206
  origin: tuple[float, float, float] = (0.0, 0.0, 0.0),
@@ -199,6 +212,7 @@ def mirror(main: GeoObject,
199
212
  main (GeoVolume): The object to mirror
200
213
  origin (tuple[float, float, float], optional): The point of origin in meters. Defaults to (0.0, 0.0, 0.0).
201
214
  direction (tuple[float, float, float], optional): The normal axis defining the plane of reflection. Defaults to (1.0, 0.0, 0.0).
215
+ make_copy (bool, optional): Whether to make a copy first before mirroring.
202
216
 
203
217
  Returns:
204
218
  GeoVolume: The mirrored GeoVolume object
@@ -211,14 +225,12 @@ def mirror(main: GeoObject,
211
225
  d = -(a*x0 + b*y0 + c*z0)
212
226
  if (a==0) and (b==0) and (c==0):
213
227
  return main
228
+
214
229
  mirror_obj = main
215
230
  if make_copy:
216
- new_obj = main.make_copy()
217
- gmsh.model.occ.mirror(new_obj.dimtags, a,b,c,d)
218
- mirror_obj = new_obj
219
- else:
220
- gmsh.model.occ.mirror(main.dimtags, a,b,c,d)
221
-
231
+ mirror_obj = main.make_copy()
232
+ gmsh.model.occ.mirror(mirror_obj.dimtags, a,b,c,d)
233
+
222
234
  for fp in mirror_obj._all_pointers:
223
235
  fp.mirror(origin, direction)
224
236
  return mirror_obj
@@ -237,7 +249,7 @@ def change_coordinate_system(main: GeoObject,
237
249
  old_cs (CoordinateSystem, optional): The old coordinate system. Defaults to GCS.
238
250
 
239
251
  Returns:
240
- _type_: _description_
252
+ GeoObject: The output object
241
253
  """
242
254
  if new_cs._is_global and old_cs._is_global:
243
255
  return main
@@ -254,4 +266,39 @@ def change_coordinate_system(main: GeoObject,
254
266
  for fp in main._all_pointers:
255
267
  fp.affine_transform(M1)
256
268
  fp.affine_transform(M2)
257
- return main
269
+ return main
270
+
271
+ @overload
272
+ def unite(*objects: GeoVolume) -> GeoVolume: ...
273
+
274
+ @overload
275
+ def unite(*objects: GeoSurface) -> GeoSurface: ...
276
+
277
+ @overload
278
+ def unite(*objects: GeoEdge) -> GeoEdge: ...
279
+
280
+ @overload
281
+ def unite(*objects: GeoPolygon) -> GeoSurface: ...
282
+
283
+ def unite(*objects: GeoObject) -> GeoObject:
284
+ """Applies a fusion consisting of all geometries in the argument.
285
+
286
+ Returns:
287
+ GeoObject: The resultant object
288
+ """
289
+ main, *rest = objects
290
+ if not rest:
291
+ return main
292
+ main._exists = False
293
+ dts = []
294
+ for other in rest:
295
+ dts.extend(other.dimtags)
296
+ other._exists = False
297
+
298
+ new_dimtags, mapping = gmsh.model.occ.fuse(main.dimtags, dts)
299
+
300
+ new_obj = GeoObject.from_dimtags(new_dimtags)._take_tools(*objects)
301
+ new_obj.set_material(main.material)
302
+ new_obj.prio_set(main._priority)
303
+
304
+ return new_obj
@@ -22,7 +22,7 @@ from ..geometry import GeoPolygon, GeoVolume, GeoSurface
22
22
  from ..material import Material, AIR, COPPER
23
23
  from .shapes import Box, Plate, Cylinder
24
24
  from .polybased import XYPolygon
25
- from .operations import change_coordinate_system
25
+ from .operations import change_coordinate_system, unite
26
26
  from .pcb_tools.macro import parse_macro
27
27
  from .pcb_tools.calculator import PCBCalculator
28
28
 
@@ -310,7 +310,6 @@ class StripCurve(StripTurn):
310
310
  points: list[tuple[float, float]] = []
311
311
  Npts = int(np.ceil(abs(self.angle/self.dang)))
312
312
  R = self.radius-np.sign(self.angle)*self.width/2
313
- print(R, self.circ_origin, self._xhat, self._yhat)
314
313
  for i in range(Npts):
315
314
  ang = abs((i+1)/Npts * self.angle * np.pi/180)
316
315
  pnew = self.circ_origin + R*(self._xhat*np.cos(ang)+self._yhat*np.sin(ang))
@@ -324,7 +323,6 @@ class StripCurve(StripTurn):
324
323
 
325
324
  Npts = int(np.ceil(abs(self.angle/self.dang)))
326
325
  R = self.radius+np.sign(self.angle)*self.width/2
327
- print(R, self.circ_origin, self._xhat, self._yhat)
328
326
  for i in range(Npts):
329
327
  ang = abs((i+1)/Npts * self.angle * np.pi/180)
330
328
  pnew = self.circ_origin + R*(self._xhat*np.cos(ang)+self._yhat*np.sin(ang))
@@ -1247,7 +1245,13 @@ class PCB:
1247
1245
  plate = change_coordinate_system(plate, self.cs)
1248
1246
  return plate # type: ignore
1249
1247
 
1250
- def generate_vias(self, merge=False) -> list[Cylinder] | Cylinder:
1248
+ @overload
1249
+ def generate_vias(self, merge=Literal[True]) -> GeoVolume: ...
1250
+
1251
+ @overload
1252
+ def generate_vias(self, merge=Literal[False]) -> list[Cylinder]: ...
1253
+
1254
+ def generate_vias(self, merge=False) -> list[Cylinder] | GeoVolume:
1251
1255
  """Generates the via objects.
1252
1256
 
1253
1257
  Args:
@@ -1321,7 +1325,7 @@ class PCB:
1321
1325
  Returns:
1322
1326
  list[Polygon] | GeoSurface: The output stripline polygons possibly merged if merge = True.
1323
1327
  """
1324
- polys = []
1328
+ polys: list[GeoSurface] = []
1325
1329
  allx = []
1326
1330
  ally = []
1327
1331
 
@@ -1359,13 +1363,14 @@ class PCB:
1359
1363
 
1360
1364
  self.traces = polys
1361
1365
  if merge:
1362
- tags = []
1363
- for p in polys:
1364
- tags.extend(p.tags)
1365
- if p.material != COPPER:
1366
- logger.warning(f'Merging a polygon with material {p.material} into a single polygon that will be COPPER.')
1367
- polys = GeoSurface(tags)
1368
- polys.material = COPPER
1366
+ polys = unite(*polys)
1367
+ # tags = []
1368
+ # for p in polys:
1369
+ # tags.extend(p.tags)
1370
+ # if p.material != COPPER:
1371
+ # logger.warning(f'Merging a polygon with material {p.material} into a single polygon that will be COPPER.')
1372
+ # polys = GeoSurface(tags)
1373
+ # polys.material = COPPER
1369
1374
  return polys
1370
1375
 
1371
1376
  ############################################################
@@ -31,9 +31,18 @@ class Alignment(Enum):
31
31
  CORNER = 2
32
32
 
33
33
  class Box(GeoVolume):
34
- """ A class that represents a box shaped volume
34
+ """Creates a box volume object.
35
+ Specify the alignment of the box with the provided position. The options are CORNER (default)
36
+ for the front-left-bottom node of the box or CENTER for the center of the box.
35
37
 
36
- """
38
+ Args:
39
+ width (float): The x-size
40
+ depth (float): The y-size
41
+ height (float): The z-size
42
+ position (tuple, optional): The position of the box. Defaults to (0,0,0).
43
+ alignment (Alignment, optional): Which point of the box is placed at the position.
44
+ Defaults to Alignment.CORNER.
45
+ """
37
46
 
38
47
  def __init__(self,
39
48
  width: float,
@@ -94,7 +103,12 @@ class Box(GeoVolume):
94
103
 
95
104
 
96
105
  class Sphere(GeoVolume):
106
+ """Generates a sphere objected centered ont he position with the given radius
97
107
 
108
+ Args:
109
+ radius (float): The sphere radius
110
+ position (tuple, optional): The center position. Defaults to (0,0,0).
111
+ """
98
112
  def __init__(self,
99
113
  radius: float,
100
114
  position: tuple = (0,0,0)):
@@ -109,6 +123,17 @@ class Sphere(GeoVolume):
109
123
  self.tags: list[int] = [gmsh.model.occ.addSphere(x,y,z,radius),]
110
124
 
111
125
  class XYPlate(GeoSurface):
126
+ """Generates and XY-plane oriented plate
127
+
128
+ Specify the alignment of the plate with the provided position. The options are CORNER (default)
129
+ for the front-left node of the plate or CENTER for the center of the plate.
130
+
131
+ Args:
132
+ width (float): The x-size of the plate
133
+ depth (float): The y-size of the plate
134
+ position (tuple, optional): The position of the alignment node. Defaults to (0,0,0).
135
+ alignment (Alignment, optional): Which node to align to. Defaults to Alignment.CORNER.
136
+ """
112
137
  def __init__(self,
113
138
  width: float,
114
139
  depth: float,
@@ -134,7 +159,19 @@ class XYPlate(GeoSurface):
134
159
 
135
160
 
136
161
  class Plate(GeoSurface):
137
-
162
+ """A generalized 2D rectangular plate in XYZ-space.
163
+
164
+ The plate is specified by an origin (o) in meters coordinate plus two vectors (u,v) in meters
165
+ that span two of the sides such that all points of the plate are defined by:
166
+ p1 = o
167
+ p2 = o+u
168
+ p3 = o+v
169
+ p4 = o+u+v
170
+ Args:
171
+ origin (tuple[float, float, float]): The origin of the plate in meters
172
+ u (tuple[float, float, float]): The u-axis of the plate
173
+ v (tuple[float, float, float]): The v-axis of the plate
174
+ """
138
175
  def __init__(self,
139
176
  origin: tuple[float, float, float],
140
177
  u: tuple[float, float, float],
@@ -297,7 +334,6 @@ class CoaxCylinder(GeoVolume):
297
334
 
298
335
  xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
299
336
  return xo, yo, zo
300
- return super().boundary()
301
337
 
302
338
  class HalfSphere(GeoVolume):
303
339
 
@@ -411,8 +411,8 @@ class GeoObject:
411
411
  self._priority = _GEOMANAGER.highest_priority()+10
412
412
  return self
413
413
 
414
- def outside(self, *exclude: FaceNames, tags: list[int] | None = None) -> FaceSelection:
415
- """Returns the complete set of outside faces.
414
+ def boundary(self, exclude: tuple[FaceNames,...] | None = None, tags: list[int] | None = None) -> FaceSelection:
415
+ """Returns the complete set of boundary faces.
416
416
 
417
417
  If implemented, it is possible to exclude a set of faces based on their name
418
418
  or a list of tags (integers)
@@ -420,8 +420,13 @@ class GeoObject:
420
420
  Returns:
421
421
  FaceSelection: The selected faces
422
422
  """
423
+ if exclude is None:
424
+ exclude = tuple()
425
+
423
426
  if tags is None:
424
427
  tags = []
428
+
429
+
425
430
  for name in exclude:
426
431
  tags.extend(self.face(name).tags)
427
432
  dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
@@ -441,6 +446,22 @@ class GeoObject:
441
446
 
442
447
  return FaceSelection(self._face_tags(name, tool))
443
448
 
449
+ def faces(self, *names: FaceNames, tool: GeoObject | None = None) -> FaceSelection:
450
+ """Returns the FaceSelection for a given face names.
451
+
452
+ The face name must be defined for the type of geometry.
453
+
454
+ Args:
455
+ name (FaceNames): The name of the face to select.
456
+
457
+ Returns:
458
+ FaceSelection: The selected face
459
+ """
460
+ tags = []
461
+ for name in names:
462
+ tags.extend(self._face_tags(name, tool))
463
+ return FaceSelection(tags)
464
+
444
465
  @property
445
466
  def dimtags(self) -> list[tuple[int, int]]:
446
467
  return [(self.dim, tag) for tag in self.tags]
@@ -448,15 +469,6 @@ class GeoObject:
448
469
  @property
449
470
  def embeddings(self) -> list[tuple[int,int]]:
450
471
  return []
451
-
452
- def boundary(self) -> FaceSelection:
453
- if self.dim == 3:
454
- tags = gmsh.model.get_boundary(self.dimtags, oriented=False)
455
- return FaceSelection([t[1] for t in tags])
456
- if self.dim == 2:
457
- return FaceSelection(self.tags)
458
- else:
459
- raise ValueError('Can only generate faces for objects of dimension 2 or higher.')
460
472
 
461
473
  @staticmethod
462
474
  def from_dimtags(dimtags: list[tuple[int,int]]) -> GeoVolume | GeoSurface | GeoObject:
@@ -549,6 +561,25 @@ class GeoPolygon(GeoSurface):
549
561
  self.points: list[int] = []
550
562
  self.lines: list[int] = []
551
563
 
552
-
553
- T = TypeVar('T', GeoVolume, GeoEdge, GeoPoint, GeoSurface)
554
-
564
+
565
+ ############################################################
566
+ # SHORT FUNCTIONS #
567
+ ############################################################
568
+
569
+ def select(*items: Selection | GeoObject) -> Selection:
570
+ """Generate a selection from a series of selections and/or objects that share the same dimension.
571
+
572
+ Raises:
573
+ ValueError: Raised if the dimensions provided are not consistent
574
+
575
+ Returns:
576
+ Selection: An output selection object.
577
+ """
578
+ dim = items[0].dim
579
+ tags = []
580
+ for item in items:
581
+ if item.dim!=dim:
582
+ raise ValueError(f'Cannot group of objects with a dissimilar dimensions. Trying to include {item} in a list of dimension {dim}.')
583
+ tags.extend(item.tags)
584
+ return Selection.from_dim_tags(dim, tags)
585
+
@@ -69,7 +69,7 @@ class _HowtoClass:
69
69
 
70
70
  The naming convention is left(-X), right(+X), front(-Y), back(+Y), bottom(-Z), top(+Z)
71
71
  All outside faces can be selected using
72
- >>> outside = object.outside()
72
+ >>> outside = object.boundary()
73
73
 
74
74
  If objects are the results from operations, you can access the faces from the
75
75
  source objects using the optional tool argument
@@ -590,3 +590,4 @@ class Selector:
590
590
  return FaceSelection([lowest_key,])
591
591
 
592
592
  SELECTOR_OBJ = Selector()
593
+
@@ -29,7 +29,7 @@ dx = 2 * mm # distance from horn exit to PML start
29
29
 
30
30
  # Create simulation object
31
31
  m = em.Simulation('HornAntenna')
32
- m.check_version("0.6.5") # Checks version compatibility.
32
+ m.check_version("0.6.6") # Checks version compatibility.
33
33
  # --- Coordinate system for horn geometry -------------------------------
34
34
  hornCS = em.CS(em.YAX, em.ZAX, em.XAX)
35
35
 
@@ -81,7 +81,7 @@ m.generate_mesh()
81
81
  # --- Boundary conditions ------------------------------------------------
82
82
  p1 = m.mw.bc.ModalPort(feed.face('left'), 1) # excite TE10 in feed
83
83
  PMC = m.mw.bc.PMC(m.select.face.inplane(0, 0, 0, 0, 1, 0)) # perfect magnetic on symmetry
84
- radiation_boundary = air.outside('front', 'left', 'bottom') # open faces
84
+ radiation_boundary = air2.faces('back','top','right', tool=air) # open faces
85
85
  abc = m.mw.bc.AbsorbingBoundary(m.select.face.inplane(Lhorn-dx,0,0,1,0,0))
86
86
  # View mesh and BC selections
87
87
  m.view(selections=[p1.selection, PMC.selection, radiation_boundary])
@@ -29,7 +29,7 @@ def Cf(C):
29
29
  pack = '0603' # package footprint for lumped components
30
30
  # Create simulation and PCB layouter with substrate thickness and material
31
31
  m = em.Simulation('LumpedFilter')
32
- m.check_version("0.6.5") # Checks version compatibility.
32
+ m.check_version("0.6.6") # Checks version compatibility.
33
33
 
34
34
  th = 0.5 # substrate thickness (meters)
35
35
  Hair = 2.0
@@ -99,7 +99,7 @@ for le in LEs:
99
99
  m.mw.bc.LumpedElement(le)
100
100
  # Perfect conductor on copper traces and vias
101
101
  m.mw.bc.PEC(traces)
102
- m.mw.bc.PEC(vias.outside())
102
+ m.mw.bc.PEC(vias.boundary())
103
103
 
104
104
  # --- Run frequency-domain simulation ------------------------------------
105
105
  data = m.mw.run_sweep(parallel=True, njobs=4, frequency_groups=8)
@@ -12,7 +12,7 @@ direction (+Z vs -Z). In EMerge we can alignm modes using the .align_mode() meth
12
12
 
13
13
  # First we define our simulation
14
14
  m = em.Simulation('aligntest')
15
- m.check_version("0.6.5") # Checks version compatibility.
15
+ m.check_version("0.6.6") # Checks version compatibility.
16
16
 
17
17
  # We create a cyllindrical waveguide in the Y-axis.
18
18
  cyl = em.geo.Cylinder(0.012, 0.05, em.CS(em.XAX, em.ZAX, em.YAX))
@@ -25,7 +25,7 @@ porth = 2*mm # vertical height of the feed extrusion
25
25
 
26
26
  # --- Simulation object -------------------------------------------------------
27
27
  model = em.Simulation('helix')
28
- model.check_version("0.6.5") # Checks version compatibility.
28
+ model.check_version("0.6.6") # Checks version compatibility.
29
29
 
30
30
  dfeed = 3*mm # straight feed length before the helix starts
31
31
 
@@ -78,8 +78,8 @@ model.generate_mesh()
78
78
  model.view()
79
79
 
80
80
  # --- Boundary selections for BCs & port -------------------------------------
81
- abc_sel = airbox.outside('bottom') # absorbing boundary applied at bottom face of airbox
82
- port_sel = feed.outside('front','back')# port faces on the feed (two opposite faces)
81
+ abc_sel = airbox.boundary(exclude=('bottom',)) # absorbing boundary applied at bottom face of airbox
82
+ port_sel = feed.boundary(exclude=('front','back'))# port faces on the feed (two opposite faces)
83
83
 
84
84
  # --- Boundary conditions -----------------------------------------------------
85
85
  abc = model.mw.bc.AbsorbingBoundary(abc_sel) # open-space termination
@@ -37,7 +37,7 @@ wgb = 10.16*mm
37
37
  L = 50*mm
38
38
 
39
39
  model = em.Simulation('Test Mode')
40
- model.check_version("0.6.5") # Checks version compatibility.
40
+ model.check_version("0.6.6") # Checks version compatibility.
41
41
 
42
42
  # first lets define a WR90 waveguide
43
43
  wg_box = em.geo.Box(L, wga, wgb, position=(-L, -wga/2, -wgb/2))
@@ -71,7 +71,7 @@ feed_port = wg_box_new.face('left', tool=wg_box)
71
71
 
72
72
  # We can also select the outside and exclude a given face. Because our airbox
73
73
  # is not modified, we don't have to work with tools.
74
- radiation_boundary = airbox.outside('left')
74
+ radiation_boundary = airbox.boundary(exclude=('left',))
75
75
 
76
76
  # Lets view our result
77
77
  model.view(selections=[feed_port, radiation_boundary])
@@ -28,7 +28,7 @@ pcbmat = em.Material(er=2.2, tand=0.00, color="#217627", opacity=0.2)
28
28
  # We start by creating our simulation object.
29
29
 
30
30
  m = em.Simulation('Demo1_SIF', loglevel='INFO')
31
- m.check_version("0.6.5") # Checks version compatibility.
31
+ m.check_version("0.6.6") # Checks version compatibility.
32
32
  # To accomodate PCB routing we make use of the PCBLayouter class. To use it we need to
33
33
  # supply it with a thickness, the desired air-box height, the units at which we supply
34
34
  # the dimensions and the PCB material.
@@ -43,7 +43,7 @@ lfeed = 100*mil
43
43
 
44
44
  # A usual we start our simulation file
45
45
  model = em.Simulation('Combline_DEMO')
46
- model.check_version("0.6.5") # Checks version compatibility.
46
+ model.check_version("0.6.6") # Checks version compatibility.
47
47
 
48
48
  # The filter consists of quarter lamba cylindrical pins inside an airbox.
49
49
  # First we create the airbox
@@ -38,7 +38,7 @@ extra = 100 # extra margin (mil)
38
38
 
39
39
  # --- Simulation setup ----------------------------------------------------
40
40
  model = em.Simulation('Demo3')
41
- model.check_version("0.6.5") # Checks version compatibility.
41
+ model.check_version("0.6.6") # Checks version compatibility.
42
42
  # --- Material and layouter -----------------------------------------------
43
43
  mat = em.Material(er=3.55, color="#488343", opacity=0.1)
44
44
  # Create PCB layouter with given substrate thickness and units
@@ -36,7 +36,7 @@ f2 = 1.6e9 # stop frequency
36
36
  # --- Create simulation object -------------------------------------------
37
37
  # Using PVDisplay backend for 3D visualization
38
38
  model = em.Simulation('MyPatchAntenna')
39
- model.check_version("0.6.5") # Checks version compatibility.
39
+ model.check_version("0.6.6") # Checks version compatibility.
40
40
  # --- Define geometry primitives -----------------------------------------
41
41
  # Substrate block centered at origin in XY, thickness in Z (negative down)
42
42
  dielectric = em.geo.Box(wsub, hsub, th,
@@ -103,7 +103,7 @@ port = model.mw.bc.LumpedPort(
103
103
  active=True, Z0=50
104
104
  )
105
105
  # Apply absorbing boundary on underside of airbox to simulate open space
106
- boundary_selection = air.outside('bottom')
106
+ boundary_selection = air.boundary(exclude=('bottom',))
107
107
 
108
108
  model.view(selections=[boundary_selection,])
109
109
  abc = model.mw.bc.AbsorbingBoundary(boundary_selection)
@@ -4,7 +4,7 @@ import emerge as em
4
4
  For now it just shows you how to work with the revolve system.
5
5
  """
6
6
  model = em.Simulation('Revolve test')
7
- model.check_version("0.6.5") # Checks version compatibility.
7
+ model.check_version("0.6.6") # Checks version compatibility.
8
8
 
9
9
  mm = 0.001
10
10
  rad_feed = 30*mm
@@ -12,7 +12,7 @@ mm = 0.001
12
12
  th = 1
13
13
 
14
14
  model = em.Simulation('Stripline_test')
15
- model.check_version("0.6.5") # Checks version compatibility.
15
+ model.check_version("0.6.6") # Checks version compatibility.
16
16
 
17
17
  # As usual we start by creating our layouter
18
18
  ly = em.geo.PCB(th, mm, em.GCS, em.lib.DIEL_RO4350B)
@@ -59,9 +59,9 @@ model.mesher.set_boundary_size(trace, 0.001)
59
59
 
60
60
  model.generate_mesh()
61
61
 
62
- # We display the geometry with extra attention to the vias. With the vias.outside() method we can
62
+ # We display the geometry with extra attention to the vias. With the vias.boundary() method we can
63
63
  # specifically show the outside faces of the via.
64
- model.view(selections=[vias.outside()])
64
+ model.view(selections=[vias.boundary()])
65
65
 
66
66
  # We setup the lumped port boundary conditions. Because of an added functionality in the PCBLayouter
67
67
  # class, you don't have to specify the width, height and direction of the lumped port, this information
@@ -74,7 +74,7 @@ p2 = model.mw.bc.LumpedPort(lp2, 2)
74
74
  pec = model.mw.bc.PEC(trace)
75
75
 
76
76
  #We also add a PEC for the outsides of our via.
77
- pecvia = model.mw.bc.PEC(vias.outside())
77
+ pecvia = model.mw.bc.PEC(vias.boundary())
78
78
 
79
79
  # Finally we run the simulation!
80
80
  data = model.mw.run_sweep(True, 4, frequency_groups=8)
@@ -29,7 +29,7 @@ fl = 25*mm
29
29
 
30
30
  # We start again by defining our simulation model
31
31
  model = em.Simulation('Periodic')
32
- model.check_version("0.6.5") # Checks version compatibility.
32
+ model.check_version("0.6.6") # Checks version compatibility.
33
33
 
34
34
  # Next we will create a PeriodicCell class (in our case a hexagonal cell). This class
35
35
  # is simply meant to simplify our lives and improve the simulation setup flow.
@@ -80,7 +80,7 @@ wgaps = np.linspace(1*mm, 20*mm, 21)
80
80
  Ks = []
81
81
  hphis = []
82
82
  with em.Simulation('IrisSim') as sim:
83
- sim.check_version("0.6.5") # Checks version compatibility.
83
+ sim.check_version("0.6.6") # Checks version compatibility.
84
84
  for (wgap,) in sim.parameter_sweep(True, wgap=wgaps):
85
85
  # Define two short waveguide sections separated by iris plate
86
86
  wg1 = em.geo.Box(wga, Lfeed, wgb, (-wga/2, -Lfeed - t_thickness/2, 0))
@@ -33,7 +33,7 @@ Nmodes = 5
33
33
 
34
34
  # --- Create simulation ---------------------------------------------------
35
35
  model = em.Simulation('DielectricResonatorFilter')
36
- model.check_version("0.6.5") # Checks version compatibility.
36
+ model.check_version("0.6.6") # Checks version compatibility.
37
37
 
38
38
  # --- Build geometry ------------------------------------------------------
39
39
  # Metal enclosure box (PEC by default)
@@ -3,7 +3,7 @@ allow-direct-references = true
3
3
 
4
4
  [project]
5
5
  name = "emerge"
6
- version = "0.6.5"
6
+ version = "0.6.6"
7
7
  description = "An open source EM FEM simulator in Python"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10, <4.0"
@@ -236,7 +236,7 @@ wheels = [
236
236
 
237
237
  [[package]]
238
238
  name = "emerge"
239
- version = "0.6.5"
239
+ version = "0.6.6"
240
240
  source = { editable = "." }
241
241
  dependencies = [
242
242
  { name = "gmsh" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes