emerge 0.6.6__tar.gz → 0.6.7__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.6 → emerge-0.6.7}/.bumpversion.toml +1 -1
  2. {emerge-0.6.6 → emerge-0.6.7}/PKG-INFO +1 -1
  3. {emerge-0.6.6 → emerge-0.6.7}/emerge/__init__.py +1 -1
  4. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/__init__.py +1 -1
  5. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/operations.py +31 -4
  6. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/pcb.py +44 -9
  7. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/shapes.py +61 -26
  8. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/microwave_bc.py +2 -4
  9. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/microwave_data.py +13 -0
  10. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/pyvista/display.py +113 -15
  11. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/simmodel.py +10 -8
  12. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/solver.py +4 -4
  13. {emerge-0.6.6 → emerge-0.6.7}/examples/demo10_sgh.py +1 -1
  14. {emerge-0.6.6 → emerge-0.6.7}/examples/demo11_lumped_element_filter.py +1 -1
  15. {emerge-0.6.6 → emerge-0.6.7}/examples/demo12_mode_alignment.py +1 -1
  16. {emerge-0.6.6 → emerge-0.6.7}/examples/demo13_helix_antenna.py +1 -1
  17. {emerge-0.6.6 → emerge-0.6.7}/examples/demo14_boundary_selection.py +1 -1
  18. {emerge-0.6.6 → emerge-0.6.7}/examples/demo1_stepped_imp_filter.py +2 -2
  19. {emerge-0.6.6 → emerge-0.6.7}/examples/demo2_combline_filter.py +1 -1
  20. {emerge-0.6.6 → emerge-0.6.7}/examples/demo3_coupled_line_filter.py +1 -1
  21. {emerge-0.6.6 → emerge-0.6.7}/examples/demo4_patch_antenna.py +2 -2
  22. {emerge-0.6.6 → emerge-0.6.7}/examples/demo5_revolve.py +1 -1
  23. {emerge-0.6.6 → emerge-0.6.7}/examples/demo6_striplines_with_vias.py +1 -1
  24. {emerge-0.6.6 → emerge-0.6.7}/examples/demo7_periodic_cells.py +1 -1
  25. {emerge-0.6.6 → emerge-0.6.7}/examples/demo8_waveguide_bpf_synthesis.py +1 -1
  26. {emerge-0.6.6 → emerge-0.6.7}/examples/demo9_dielectric_resonator.py +1 -1
  27. {emerge-0.6.6 → emerge-0.6.7}/pyproject.toml +1 -1
  28. {emerge-0.6.6 → emerge-0.6.7}/uv.lock +1 -1
  29. {emerge-0.6.6 → emerge-0.6.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  30. {emerge-0.6.6 → emerge-0.6.7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  31. {emerge-0.6.6 → emerge-0.6.7}/.gitignore +0 -0
  32. {emerge-0.6.6 → emerge-0.6.7}/.opt +0 -0
  33. {emerge-0.6.6 → emerge-0.6.7}/.python-version +0 -0
  34. {emerge-0.6.6 → emerge-0.6.7}/LICENSE +0 -0
  35. {emerge-0.6.6 → emerge-0.6.7}/README.md +0 -0
  36. {emerge-0.6.6 → emerge-0.6.7}/THIRD_PARTY_LICENSES.md +0 -0
  37. {emerge-0.6.6 → emerge-0.6.7}/UMFPACK_Install_windows.md +0 -0
  38. {emerge-0.6.6 → emerge-0.6.7}/UMFPACK_installer_windows.py +0 -0
  39. {emerge-0.6.6 → emerge-0.6.7}/emerge/__main__.py +0 -0
  40. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/__init__.py +0 -0
  41. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/_cache_check.py +0 -0
  42. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/bc.py +0 -0
  43. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/const.py +0 -0
  44. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/coord.py +0 -0
  45. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/cs.py +0 -0
  46. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/dataset.py +0 -0
  47. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/__init__.py +0 -0
  48. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/femdata.py +0 -0
  49. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/index_interp.py +0 -0
  50. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/ned2_interp.py +0 -0
  51. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/nedelec2.py +0 -0
  52. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/elements/nedleg2.py +0 -0
  53. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/horn.py +0 -0
  54. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/modeler.py +0 -0
  55. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  56. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  57. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/pmlbox.py +0 -0
  58. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/polybased.py +0 -0
  59. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo/step.py +0 -0
  60. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geo2d.py +0 -0
  61. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/geometry.py +0 -0
  62. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/howto.py +0 -0
  63. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/logsettings.py +0 -0
  64. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/material.py +0 -0
  65. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mesh3d.py +0 -0
  66. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mesher.py +0 -0
  67. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mth/common_functions.py +0 -0
  68. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mth/integrals.py +0 -0
  69. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mth/optimized.py +0 -0
  70. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/mth/pairing.py +0 -0
  71. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/periodic.py +0 -0
  72. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/__init__.py +0 -0
  73. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/__init__.py +0 -0
  74. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  75. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/assembler.py +0 -0
  76. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
  77. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
  78. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
  79. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
  80. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
  81. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/microwave_3d.py +0 -0
  82. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  83. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
  84. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/sc.py +0 -0
  85. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  86. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  87. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  88. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/__init__.py +0 -0
  89. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/display.py +0 -0
  90. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  91. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  92. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  93. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot/simple_plots.py +0 -0
  94. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/plot.py +0 -0
  95. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/projects/__init__.py +0 -0
  96. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/projects/_gen_base.txt +0 -0
  97. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/projects/_load_base.txt +0 -0
  98. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/projects/generate_project.py +0 -0
  99. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/selection.py +0 -0
  100. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/simulation_data.py +0 -0
  101. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  102. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  103. {emerge-0.6.6 → emerge-0.6.7}/emerge/_emerge/system.py +0 -0
  104. {emerge-0.6.6 → emerge-0.6.7}/emerge/cli.py +0 -0
  105. {emerge-0.6.6 → emerge-0.6.7}/emerge/ext.py +0 -0
  106. {emerge-0.6.6 → emerge-0.6.7}/emerge/lib.py +0 -0
  107. {emerge-0.6.6 → emerge-0.6.7}/emerge/plot.py +0 -0
  108. {emerge-0.6.6 → emerge-0.6.7}/emerge/pyvista.py +0 -0
  109. {emerge-0.6.6 → emerge-0.6.7}/src/__init__.py +0 -0
  110. {emerge-0.6.6 → emerge-0.6.7}/src/_img/logo.jpeg +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "0.6.6"
2
+ current_version = "0.6.7"
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.6
3
+ Version: 0.6.7
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.6"
21
+ __version__ = "0.6.7"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -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, unite
22
+ from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite, expand_surface
23
23
  from .polybased import XYPolygon, GeoPrism, Disc, Curve
24
24
  from .step import STEPItems
@@ -60,7 +60,7 @@ def add(main: T, tool: T,
60
60
  main._exists = False
61
61
  if remove_tool:
62
62
  tool._exists = False
63
- return output # type: ignore
63
+ return output.set_material(main.material) # type: ignore
64
64
 
65
65
  def remove(main: T, tool: T,
66
66
  remove_object: bool = True,
@@ -90,7 +90,7 @@ def remove(main: T, tool: T,
90
90
  main._exists = False
91
91
  if remove_tool:
92
92
  tool._exists = False
93
- return output # type: ignore
93
+ return output.set_material(main.material) # type: ignore
94
94
 
95
95
  subtract = remove
96
96
 
@@ -122,7 +122,7 @@ def intersect(main: T, tool: T,
122
122
  main._exists = False
123
123
  if remove_tool:
124
124
  tool._exists = False
125
- return output #type:ignore
125
+ return output.set_material(main.material) #type:ignore
126
126
 
127
127
  def embed(main: GeoVolume, other: GeoSurface) -> None:
128
128
  ''' Embeds a surface into a volume in the GMSH model.
@@ -301,4 +301,31 @@ def unite(*objects: GeoObject) -> GeoObject:
301
301
  new_obj.set_material(main.material)
302
302
  new_obj.prio_set(main._priority)
303
303
 
304
- return new_obj
304
+ return new_obj
305
+
306
+ def expand_surface(surface: GeoSurface, distance: float) -> GeoSurface:
307
+ """EXPERIMENTAL: Expands an input surface. The surface must exist on a 2D plane.
308
+
309
+ The output surface does not inherit material properties.
310
+
311
+ If any problems occur, reach out through email.
312
+
313
+ Args:
314
+ surface (GeoSurface): The input surface to expand
315
+ distance (float): The exapansion distance
316
+
317
+ Returns:
318
+ GeoSurface: The output surface
319
+ """
320
+ surfs = []
321
+ for tag in surface.tags:
322
+ looptags, _ = gmsh.model.occ.get_curve_loops(tag)
323
+ new_curves = []
324
+ for looptag in looptags:
325
+ curve_tags = gmsh.model.occ.offset_curve(looptag, distance)
326
+ loop_tag = gmsh.model.occ.addCurveLoop([t for d,t in curve_tags])
327
+ new_curves.append(loop_tag)
328
+ surftag = gmsh.model.occ.addPlaneSurface(new_curves)
329
+ surfs.append(surftag)
330
+ surf = GeoSurface(surfs)
331
+ return surf
@@ -963,8 +963,18 @@ class PCB:
963
963
  Returns:
964
964
  float: the z-height
965
965
  """
966
+ if layer <= 0:
967
+ return self._zs[layer]
966
968
  return self._zs[layer-1]
967
969
 
970
+ @property
971
+ def top(self) -> float:
972
+ return self._zs[-1]
973
+
974
+ @property
975
+ def bottom(self) -> float:
976
+ return self._zs[0]
977
+
968
978
  def _get_z(self, element: RouteElement) -> float :
969
979
  """Return the z-height of a given Route Element
970
980
 
@@ -979,6 +989,31 @@ class PCB:
979
989
  return path.z
980
990
  raise RouteException('Requesting z-height of route element that is not contained in a path.')
981
991
 
992
+ def add_vias(self, *coordinates: tuple[float, float], radius: float,
993
+ z1: float | None = None,
994
+ z2: float | None = None,
995
+ segments: int = 6) -> None:
996
+ """Add a series of vias provided by a list of coordinates.
997
+
998
+ Make sure to define the radius explicitly, otherwise the radius gets interpreted as a coordinate:
999
+
1000
+ >>> pcb.add_vias((x1,y1), (x1,y2), radius=1)
1001
+
1002
+ Args:
1003
+ *coordinates (tuple(float, float)): A series of coordinates
1004
+ radius (float): The radius
1005
+ z1 (float | None, optional): The bottom z-coordinate. Defaults to None.
1006
+ z2 (float | None, optional): The top z-coordinate. Defaults to None.
1007
+ segments (int, optional): The number of segmets for the via. Defaults to 6.
1008
+ """
1009
+ if z1 is None:
1010
+ z1 = self.z(0)
1011
+ if z2 is None:
1012
+ z2 = self.z(-1)
1013
+
1014
+ for x,y in coordinates:
1015
+ self.vias.append(Via(x,y,z1,z2,radius,segments))
1016
+
982
1017
  def load(self, name: str) -> StripLine:
983
1018
  """Acquire the x,y, coordinate associated with the label name.
984
1019
 
@@ -1060,7 +1095,7 @@ class PCB:
1060
1095
  GeoSurface: _description_
1061
1096
  """
1062
1097
  if width is None or height is None or origin is None:
1063
- if self.width is None or self.length is None or self.origin:
1098
+ if self.width is None or self.length is None or self.origin is None:
1064
1099
  raise RouteException('Cannot define a plane with no possible definition of its size.')
1065
1100
  width = self.width
1066
1101
  height = self.length
@@ -1075,6 +1110,7 @@ class PCB:
1075
1110
 
1076
1111
  plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0)) # type: ignore
1077
1112
  plane = change_coordinate_system(plane, self.cs) # type: ignore
1113
+ plane.set_material(COPPER)
1078
1114
  return plane # type: ignore
1079
1115
 
1080
1116
  def generate_pcb(self,
@@ -1121,7 +1157,7 @@ class PCB:
1121
1157
  """Generate the Air Block object
1122
1158
 
1123
1159
  This requires that the width, depth and origin are deterimed. This
1124
- can either be done manually or via the .determine_boudns() method.
1160
+ can either be done manually or via the .determine_bounds() method.
1125
1161
 
1126
1162
  Returns:
1127
1163
  GeoVolume: The PCB Block
@@ -1357,20 +1393,19 @@ class PCB:
1357
1393
  poly = self._gen_poly(pcbpoly.xys, pcbpoly.z)
1358
1394
  poly.material = pcbpoly.material
1359
1395
  polys.append(poly)
1396
+ xs, ys = zip(*pcbpoly.xys)
1397
+ allx.extend(xs)
1398
+ ally.extend(ys)
1399
+
1360
1400
 
1361
1401
  self.xs = allx
1362
1402
  self.ys = ally
1363
1403
 
1364
1404
  self.traces = polys
1405
+
1365
1406
  if merge:
1366
1407
  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
1408
+
1374
1409
  return polys
1375
1410
 
1376
1411
  ############################################################
@@ -30,6 +30,7 @@ class Alignment(Enum):
30
30
  CENTER = 1
31
31
  CORNER = 2
32
32
 
33
+
33
34
  class Box(GeoVolume):
34
35
  """Creates a box volume object.
35
36
  Specify the alignment of the box with the provided position. The options are CORNER (default)
@@ -100,7 +101,7 @@ class Box(GeoVolume):
100
101
 
101
102
  tags = list(reduce(lambda a,b: a+b, tagslist))
102
103
  return FaceSelection(tags)
103
-
104
+
104
105
 
105
106
  class Sphere(GeoVolume):
106
107
  """Generates a sphere objected centered ont he position with the given radius
@@ -122,6 +123,7 @@ class Sphere(GeoVolume):
122
123
  x,y,z = position
123
124
  self.tags: list[int] = [gmsh.model.occ.addSphere(x,y,z,radius),]
124
125
 
126
+
125
127
  class XYPlate(GeoSurface):
126
128
  """Generates and XY-plane oriented plate
127
129
 
@@ -209,27 +211,43 @@ class Plate(GeoSurface):
209
211
  tags: list[int] = [gmsh.model.occ.addPlaneSurface([tag_wire,]),]
210
212
  super().__init__(tags)
211
213
 
214
+
212
215
  class Cylinder(GeoVolume):
216
+ """Generates a Cylinder object in 3D space.
217
+ The cylinder will always be placed in the origin of the provided CoordinateSystem.
218
+ The bottom cylinder plane is always placed in the XY-plane. The length of the cylinder is
219
+ oriented along the Z-axis.
213
220
 
221
+ By default the cylinder uses the Open Cascade modeling for a cylinder. In this representation
222
+ the surface of the cylinder is approximated with a tolerance thay may be irregular.
223
+ As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
224
+ by an extrusion of a regular N-sided polygon.
225
+
226
+ Args:
227
+ radius (float): The radius of the Cylinder
228
+ height (float): The height of the Cylinder
229
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
230
+ Nsections (int, optional): The number of sections. Defaults to None.
231
+ """
214
232
  def __init__(self,
215
233
  radius: float,
216
234
  height: float,
217
- cs: CoordinateSystem = None,
218
- Nsections: int = None):
235
+ cs: CoordinateSystem = GCS,
236
+ Nsections: int | None = None):
219
237
  """Generates a Cylinder object in 3D space.
220
- The cyllinder will always be placed in the origin of the provided CoordinateSystem.
221
- The bottom cyllinder plane is always placed in the XY-plane. The lenth of the cyllinder is
238
+ The cylinder will always be placed in the origin of the provided CoordinateSystem.
239
+ The bottom cylinder plane is always placed in the XY-plane. The length of the cylinder is
222
240
  oriented along the Z-axis.
223
241
 
224
- By default the cyllinder uses the Open Cascade modeling for a cyllinder. In this representation
225
- the surface of the cyllinder is approximated with a tolerance thay may be irregular.
242
+ By default the cylinder uses the Open Cascade modeling for a cylinder. In this representation
243
+ the surface of the cylinder is approximated with a tolerance thay may be irregular.
226
244
  As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
227
245
  by an extrusion of a regular N-sided polygon.
228
246
 
229
247
  Args:
230
248
  radius (float): The radius of the Cylinder
231
249
  height (float): The height of the Cylinder
232
- cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
250
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
233
251
  Nsections (int, optional): The number of sections. Defaults to None.
234
252
  """
235
253
  ax = cs.zax.np
@@ -270,33 +288,46 @@ class Cylinder(GeoVolume):
270
288
  xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
271
289
  return xo, yo, zo
272
290
 
291
+
273
292
  class CoaxCylinder(GeoVolume):
274
- """A coaxial cylinder with an inner and outer radius."""
275
-
293
+ """Generates a Coaxial cylinder object in 3D space.
294
+ The coaxial cylinder will always be placed in the origin of the provided CoordinateSystem.
295
+ The bottom coax plane is always placed in the XY-plane. The lenth of the coax is
296
+ oriented along the Z-axis.
297
+
298
+ By default the coax uses the Open Cascade modeling for a cylinder. In this representation
299
+ the surface of the cylinder is approximated with a tolerance thay may be irregular.
300
+ As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
301
+ by an extrusion of a regular N-sided polygon.
302
+
303
+ Args:
304
+ radius (float): The radius of the Cylinder
305
+ height (float): The height of the Cylinder
306
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
307
+ Nsections (int, optional): The number of sections. Defaults to None.
308
+ """
276
309
  def __init__(self,
277
310
  rout: float,
278
311
  rin: float,
279
312
  height: float,
280
- cs: CoordinateSystem = None,
281
- Nsections: int = None):
282
- """Generates a Coaxial cyllinder object in 3D space.
283
- The coaxial cyllinder will always be placed in the origin of the provided CoordinateSystem.
313
+ cs: CoordinateSystem = GCS,
314
+ Nsections: int | None = None):
315
+ """Generates a Coaxial cylinder object in 3D space.
316
+ The coaxial cylinder will always be placed in the origin of the provided CoordinateSystem.
284
317
  The bottom coax plane is always placed in the XY-plane. The lenth of the coax is
285
318
  oriented along the Z-axis.
286
319
 
287
- By default the coax uses the Open Cascade modeling for a cyllinder. In this representation
288
- the surface of the cyllinder is approximated with a tolerance thay may be irregular.
320
+ By default the coax uses the Open Cascade modeling for a cylinder. In this representation
321
+ the surface of the cylinder is approximated with a tolerance thay may be irregular.
289
322
  As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
290
323
  by an extrusion of a regular N-sided polygon.
291
324
 
292
325
  Args:
293
326
  radius (float): The radius of the Cylinder
294
327
  height (float): The height of the Cylinder
295
- cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
328
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
296
329
  Nsections (int, optional): The number of sections. Defaults to None.
297
330
  """
298
- if cs is None:
299
- cs = GCS
300
331
  if rout <= rin:
301
332
  raise ValueError("Outer radius must be greater than inner radius.")
302
333
 
@@ -336,7 +367,7 @@ class CoaxCylinder(GeoVolume):
336
367
  return xo, yo, zo
337
368
 
338
369
  class HalfSphere(GeoVolume):
339
-
370
+ """A half sphere volume."""
340
371
  def __init__(self,
341
372
  radius: float,
342
373
  position: tuple = (0,0,0),
@@ -362,8 +393,6 @@ class HalfSphere(GeoVolume):
362
393
  self._add_face_pointer('back',np.array(position), np.array(direction))
363
394
  self._add_face_pointer('bottom',np.array(position), np.array(direction))
364
395
  self._add_face_pointer('face',np.array(position), np.array(direction))
365
-
366
-
367
396
 
368
397
 
369
398
  class OldBox(GeoVolume):
@@ -389,8 +418,6 @@ class OldBox(GeoVolume):
389
418
  alignment (Alignment, optional): Which point of the box is placed at the position.
390
419
  Defaults to Alignment.CORNER.
391
420
  """
392
-
393
-
394
421
  if alignment is Alignment.CORNER:
395
422
  position = (position[0]+width/2, position[1]+depth/2, position[2])
396
423
  elif alignment is Alignment.CENTER:
@@ -472,7 +499,6 @@ class OldBox(GeoVolume):
472
499
  self._add_face_pointer('right', pc + width/2*wax, wax)
473
500
  self._add_face_pointer('top', pc + height/2*hax, hax)
474
501
  self._add_face_pointer('bottom', pc - height/2*hax, -hax)
475
-
476
502
 
477
503
  def outside(self, *exclude: Literal['bottom','top','right','left','front','back']) -> FaceSelection:
478
504
  """Select all outside faces except for the once specified by outside
@@ -485,8 +511,17 @@ class OldBox(GeoVolume):
485
511
  tags = list(reduce(lambda a,b: a+b, tagslist))
486
512
  return FaceSelection(tags)
487
513
 
514
+
488
515
  class Cone(GeoVolume):
489
-
516
+ """Constructis a cone that starts at position p0 and is aimed in the given direction.
517
+ r1 is the start radius and r2 the end radius. The magnitude of direction determines its length.
518
+
519
+ Args:
520
+ p0 (tuple[float, float, float]): _description_
521
+ direction (tuple[float, float, float]): _description_
522
+ r1 (float): _description_
523
+ r2 (float): _description_
524
+ """
490
525
  def __init__(self, p0: tuple[float, float, float],
491
526
  direction: tuple[float, float, float],
492
527
  r1: float,
@@ -787,7 +787,6 @@ class LumpedPort(PortBC):
787
787
  width: float | None = None,
788
788
  height: float | None = None,
789
789
  direction: Axis | None = None,
790
- Idirection: Axis | None = None,
791
790
  active: bool = False,
792
791
  power: float = 1,
793
792
  Z0: float = 50):
@@ -814,7 +813,7 @@ class LumpedPort(PortBC):
814
813
  if width is None:
815
814
  if not isinstance(face, GeoObject):
816
815
  raise ValueError(f'The width, height and direction must be defined. Information cannot be extracted from {face}')
817
- width, height, direction, Idirection = face._data('width','height','vdir', 'idir')
816
+ width, height, direction = face._data('width','height','vdir')
818
817
  if width is None or height is None or direction is None:
819
818
  raise ValueError(f'The width, height and direction could not be extracted from {face}')
820
819
 
@@ -828,7 +827,6 @@ class LumpedPort(PortBC):
828
827
  self.width: float = width
829
828
  self.height: float = height # type: ignore
830
829
  self.Vdirection: Axis = direction # type: ignore
831
- self.Idirection: Axis = Idirection # type: ignore
832
830
  self.type = 'TEM'
833
831
 
834
832
  logger.info('Constructing coordinate system from normal port')
@@ -836,7 +834,6 @@ class LumpedPort(PortBC):
836
834
 
837
835
  self.vintline: Line | None = None
838
836
  self.v_integration = True
839
- self.iintline: Line | None = None
840
837
 
841
838
  @property
842
839
  def surfZ(self) -> float:
@@ -923,6 +920,7 @@ class LumpedPort(PortBC):
923
920
  Exg, Eyg, Ezg = self.cs.in_global_basis(Ex, Ey, Ez)
924
921
  return np.array([Exg, Eyg, Ezg])
925
922
 
923
+
926
924
  class LumpedElement(RobinBC):
927
925
 
928
926
  _include_stiff: bool = True
@@ -731,6 +731,19 @@ class MWField:
731
731
  x: float | None = None,
732
732
  y: float | None = None,
733
733
  z: float | None = None) -> EHField:
734
+ """Create a cartesian cut plane (XY, YZ or XZ) and compute the E and H-fields there
735
+
736
+ Only one coordiante and thus cutplane may be defined. If multiple are defined only the last (x->y->z) is used.
737
+
738
+ Args:
739
+ ds (float): The discretization step size
740
+ x (float | None, optional): The X-coordinate in case of a YZ-plane. Defaults to None.
741
+ y (float | None, optional): The Y-coordinate in case of an XZ-plane. Defaults to None.
742
+ z (float | None, optional): The Z-coordinate in case of an XY-plane. Defaults to None.
743
+
744
+ Returns:
745
+ EHField: The resultant EHField object
746
+ """
734
747
  xb, yb, zb = self.basis.bounds
735
748
  xs = np.linspace(xb[0], xb[1], int((xb[1]-xb[0])/ds))
736
749
  ys = np.linspace(yb[0], yb[1], int((yb[1]-yb[0])/ds))
@@ -26,7 +26,7 @@ from typing import Iterable, Literal, Callable, Any
26
26
  from ..display import BaseDisplay
27
27
  from .display_settings import PVDisplaySettings
28
28
  from matplotlib.colors import ListedColormap
29
-
29
+ from itertools import cycle
30
30
  ### Color scale
31
31
 
32
32
  # Define the colors we want to use
@@ -40,6 +40,40 @@ cmap_names = Literal['bgy','bgyw','kbc','blues','bmw','bmy','kgy','gray','dimgra
40
40
  'bkr','bky','coolwarm','gwv','bjy','bwy','cwr','colorwheel','isolum','rainbow','fire',
41
41
  'cet_fire','gouldian','kbgyw','cwr','CET_CBL1','CET_CBL3','CET_D1A']
42
42
 
43
+
44
+ def _gen_c_cycle():
45
+ colors = [
46
+ "#0000aa",
47
+ "#aa0000",
48
+ "#009900",
49
+ "#990099",
50
+ "#994400",
51
+ "#005588"
52
+ ]
53
+ return cycle(colors)
54
+
55
+ C_CYCLE = _gen_c_cycle()
56
+
57
+ class _RunState:
58
+
59
+ def __init__(self):
60
+ self.state: bool = False
61
+ self.ctr: int = 0
62
+
63
+
64
+ def run(self):
65
+ self.state = True
66
+ self.ctr = 0
67
+
68
+ def stop(self):
69
+ self.state = False
70
+ self.ctr = 0
71
+
72
+ def step(self):
73
+ self.ctr += 1
74
+
75
+ ANIM_STATE = _RunState()
76
+
43
77
  def gen_cmap(mesh, N: int = 256):
44
78
  # build a linear grid of data‐values (not strictly needed for pure colormap)
45
79
  vmin, vmax = mesh['values'].min(), mesh['values'].max()
@@ -201,6 +235,7 @@ class PVDisplay(BaseDisplay):
201
235
  self._stop: bool = False
202
236
  self._objs: list[_AnimObject] = []
203
237
  self._do_animate: bool = False
238
+ self._closed_via_x: bool = False
204
239
  self._Nsteps: int = 0
205
240
  self._fps: int = 25
206
241
  self._ruler: ScreenRuler = ScreenRuler(self, 0.001)
@@ -216,6 +251,15 @@ class PVDisplay(BaseDisplay):
216
251
  self._ctr: int = 0
217
252
 
218
253
  self.camera_position = (1, -1, 1) # +X, +Z, -Y
254
+
255
+ def _wire_close_events(self):
256
+ self._closed = False
257
+
258
+ def mark_closed(*_):
259
+ self._closed = True
260
+ self._stop = True
261
+
262
+ self._plot.add_key_event('q', lambda: mark_closed())
219
263
 
220
264
  def _update_camera(self):
221
265
  x,y,z = self._plot.camera.position
@@ -241,8 +285,12 @@ class PVDisplay(BaseDisplay):
241
285
  self._update_camera()
242
286
  self._add_aux_items()
243
287
  if self._do_animate:
288
+ self._wire_close_events()
289
+ self.add_text('Press Q to close!',color='red', position='upper_left')
244
290
  self._plot.show(auto_close=False, interactive_update=True, before_close_callback=self._close_callback)
245
291
  self._animate()
292
+
293
+
246
294
  else:
247
295
  self._plot.show()
248
296
  self._reset()
@@ -261,25 +309,55 @@ class PVDisplay(BaseDisplay):
261
309
  self._plot = pv.Plotter()
262
310
  self._stop = False
263
311
  self._objs = []
312
+ C_CYCLE = _gen_c_cycle()
264
313
 
265
- def _close_callback(self):
314
+ def _close_callback(self, arg):
266
315
  """The private callback function that stops the animation.
267
316
  """
268
317
  self._stop = True
318
+ print('CLOSE!')
269
319
 
270
320
  def _animate(self) -> None:
271
321
  """Private function that starts the animation loop.
272
322
  """
273
- self._plot.update()
274
- while not self._stop:
275
- for step in range(self._Nsteps):
276
- if self._stop:
277
- break
323
+
324
+ self._stop = False
325
+
326
+ # guard values
327
+ steps = max(1, int(self._Nsteps))
328
+ fps = max(1, int(self._fps))
329
+ dt = 1.0 / fps
330
+ next_tick = time.perf_counter()
331
+ step = 0
332
+
333
+ while (not self._stop
334
+ and not self._closed_via_x
335
+ and self._plot.render_window is not None):
336
+ # process window/UI events so close button works
337
+ self._plot.update()
338
+
339
+ now = time.perf_counter()
340
+ if now >= next_tick:
341
+ step = (step + 1) % steps
342
+ phi = np.exp(1j * (step / steps) * 2*np.pi)
343
+
344
+ # update all animated objects
278
345
  for aobj in self._objs:
279
- phi = np.exp(1j*(step/self._Nsteps)*2*np.pi)
280
346
  aobj.update(phi)
281
- self._plot.update()
282
- time.sleep(1/self._fps)
347
+
348
+ # draw one frame
349
+ self._plot.render()
350
+
351
+ # schedule next frame; catch up if we fell behind
352
+ next_tick += dt
353
+ if now > next_tick + dt:
354
+ next_tick = now + dt
355
+
356
+ # be kind to the CPU
357
+ time.sleep(0.001)
358
+
359
+ # ensure cleanup pathway runs once
360
+ self._close_callback(None)
283
361
 
284
362
  def animate(self, Nsteps: int = 35, fps: int = 25) -> PVDisplay:
285
363
  """ Turns on the animation mode with the specified number of steps and FPS.
@@ -357,11 +435,29 @@ class PVDisplay(BaseDisplay):
357
435
  return None
358
436
 
359
437
  ## OBLIGATORY METHODS
360
- def add_object(self, obj: GeoObject | Selection, *args, **kwargs):
361
- kwargs = setdefault(kwargs, color=obj.color_rgb, opacity=obj.opacity, silhouette=False, show_edges=False, pickable=True)
438
+ def add_object(self, obj: GeoObject | Selection, mesh: bool = False, volume_mesh: bool = True, *args, **kwargs):
439
+
440
+ show_edges = False
441
+ opacity = obj.opacity
442
+ line_width = 0.5
443
+ color = obj.color_rgb
444
+ style='surface'
445
+
446
+ if mesh is True:
447
+ show_edges = True
448
+ opacity = 0.7
449
+ line_width= 1
450
+ style='wireframe'
451
+ color=next(C_CYCLE)
452
+
453
+ kwargs = setdefault(kwargs, color=color, opacity=opacity, line_width=line_width, show_edges=show_edges, pickable=True, style=style)
454
+ mesh_obj = self.mesh(obj)
362
455
 
363
- actor = self._plot.add_mesh(self.mesh(obj), *args, **kwargs)
364
- self._plot.add_mesh(self._volume_edges(_select(obj)), color='#000000', line_width=1, show_edges=True)
456
+ if mesh is True and volume_mesh is True:
457
+ mesh_obj = mesh_obj.extract_all_edges()
458
+
459
+ actor = self._plot.add_mesh(mesh_obj, *args, **kwargs)
460
+ self._plot.add_mesh(self._volume_edges(_select(obj)), color='#000000', line_width=2, show_edges=True)
365
461
 
366
462
  def add_scatter(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray):
367
463
  """Adds a scatter point cloud
@@ -501,7 +597,7 @@ class PVDisplay(BaseDisplay):
501
597
  if self._do_animate:
502
598
  def on_update(obj: _AnimObject, phi: complex):
503
599
  field = obj.T(np.real(obj.field*phi))
504
- obj.grid['anim'] = field
600
+ obj.grid[name] = field
505
601
  self._objs.append(_AnimObject(field_flat, T, grid, actor, on_update)) # type: ignore
506
602
 
507
603
 
@@ -524,6 +620,8 @@ class PVDisplay(BaseDisplay):
524
620
  if abs_position is not None:
525
621
  final_position = abs_position
526
622
  viewport = True
623
+ else:
624
+ final_position = abs_position
527
625
  self._plot.add_text(
528
626
  text,
529
627
  position=final_position,
@@ -319,24 +319,26 @@ class Simulation:
319
319
  def view(self,
320
320
  selections: list[Selection] | None = None,
321
321
  use_gmsh: bool = False,
322
- volume_opacity: float = 0.1,
323
- surface_opacity: float = 1,
324
- show_edges: bool = True) -> None:
322
+ plot_mesh: bool = False,
323
+ volume_mesh: bool = True,
324
+ opacity: float | None = None) -> None:
325
325
  """View the current geometry in either the BaseDisplay object (PVDisplay only) or
326
326
  the GMSH viewer.
327
327
 
328
328
  Args:
329
- selections (list[Selection], optional): Additional selections to highlight. Defaults to None.
330
- use_gmsh (bool, optional): Whether to use the GMSH display. Defaults to False.
331
- opacity (float, optional): The global opacity of all objects.. Defaults to None.
332
- show_edges (bool, optional): Whether to show the geometry edges. Defaults to None.
329
+ selections (list[Selection] | None, optional): Optional selections to highlight. Defaults to None.
330
+ use_gmsh (bool, optional): If GMSH's GUI should be used. Defaults to False.
331
+ plot_mesh (bool, optional): If the mesh should be plot instead of the object. Defaults to False.
332
+ volume_mesh (bool, optional): If the internal mesh should be plot instead of only the surface boundary mesh. Defaults to True
333
+ opacity (float | None, optional): The object/mesh opacity. Defaults to None.
334
+
333
335
  """
334
336
  if not (self.display is not None and self.mesh.defined) or use_gmsh:
335
337
  gmsh.model.occ.synchronize()
336
338
  gmsh.fltk.run()
337
339
  return
338
340
  for geo in _GEOMANAGER.all_geometries():
339
- self.display.add_object(geo)
341
+ self.display.add_object(geo, mesh=plot_mesh, opacity=opacity, volume_mesh=volume_mesh)
340
342
  if selections:
341
343
  [self.display.add_object(sel, color='red', opacity=0.3) for sel in selections]
342
344
  self.display.show()
@@ -674,7 +674,7 @@ class SolverARPACK(EigSolver):
674
674
  target_k0: float = 0,
675
675
  which: str = 'LM',
676
676
  sign: float = 1.0) -> tuple[np.ndarray, np.ndarray]:
677
- logger.info(f'Searching around β = {target_k0:.2f} rad/m')
677
+ logger.info(f'Searching around β = {target_k0:.2f} rad/m with ARPACK')
678
678
  sigma = sign*(target_k0**2)
679
679
  eigen_values, eigen_modes = eigs(A, k=nmodes, M=B, sigma=sigma, which=which)
680
680
  return eigen_values, eigen_modes
@@ -698,7 +698,7 @@ class SmartARPACK_BMA(EigSolver):
698
698
  which: str = 'LM',
699
699
  sign: float = 1.) -> tuple[np.ndarray, np.ndarray]:
700
700
 
701
- logger.info(f'Searching around β = {target_k0:.2f} rad/m')
701
+ logger.info(f'Searching around β = {target_k0:.2f} rad/m with SmartARPACK (BMA)')
702
702
  qs = np.geomspace(1, self.search_range, self.symmetric_steps)
703
703
  tot_eigen_values = []
704
704
  tot_eigen_modes = []
@@ -747,7 +747,7 @@ class SmartARPACK(EigSolver):
747
747
  target_k0: float = 0,
748
748
  which: str = 'LM',
749
749
  sign: float = 1.) -> tuple[np.ndarray, np.ndarray]:
750
- logger.info(f'Searching around β = {target_k0:.2f} rad/m')
750
+ logger.info(f'Searching around β = {target_k0:.2f} rad/m with SmartARPACK')
751
751
  qs = np.geomspace(1, self.search_range, self.symmetric_steps)
752
752
  tot_eigen_values = []
753
753
  tot_eigen_modes = []
@@ -1062,7 +1062,7 @@ class SolveRoutine:
1062
1062
  if direct or A.shape[0] < 1000:
1063
1063
  return self.solvers[EMSolver.LAPACK] # type: ignore
1064
1064
  else:
1065
- return self.solvers[EMSolver.ARPACK] # type: ignore
1065
+ return self.solvers[EMSolver.SMART_ARPACK_BMA] # type: ignore
1066
1066
 
1067
1067
  def solve(self, A: csr_matrix | csr_matrix,
1068
1068
  b: np.ndarray,
@@ -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.6") # Checks version compatibility.
32
+ m.check_version("0.6.7") # Checks version compatibility.
33
33
  # --- Coordinate system for horn geometry -------------------------------
34
34
  hornCS = em.CS(em.YAX, em.ZAX, em.XAX)
35
35
 
@@ -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.6") # Checks version compatibility.
32
+ m.check_version("0.6.7") # Checks version compatibility.
33
33
 
34
34
  th = 0.5 # substrate thickness (meters)
35
35
  Hair = 2.0
@@ -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.6") # Checks version compatibility.
15
+ m.check_version("0.6.7") # 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.6") # Checks version compatibility.
28
+ model.check_version("0.6.7") # Checks version compatibility.
29
29
 
30
30
  dfeed = 3*mm # straight feed length before the helix starts
31
31
 
@@ -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.6") # Checks version compatibility.
40
+ model.check_version("0.6.7") # 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))
@@ -23,12 +23,12 @@ Hair = 60
23
23
  ## Material definition
24
24
 
25
25
  # We can define the material using the Material class. Just supply the dielectric properties and you are done!
26
- pcbmat = em.Material(er=2.2, tand=0.00, color="#217627", opacity=0.2)
26
+ pcbmat = em.Material(er=er, tand=0.00, color="#217627", opacity=0.2)
27
27
 
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.6") # Checks version compatibility.
31
+ m.check_version("0.6.7") # 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.6") # Checks version compatibility.
46
+ model.check_version("0.6.7") # 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.6") # Checks version compatibility.
41
+ model.check_version("0.6.7") # 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.6") # Checks version compatibility.
39
+ model.check_version("0.6.7") # 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,
@@ -138,4 +138,4 @@ ff3d = data.field[1].farfield_3d(boundary_selection)
138
138
  surf = ff3d.surfplot('normE', rmax=60 * mm,
139
139
  offset=(0, 0, 20 * mm))
140
140
  model.display.add_surf(*surf, cmap='viridis', symmetrize=False)
141
- model.display.show()
141
+ model.display.show()
@@ -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.6") # Checks version compatibility.
7
+ model.check_version("0.6.7") # 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.6") # Checks version compatibility.
15
+ model.check_version("0.6.7") # 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)
@@ -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.6") # Checks version compatibility.
32
+ model.check_version("0.6.7") # 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.6") # Checks version compatibility.
83
+ sim.check_version("0.6.7") # 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.6") # Checks version compatibility.
36
+ model.check_version("0.6.7") # 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.6"
6
+ version = "0.6.7"
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.6"
239
+ version = "0.6.7"
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