emerge 0.6.2__tar.gz → 0.6.5__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 (112) hide show
  1. emerge-0.6.5/.bumpversion.toml +72 -0
  2. {emerge-0.6.2 → emerge-0.6.5}/PKG-INFO +1 -1
  3. {emerge-0.6.2 → emerge-0.6.5}/emerge/__init__.py +1 -1
  4. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/polybased.py +118 -24
  5. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/microwave_3d.py +1 -1
  6. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/microwave_data.py +61 -8
  7. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/simmodel.py +26 -1
  8. {emerge-0.6.2 → emerge-0.6.5}/examples/demo10_sgh.py +2 -2
  9. {emerge-0.6.2 → emerge-0.6.5}/examples/demo11_lumped_element_filter.py +3 -1
  10. {emerge-0.6.2 → emerge-0.6.5}/examples/demo12_mode_alignment.py +2 -1
  11. {emerge-0.6.2 → emerge-0.6.5}/examples/demo13_helix_antenna.py +10 -4
  12. emerge-0.6.2/examples/demo4_boundary_selection.py → emerge-0.6.5/examples/demo14_boundary_selection.py +1 -0
  13. {emerge-0.6.2 → emerge-0.6.5}/examples/demo1_stepped_imp_filter.py +1 -1
  14. {emerge-0.6.2 → emerge-0.6.5}/examples/demo2_combline_filter.py +2 -2
  15. {emerge-0.6.2 → emerge-0.6.5}/examples/demo3_coupled_line_filter.py +2 -2
  16. emerge-0.6.2/examples/demo3_patch_antenna.py → emerge-0.6.5/examples/demo4_patch_antenna.py +2 -2
  17. {emerge-0.6.2 → emerge-0.6.5}/examples/demo5_revolve.py +1 -0
  18. {emerge-0.6.2 → emerge-0.6.5}/examples/demo6_striplines_with_vias.py +3 -1
  19. {emerge-0.6.2 → emerge-0.6.5}/examples/demo7_periodic_cells.py +2 -1
  20. {emerge-0.6.2 → emerge-0.6.5}/examples/demo8_waveguide_bpf_synthesis.py +4 -3
  21. {emerge-0.6.2 → emerge-0.6.5}/examples/demo9_dielectric_resonator.py +2 -1
  22. {emerge-0.6.2 → emerge-0.6.5}/pyproject.toml +1 -1
  23. {emerge-0.6.2 → emerge-0.6.5}/uv.lock +1 -1
  24. emerge-0.6.2/.bumpversion.toml +0 -30
  25. emerge-0.6.2/emerge/_emerge/geo/pipes.py +0 -62
  26. {emerge-0.6.2 → emerge-0.6.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  27. {emerge-0.6.2 → emerge-0.6.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  28. {emerge-0.6.2 → emerge-0.6.5}/.gitignore +0 -0
  29. {emerge-0.6.2 → emerge-0.6.5}/.opt +0 -0
  30. {emerge-0.6.2 → emerge-0.6.5}/.python-version +0 -0
  31. {emerge-0.6.2 → emerge-0.6.5}/LICENSE +0 -0
  32. {emerge-0.6.2 → emerge-0.6.5}/README.md +0 -0
  33. {emerge-0.6.2 → emerge-0.6.5}/THIRD_PARTY_LICENSES.md +0 -0
  34. {emerge-0.6.2 → emerge-0.6.5}/UMFPACK_Install_windows.md +0 -0
  35. {emerge-0.6.2 → emerge-0.6.5}/UMFPACK_installer_windows.py +0 -0
  36. {emerge-0.6.2 → emerge-0.6.5}/emerge/__main__.py +0 -0
  37. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/__init__.py +0 -0
  38. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/_cache_check.py +0 -0
  39. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/bc.py +0 -0
  40. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/const.py +0 -0
  41. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/coord.py +0 -0
  42. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/cs.py +0 -0
  43. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/dataset.py +0 -0
  44. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/__init__.py +0 -0
  45. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/femdata.py +0 -0
  46. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/index_interp.py +0 -0
  47. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/ned2_interp.py +0 -0
  48. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/nedelec2.py +0 -0
  49. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/elements/nedleg2.py +0 -0
  50. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/__init__.py +0 -0
  51. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/horn.py +0 -0
  52. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/modeler.py +0 -0
  53. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/operations.py +0 -0
  54. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/pcb.py +0 -0
  55. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  56. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  57. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/pmlbox.py +0 -0
  58. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/shapes.py +0 -0
  59. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo/step.py +0 -0
  60. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geo2d.py +0 -0
  61. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/geometry.py +0 -0
  62. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/howto.py +0 -0
  63. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/logsettings.py +0 -0
  64. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/material.py +0 -0
  65. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mesh3d.py +0 -0
  66. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mesher.py +0 -0
  67. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mth/common_functions.py +0 -0
  68. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mth/integrals.py +0 -0
  69. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mth/optimized.py +0 -0
  70. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/mth/pairing.py +0 -0
  71. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/periodic.py +0 -0
  72. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/__init__.py +0 -0
  73. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/__init__.py +0 -0
  74. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  75. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/assembler.py +0 -0
  76. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
  77. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
  78. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
  79. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
  80. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
  81. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/microwave_bc.py +0 -0
  82. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  83. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
  84. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/sc.py +0 -0
  85. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  86. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  87. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  88. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/__init__.py +0 -0
  89. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/display.py +0 -0
  90. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  91. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  92. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/pyvista/display.py +0 -0
  93. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  94. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot/simple_plots.py +0 -0
  95. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/plot.py +0 -0
  96. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/projects/__init__.py +0 -0
  97. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/projects/_gen_base.txt +0 -0
  98. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/projects/_load_base.txt +0 -0
  99. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/projects/generate_project.py +0 -0
  100. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/selection.py +0 -0
  101. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/simulation_data.py +0 -0
  102. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  103. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  104. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/solver.py +0 -0
  105. {emerge-0.6.2 → emerge-0.6.5}/emerge/_emerge/system.py +0 -0
  106. {emerge-0.6.2 → emerge-0.6.5}/emerge/cli.py +0 -0
  107. {emerge-0.6.2 → emerge-0.6.5}/emerge/ext.py +0 -0
  108. {emerge-0.6.2 → emerge-0.6.5}/emerge/lib.py +0 -0
  109. {emerge-0.6.2 → emerge-0.6.5}/emerge/plot.py +0 -0
  110. {emerge-0.6.2 → emerge-0.6.5}/emerge/pyvista.py +0 -0
  111. {emerge-0.6.2 → emerge-0.6.5}/src/__init__.py +0 -0
  112. {emerge-0.6.2 → emerge-0.6.5}/src/_img/logo.jpeg +0 -0
@@ -0,0 +1,72 @@
1
+ [tool.bumpversion]
2
+ current_version = "0.6.5"
3
+ parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
+ serialize = ["{major}.{minor}.{patch}"]
5
+ search = "{current_version}"
6
+ replace = "{new_version}"
7
+ regex = false
8
+ ignore_missing_version = false
9
+ ignore_missing_files = false
10
+ tag = true
11
+ sign_tags= false
12
+ tag_name = "v{new_version}"
13
+ tag_message = "Bump version: {current_version} → {new_version}"
14
+ allow_dirty = false
15
+ commit = true
16
+ message = "Bump version: {current_version} → {new_version}"
17
+ moveable_tags = []
18
+ commit_args = ""
19
+ setup_hooks = []
20
+ pre_commit_hooks = []
21
+ post_commit_hooks = []
22
+
23
+ [[tool.bumpversion.files]]
24
+ filename = "uv.lock"
25
+
26
+ [[tool.bumpversion.files]]
27
+ filename = "pyproject.toml"
28
+
29
+ [[tool.bumpversion.files]]
30
+ filename = "src/emerge/__init__.py"
31
+
32
+ [[tool.bumpversion.files]]
33
+ filename = "examples/demo1_stepped_imp_filter.py"
34
+
35
+ [[tool.bumpversion.files]]
36
+ filename = "examples/demo2_combline_filter.py"
37
+
38
+ [[tool.bumpversion.files]]
39
+ filename = "examples/demo3_coupled_line_filter.py"
40
+
41
+ [[tool.bumpversion.files]]
42
+ filename = "examples/demo4_patch_antenna.py"
43
+
44
+ [[tool.bumpversion.files]]
45
+ filename = "examples/demo5_revolve.py"
46
+
47
+ [[tool.bumpversion.files]]
48
+ filename = "examples/demo6_striplines_with_vias.py"
49
+
50
+ [[tool.bumpversion.files]]
51
+ filename = "examples/demo7_periodic_cells.py"
52
+
53
+ [[tool.bumpversion.files]]
54
+ filename = "examples/demo8_waveguide_bpf_synthesis.py"
55
+
56
+ [[tool.bumpversion.files]]
57
+ filename = "examples/demo9_dielectric_resonator.py"
58
+
59
+ [[tool.bumpversion.files]]
60
+ filename = "examples/demo10_sgh.py"
61
+
62
+ [[tool.bumpversion.files]]
63
+ filename = "examples/demo11_lumped_element_filter.py"
64
+
65
+ [[tool.bumpversion.files]]
66
+ filename = "examples/demo12_mode_alignment.py"
67
+
68
+ [[tool.bumpversion.files]]
69
+ filename = "examples/demo13_helix_antenna.py"
70
+
71
+ [[tool.bumpversion.files]]
72
+ filename = "examples/demo14_boundary_selection.py"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 0.6.2
3
+ Version: 0.6.5
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.2"
21
+ __version__ = "0.6.5"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -16,7 +16,7 @@
16
16
  # <https://www.gnu.org/licenses/>.
17
17
  from __future__ import annotations
18
18
  import numpy as np
19
- from ..cs import CoordinateSystem, GCS, Axis
19
+ from ..cs import CoordinateSystem, GCS, Axis, _parse_axis
20
20
  from ..geometry import GeoVolume, GeoPolygon, GeoEdge, GeoSurface
21
21
  from .shapes import Alignment
22
22
  import gmsh
@@ -158,7 +158,35 @@ def rotate_point(point: tuple[float, float, float],
158
158
  rotated += o
159
159
  return tuple(rotated)
160
160
 
161
+ def _dist(p1: tuple[float, float], p2: tuple[float, float]) -> float:
162
+ return ((p2[0]-p1[0])**2 + (p2[1]-p1[1])**2)**(0.5)
161
163
 
164
+ def _pair_points(x1: np.ndarray,
165
+ y1: np.ndarray,
166
+ x2: np.ndarray,
167
+ y2: np.ndarray) -> list[tuple[int,int]]:
168
+ if x1.shape[0]==x2.shape[0]:
169
+ return [(i,i) for i in range(x1.shape[0])]
170
+
171
+ #create point tuples
172
+ p1s = [(x,y) for x,y in zip(x1, y1)]
173
+ p2s = [(x,y) for x,y in zip(x2, y2)]
174
+
175
+ pairs = []
176
+ for i, p1 in enumerate(p1s):
177
+ d1 = _dist(p1, p2s[i-1])
178
+ d2 = _dist(p1, p2s[i])
179
+ d3 = _dist(p1, p2s[i+1])
180
+ mind = min([d1, d2, d3])
181
+ if mind==d1:
182
+ pairs.append((i,i-1))
183
+ continue
184
+ elif mind==d2:
185
+ pairs.append((i,i))
186
+ else:
187
+ pairs.append((i,i+1))
188
+ return pairs
189
+
162
190
  def orthonormalize(axis: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
163
191
  """Generates a set of orthonormal vectors given an input vector X
164
192
 
@@ -233,8 +261,9 @@ class XYPolygon:
233
261
  """This class generalizes a polygon in an un-embedded XY space that can be embedded in 3D space.
234
262
  """
235
263
  def __init__(self,
236
- xs: np.ndarray | list | tuple = None,
237
- ys: np.ndarray | list | tuple = None):
264
+ xs: np.ndarray | list | tuple | None = None,
265
+ ys: np.ndarray | list | tuple | None = None,
266
+ cs: CoordinateSystem | None = None):
238
267
  """Constructs an XY-plane placed polygon.
239
268
 
240
269
  Args:
@@ -250,7 +279,13 @@ class XYPolygon:
250
279
  self.y: np.ndarray = np.asarray(ys)
251
280
 
252
281
  self.fillets: list[tuple[float, int]] = []
282
+
283
+ self._cs: CoordinateSystem = cs
253
284
 
285
+ @property
286
+ def center(self) -> tuple[float, float]:
287
+ return np.mean(self.x), np.mean(self.y)
288
+
254
289
  @property
255
290
  def length(self):
256
291
  return sum([((self.x[i2]-self.x[i1])**2 + (self.y[i2]-self.y[i1])**2)**0.5 for i1, i2 in zip(range(self.N-1),range(1, self.N))])
@@ -282,6 +317,10 @@ class XYPolygon:
282
317
  """
283
318
  return 0.5*np.abs(np.dot(self.x,np.roll(self.y,1))-np.dot(self.y,np.roll(self.x,1)))
284
319
 
320
+ def incs(self, cs: CoordinateSystem) -> XYPolygon:
321
+ self._cs = cs
322
+ return self
323
+
285
324
  def extend(self, xpts: list[float], ypts: list[float]) -> XYPolygon:
286
325
  """Adds a series for x and y coordinates to the existing polygon.
287
326
 
@@ -301,7 +340,7 @@ class XYPolygon:
301
340
  for i in range(self.N):
302
341
  yield (self.x[i], self.y[i])
303
342
 
304
- def fillet(self, radius: float, *indices: int) -> None:
343
+ def fillet(self, radius: float, *indices: int) -> XYPolygon:
305
344
  """Add a fillet rounding with a given radius to the provided nodes.
306
345
 
307
346
  Example:
@@ -313,8 +352,9 @@ class XYPolygon:
313
352
  """
314
353
  for i in indices:
315
354
  self.fillets.append((radius, i))
355
+ return self
316
356
 
317
- def _finalize(self, cs: CoordinateSystem = None) -> GeoPolygon:
357
+ def _make_wire(self, cs: CoordinateSystem) -> tuple[list[int], list[int], int]:
318
358
  """Turns the XYPolygon object into a GeoPolygon that is embedded in 3D space.
319
359
 
320
360
  The polygon will be placed in the XY-plane of the provided coordinate center.
@@ -340,13 +380,27 @@ class XYPolygon:
340
380
 
341
381
  add = 0
342
382
  for radius, index in self.fillets:
343
- t1 = lines[index + add]
344
- t2 = lines[(index+add-1) % len(lines)]
383
+ t1 = lines[(index + add-1) % len(lines)]
384
+ t2 = lines[index + add]
345
385
  tag = gmsh.model.occ.fillet2_d(t1, t2, radius)
346
386
  lines.insert(index, tag)
347
387
  add += 1
348
388
 
349
389
  wiretag = gmsh.model.occ.add_wire(lines)
390
+ return ptags, lines, wiretag
391
+
392
+ def _finalize(self, cs: CoordinateSystem) -> GeoPolygon:
393
+ """Turns the XYPolygon object into a GeoPolygon that is embedded in 3D space.
394
+
395
+ The polygon will be placed in the XY-plane of the provided coordinate center.
396
+
397
+ Args:
398
+ cs (CoordinateSystem, optional): The coordinate system in which to put the polygon. Defaults to None.
399
+
400
+ Returns:
401
+ GeoPolygon: The resultant 3D GeoPolygon object.
402
+ """
403
+ ptags, lines, wiretag = self._make_wire(cs)
350
404
  surftag = gmsh.model.occ.add_plane_surface([wiretag,])
351
405
  poly = GeoPolygon([surftag,])
352
406
  poly.points = ptags
@@ -384,6 +438,8 @@ class XYPolygon:
384
438
  Returns:
385
439
  GeoPolygon: The resultant object.
386
440
  """
441
+ if self._cs is not None:
442
+ return self._finalize(self._cs)
387
443
  if cs is None:
388
444
  cs = GCS
389
445
  return self._finalize(cs)
@@ -500,7 +556,34 @@ class XYPolygon:
500
556
  self.extend(xs, ys)
501
557
  return self
502
558
 
559
+ def connect(self, other: XYPolygon) -> GeoVolume:
560
+ """Connect two XYPolygons with a defined coordinate system
503
561
 
562
+ The coordinate system must be defined before this function can be used. To add a coordinate systme without
563
+ rendering the Polygon to a GeoVolume, use:
564
+ >>> polygon.incs(my_cs_obj)
565
+
566
+ Args:
567
+ other (XYPolygon): _descrThe otheiption_
568
+
569
+ Returns:
570
+ GeoVolume: The resultant volume object
571
+ """
572
+ if self._cs is None:
573
+ raise RuntimeError('Cannot connect XYPolygons without a defined coordinate system. Set this first using .incs()')
574
+ if other._cs is None:
575
+ raise RuntimeError('Cannot connect XYPolygons without a defined coordinate system. Set this first using .incs()')
576
+ p1, l1, w1 = self._make_wire(self._cs)
577
+ p2, l2, w2 = other._make_wire(other._cs)
578
+ o1 = np.array(self._cs.in_global_cs(*self.center, 0)).flatten()
579
+ o2 = np.array(other._cs.in_global_cs(*other.center, 0)).flatten()
580
+ dts = gmsh.model.occ.addThruSections([w1, w2], True, parametrization="IsoParametric")
581
+ vol = GeoVolume([t for d,t in dts if d==3])
582
+
583
+ vol._add_face_pointer('front',o1, self._cs.zax.np)
584
+ vol._add_face_pointer('back', o2, other._cs.zax.np)
585
+ return vol
586
+
504
587
  class Disc(GeoSurface):
505
588
 
506
589
  def __init__(self, origin: tuple[float, float, float],
@@ -525,8 +608,7 @@ class Curve(GeoEdge):
525
608
  degree: int = 3,
526
609
  weights: list[float] | None = None,
527
610
  knots: list[float] | None = None,
528
- ctype: Literal['Spline','BSpline','Bezier'] = 'Bezier',
529
- dstart: tuple[float, float, float] | None = None):
611
+ ctype: Literal['Spline','BSpline','Bezier'] = 'Spline'):
530
612
  """Generate a Spline/Bspline or Bezier curve based on a series of points
531
613
 
532
614
  This calls the different curve features in OpenCASCADE.
@@ -547,11 +629,6 @@ class Curve(GeoEdge):
547
629
  self.xpts: np.ndarray = xpts
548
630
  self.ypts: np.ndarray = ypts
549
631
  self.zpts: np.ndarray = zpts
550
-
551
- if dstart is None:
552
- dstart = (xpts[1]-xpts[0], ypts[1]-ypts[0], zpts[1]-zpts[0])
553
-
554
- self.dstart: tuple[float, float, float] = dstart
555
632
 
556
633
  points = [gmsh.model.occ.add_point(x,y,z) for x,y,z in zip(xpts, ypts, zpts)]
557
634
 
@@ -571,6 +648,14 @@ class Curve(GeoEdge):
571
648
  gmsh.model.occ.remove([(0,tag) for tag in points])
572
649
  super().__init__(tags)
573
650
 
651
+ gmsh.model.occ.synchronize()
652
+ p1 = gmsh.model.getValue(self.dim, self.tags[0], [0,])
653
+ p2 = gmsh.model.getValue(self.dim, self.tags[0], [1e-6])
654
+ self.dstart: tuple[float, float, float] = (p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2])
655
+
656
+
657
+
658
+
574
659
  @property
575
660
  def p0(self) -> tuple[float, float, float]:
576
661
  """The start coordinate
@@ -653,7 +738,7 @@ class Curve(GeoEdge):
653
738
  zp[2] += d/2*dz
654
739
  dp = tuple(Z)
655
740
 
656
- return Curve(xp, yp, zp, ctype='Spline', dstart=dp)
741
+ return Curve(xp, yp, zp, ctype='Spline')
657
742
 
658
743
  @staticmethod
659
744
  def helix_lh(pstart: tuple[float, float, float],
@@ -731,9 +816,12 @@ class Curve(GeoEdge):
731
816
  zp[2] += d/2*dz
732
817
  dp = tuple(Z)
733
818
 
734
- return Curve(xp, yp, zp, ctype='Spline', dstart=dp)
819
+ return Curve(xp, yp, zp, ctype='Spline')
735
820
 
736
- def pipe(self, crossection: GeoSurface | XYPolygon, max_mesh_size: float | None = None) -> GeoVolume:
821
+ def pipe(self, crossection: GeoSurface | XYPolygon,
822
+ max_mesh_size: float | None = None,
823
+ start_tangent: Axis | tuple[float, float, float] | np.ndarray | None = None,
824
+ x_axis: Axis | tuple[float, float, float] | np.ndarray | None = None) -> GeoVolume:
737
825
  """Extrudes a surface or XYPolygon object along the given curve
738
826
 
739
827
  If a GeoSurface object is used, make sure it starts at the center of the curve. This property
@@ -744,25 +832,31 @@ class Curve(GeoEdge):
744
832
  Args:
745
833
  crossection (GeoSurface | XYPolygon): The cross section definition to be used
746
834
  max_mesh_size (float, optional): The maximum mesh size. Defaults to None
835
+ start_tangent (Axis, tuple, ndarray, optional): The input polygon plane normal direction. Defaults to None
836
+ x_axis (Axis, tuple, ndarray optional): The reference X-axis to align the input polygon. Defaults to None
747
837
  Returns:
748
838
  GeoVolume: The resultant volume object
749
839
  """
750
840
  if isinstance(crossection, XYPolygon):
751
- zax = self.dstart
752
- cs = Axis(np.array(zax)).construct_cs(self.p0)
753
- surf = crossection.geo(cs)
841
+ if start_tangent is None:
842
+ start_tangent = self.dstart
843
+ if x_axis is not None:
844
+ xax = _parse_axis(x_axis)
845
+ zax = _parse_axis(self.dstart)
846
+ yax = zax.cross(xax)
847
+ cs = CoordinateSystem(xax, yax, zax, self.p0)
848
+ else:
849
+ zax = self.dstart
850
+ cs = Axis(np.array(zax)).construct_cs(self.p0)
851
+ surf = crossection.geo(cs)
754
852
  else:
755
853
  surf = crossection
756
854
  x1, y1, z1, x2, y2, z2 = gmsh.model.occ.getBoundingBox(*surf.dimtags[0])
757
855
  diag = ((x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2)**(0.5)
758
-
759
856
  pipetag = gmsh.model.occ.addPipe(surf.dimtags, self.tags[0], 'GuidePlan')
760
-
761
857
  self.remove()
762
858
  surf.remove()
763
-
764
859
  volume = GeoVolume(pipetag[0][1])
765
-
766
860
  volume.max_meshsize = diag/2
767
861
  return volume
768
862
 
@@ -1026,4 +1026,4 @@ class Microwave3D:
1026
1026
  """DEPRICATED VERSION: Use run_sweep() instead.
1027
1027
  """
1028
1028
  logger.warning('This function is depricated. Please use run_sweep() instead')
1029
- self.run_sweep(*args, **kwargs)
1029
+ return self.run_sweep(*args, **kwargs)
@@ -249,7 +249,7 @@ class MWData:
249
249
  self.sim.new(**vars)['report'] = report
250
250
 
251
251
  @dataclass
252
- class FarfieldData:
252
+ class FarFieldData:
253
253
  E: np.ndarray
254
254
  H: np.ndarray
255
255
  theta: np.ndarray
@@ -816,7 +816,7 @@ class MWField:
816
816
  ang_range: tuple[float, float] = (-180, 180),
817
817
  Npoints: int = 201,
818
818
  origin: tuple[float, float, float] | None = None,
819
- syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> FarfieldData:#tuple[np.ndarray, np.ndarray, np.ndarray]:
819
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> FarFieldData:#tuple[np.ndarray, np.ndarray, np.ndarray]:
820
820
  """Compute the farfield electric and magnetic field defined by a circle.
821
821
 
822
822
  Args:
@@ -836,14 +836,14 @@ class MWField:
836
836
  theta, phi = arc_on_plane(refdir, plane_normal_parsed, ang_range, Npoints)
837
837
  E,H = self.farfield(theta, phi, faces, origin, syms = syms)
838
838
  angs = np.linspace(*ang_range, Npoints)*np.pi/180
839
- return FarfieldData(E, H, theta, phi, ang=angs)
839
+ return FarFieldData(E, H, theta, phi, ang=angs)
840
840
 
841
841
  def farfield_3d(self,
842
842
  faces: FaceSelection | GeoSurface,
843
843
  thetas: np.ndarray | None = None,
844
844
  phis: np.ndarray | None = None,
845
845
  origin: tuple[float, float, float] | None = None,
846
- syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> FarfieldData:
846
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> FarFieldData:
847
847
  """Compute the farfield in a 3D angular grid
848
848
 
849
849
  If thetas and phis are not provided, they default to a sample space of 2 degrees.
@@ -868,7 +868,7 @@ class MWField:
868
868
  E = E.reshape((3, ) + T.shape)
869
869
  H = H.reshape((3, ) + T.shape)
870
870
 
871
- return FarfieldData(E, H, T, P)
871
+ return FarFieldData(E, H, T, P)
872
872
 
873
873
  def farfield(self, theta: np.ndarray,
874
874
  phi: np.ndarray,
@@ -924,7 +924,7 @@ class MWField:
924
924
 
925
925
  return Eff, Hff
926
926
 
927
- def optycal(self, faces: FaceSelection | GeoSurface | None = None) -> tuple:
927
+ def optycal_surface(self, faces: FaceSelection | GeoSurface | None = None) -> tuple:
928
928
  """Export this models exterior to an Optical acceptable dataset
929
929
 
930
930
  Args:
@@ -948,7 +948,24 @@ class MWField:
948
948
  H = field.H
949
949
  k0 = self.k0
950
950
  return vertices, triangles, E, H, origin, k0
951
-
951
+
952
+ def optycal_antenna(self, faces: FaceSelection | GeoSurface | None = None,
953
+ origin: tuple[float, float, float] | None = None,
954
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> dict:
955
+ """Export this models exterior to an Optical acceptable dataset
956
+
957
+ Args:
958
+ faces (FaceSelection | GeoSurface): The faces to export. Defaults to None
959
+
960
+ Returns:
961
+ tuple: _description_
962
+ """
963
+ freq = self.freq
964
+ def function(theta: np.ndarray, phi: np.ndarray, k0: float):
965
+ E, H = self.farfield(theta, phi, faces, origin, syms)
966
+ return E[0,:], E[1,:], E[2,:], H[0,:], H[1,:], H[2,:]
967
+
968
+ return dict(freq=freq, ff_function=function)
952
969
 
953
970
  class MWScalar:
954
971
  """The MWDataSet class stores solution data of FEM Time Harmonic simulations.
@@ -1027,6 +1044,42 @@ class MWScalarNdim:
1027
1044
  def S(self, i1: int, i2: int) -> np.ndarray:
1028
1045
  return self.Sp[...,self._portmap[i1], self._portmap[i2]]
1029
1046
 
1047
+ @property
1048
+ def Smat(self) -> np.ndarray:
1049
+ """Returns the full S-matrix
1050
+
1051
+ Returns:
1052
+ np.ndarray: The S-matrix with shape (nF, nP, nP)
1053
+ """
1054
+ Nports = len(self._portmap)
1055
+ nfreq = self.freq.shape[0]
1056
+
1057
+ Smat = np.zeros((nfreq,Nports,Nports), dtype=np.complex128)
1058
+
1059
+ for i in self._portnumbers:
1060
+ for j in self._portnumbers:
1061
+ Smat[:,i-1,j-1] = self.S(i,j)
1062
+
1063
+ return Smat
1064
+
1065
+ def emmodel(self, f_sample: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray]:
1066
+ """Returns the required date for a Heavi S-parameter component
1067
+
1068
+ Returns:
1069
+ tuple[np.ndarray, np.ndarray]: Heavi data
1070
+ """
1071
+
1072
+ if f_sample is not None:
1073
+ f = f_sample
1074
+ S = self.model_Smat(f_sample)
1075
+ else:
1076
+ f = self.freq
1077
+ S = self.Smat
1078
+
1079
+ Z0s = self.Z0
1080
+ S = renormalise_s(S, Z0s, 50.0)
1081
+ return f, S
1082
+
1030
1083
  def model_S(self, i: int, j: int,
1031
1084
  freq: np.ndarray,
1032
1085
  Npoles: int | Literal['auto'] = 'auto',
@@ -1062,7 +1115,7 @@ class MWScalarNdim:
1062
1115
  Returns:
1063
1116
  np.ndarray: The (Nf,Np,Np) S-parameter matrix
1064
1117
  """
1065
- Nports = len(self.datasets[0].excitation)
1118
+ Nports = len(self._portmap)
1066
1119
  nfreq = frequencies.shape[0]
1067
1120
 
1068
1121
  Smat = np.zeros((nfreq,Nports,Nports), dtype=np.complex128)
@@ -37,7 +37,7 @@ import inspect
37
37
  from pathlib import Path
38
38
  from atexit import register
39
39
  import signal
40
-
40
+ from .. import __version__
41
41
 
42
42
  ############################################################
43
43
  # EXCEPTION DEFINITIONS #
@@ -54,6 +54,9 @@ Known problems/solutions:
54
54
  class SimulationError(Exception):
55
55
  pass
56
56
 
57
+ class VersionError(Exception):
58
+ pass
59
+
57
60
  ############################################################
58
61
  # BASE 3D SIMULATION MODEL #
59
62
  ############################################################
@@ -228,6 +231,28 @@ class Simulation:
228
231
  # PUBLIC FUNCTIONS #
229
232
  ############################################################
230
233
 
234
+ def check_version(self, version: str) -> None:
235
+ """Compares the provided version number with the version number of EMerge that is running the script.
236
+
237
+ You may remove any call to check_version to suppress VersionErrors and warnings.
238
+
239
+ Args:
240
+ version (str): The EMerge version you intend to write this code for.
241
+
242
+ Raises:
243
+ VersionError: A potential version error if incompatibility is possible
244
+ """
245
+ vM, vm, vp = [float(x) for x in version.split('.')]
246
+ cM, cm, cp = [float(x) for x in __version__.split('.')]
247
+ if vM != cM:
248
+ raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}")
249
+ if vm != cm:
250
+ raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}")
251
+ if vp != cp:
252
+ logger.warning(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}")
253
+ logger.warning("You may suppress this error by removing the call to .check_version().")
254
+ input('Press enter to proceed...')
255
+
231
256
  def save(self) -> None:
232
257
  """Saves the current model in the provided project directory."""
233
258
  # Ensure directory exists
@@ -28,8 +28,8 @@ th = 1 * mm # PML thickness
28
28
  dx = 2 * mm # distance from horn exit to PML start
29
29
 
30
30
  # Create simulation object
31
- m = em.Simulation('HornAntenna', loglevel='DEBUG')
32
-
31
+ m = em.Simulation('HornAntenna')
32
+ m.check_version("0.6.5") # Checks version compatibility.
33
33
  # --- Coordinate system for horn geometry -------------------------------
34
34
  hornCS = em.CS(em.YAX, em.ZAX, em.XAX)
35
35
 
@@ -28,7 +28,9 @@ def Cf(C):
28
28
  # --- PCB and lumped-component parameters ---------------------------------
29
29
  pack = '0603' # package footprint for lumped components
30
30
  # Create simulation and PCB layouter with substrate thickness and material
31
- m = em.Simulation('LumpedFilter', loglevel='DEBUG')
31
+ m = em.Simulation('LumpedFilter')
32
+ m.check_version("0.6.5") # Checks version compatibility.
33
+
32
34
  th = 0.5 # substrate thickness (meters)
33
35
  Hair = 2.0
34
36
  pcb = em.geo.PCB(th, unit=mm, cs=em.GCS,
@@ -11,7 +11,8 @@ direction (+Z vs -Z). In EMerge we can alignm modes using the .align_mode() meth
11
11
  """
12
12
 
13
13
  # First we define our simulation
14
- m = em.Simulation('aligntest', loglevel='INFO')
14
+ m = em.Simulation('aligntest')
15
+ m.check_version("0.6.5") # Checks version compatibility.
15
16
 
16
17
  # We create a cyllindrical waveguide in the Y-axis.
17
18
  cyl = em.geo.Cylinder(0.012, 0.05, em.CS(em.XAX, em.ZAX, em.YAX))
@@ -24,8 +24,10 @@ L = 4*rad0 # helix axial length
24
24
  porth = 2*mm # vertical height of the feed extrusion
25
25
 
26
26
  # --- Simulation object -------------------------------------------------------
27
- model = em.Simulation('helix', loglevel="DEBUG")
28
- dfeed = 5*mm # straight feed length before the helix starts
27
+ model = em.Simulation('helix')
28
+ model.check_version("0.6.5") # Checks version compatibility.
29
+
30
+ dfeed = 3*mm # straight feed length before the helix starts
29
31
 
30
32
  # --- Geometry: helix curve and metal pipe -----------------------------------
31
33
  # Helix curve from (0,0,porth+dfeed/2) to (0,0,porth+dfeed/2+L)
@@ -44,11 +46,15 @@ dfeed = 5*mm # straight feed length before the helix starts
44
46
  # (x0,y1)
45
47
 
46
48
  # We add the porth height + half of the startfeed distance to the total height to put the helix in the right spot.
47
- h_curve = em.geo.Curve.helix_lh((0,0,porth+dfeed/2), (0,0,porth+dfeed/2+L), rad0, 13, r_end=0.8*rad0, startfeed=dfeed)
49
+ h_curve = em.geo.Curve.helix_lh((0,0,porth+dfeed/2), (0,0,porth+dfeed/2+L), rad0, 13, r_end=0.8*rad0)
50
+
51
+ cross_section = em.geo.XYPolygon.circle(radw, Nsections=6)
48
52
 
49
53
  # Sweep a circular cross-section along the curve to make a metallic pipe
50
- helix = h_curve.pipe(em.geo.XYPolygon.circle(radw, Nsections=6)).set_material(em.lib.MET_COPPER)
54
+ helix = h_curve.pipe(cross_section).set_material(em.lib.MET_COPPER)
51
55
 
56
+ # We add a block to make attachment of ports easier.
57
+ block = em.geo.Box(dfeed, dfeed, dfeed, position=h_curve.p0, alignment=em.geo.Alignment.CENTER).set_material(em.lib.MET_COPPER)
52
58
  # Optional preview of current scene (geometry only at this point)
53
59
  model.view()
54
60
 
@@ -37,6 +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
41
 
41
42
  # first lets define a WR90 waveguide
42
43
  wg_box = em.geo.Box(L, wga, wgb, position=(-L, -wga/2, -wgb/2))
@@ -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
-
31
+ m.check_version("0.6.5") # 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.
@@ -42,8 +42,8 @@ rin = 12.5*mil
42
42
  lfeed = 100*mil
43
43
 
44
44
  # A usual we start our simulation file
45
- model = em.Simulation('Combline_DEMO', loglevel='DEBUG')
46
-
45
+ model = em.Simulation('Combline_DEMO')
46
+ model.check_version("0.6.5") # 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
@@ -37,8 +37,8 @@ Dtot = 750 # total clearance (mil)
37
37
  extra = 100 # extra margin (mil)
38
38
 
39
39
  # --- Simulation setup ----------------------------------------------------
40
- model = em.Simulation('Demo3', loglevel='DEBUG')
41
-
40
+ model = em.Simulation('Demo3')
41
+ model.check_version("0.6.5") # 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
@@ -35,8 +35,8 @@ f2 = 1.6e9 # stop frequency
35
35
 
36
36
  # --- Create simulation object -------------------------------------------
37
37
  # Using PVDisplay backend for 3D visualization
38
- model = em.Simulation('MyPatchAntenna', loglevel='DEBUG')
39
-
38
+ model = em.Simulation('MyPatchAntenna')
39
+ model.check_version("0.6.5") # 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,
@@ -4,6 +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
8
 
8
9
  mm = 0.001
9
10
  rad_feed = 30*mm
@@ -11,7 +11,9 @@ PCB related demos (demo1 and demo3) to get more information on the PCBLayouter.
11
11
  mm = 0.001
12
12
  th = 1
13
13
 
14
- model = em.Simulation('Stripline_test', loglevel='DEBUG')
14
+ model = em.Simulation('Stripline_test')
15
+ model.check_version("0.6.5") # Checks version compatibility.
16
+
15
17
  # As usual we start by creating our layouter
16
18
  ly = em.geo.PCB(th, mm, em.GCS, em.lib.DIEL_RO4350B)
17
19
 
@@ -28,7 +28,8 @@ wgb = 18*mm
28
28
  fl = 25*mm
29
29
 
30
30
  # We start again by defining our simulation model
31
- model = em.Simulation('Periodic', loglevel='DEBUG')
31
+ model = em.Simulation('Periodic')
32
+ model.check_version("0.6.5") # Checks version compatibility.
32
33
 
33
34
  # Next we will create a PeriodicCell class (in our case a hexagonal cell). This class
34
35
  # is simply meant to simplify our lives and improve the simulation setup flow.
@@ -79,7 +79,8 @@ def KZ0(S11, S12, S21, S22):
79
79
  wgaps = np.linspace(1*mm, 20*mm, 21)
80
80
  Ks = []
81
81
  hphis = []
82
- with em.Simulation('IrisSim', loglevel='DEBUG') as sim:
82
+ with em.Simulation('IrisSim') as sim:
83
+ sim.check_version("0.6.5") # Checks version compatibility.
83
84
  for (wgap,) in sim.parameter_sweep(True, wgap=wgaps):
84
85
  # Define two short waveguide sections separated by iris plate
85
86
  wg1 = em.geo.Box(wga, Lfeed, wgb, (-wga/2, -Lfeed - t_thickness/2, 0))
@@ -128,7 +129,7 @@ cavity_lengths = (1/beta0 * np.array([
128
129
  ])).real
129
130
 
130
131
  # --- Build and simulate full filter -------------------------------------
131
- with em.Simulation('FullFilter', loglevel='DEBUG') as mf:
132
+ with em.Simulation('FullFilter') as mf:
132
133
  # Input feed section
133
134
  feed1 = em.geo.Box(wga, Lfeed, wgb, (-wga/2, -Lfeed, 0))
134
135
  # Create cavities and irises sequentially
@@ -149,7 +150,7 @@ with em.Simulation('FullFilter', loglevel='DEBUG') as mf:
149
150
  mf.commit_geometry(feed1, feed2, last_iris, *(cavities + irises))
150
151
 
151
152
  # Simulation settings and mesh
152
- mf.mw.set_frequency_range(f1 - 0.2e9, f2 + 0.2e9, 101)
153
+ mf.mw.set_frequency_range(f1 - 0.2e9, f2 + 0.2e9, 51)
153
154
  mf.mw.set_resolution(0.10)
154
155
  for ir in irises:
155
156
  mf.mesher.set_domain_size(ir, 2*mm)
@@ -32,7 +32,8 @@ mat_resonator = em.lib.Material(er=34, color="#ededed")
32
32
  Nmodes = 5
33
33
 
34
34
  # --- Create simulation ---------------------------------------------------
35
- model = em.Simulation('DielectricResonatorFilter', loglevel='INFO')
35
+ model = em.Simulation('DielectricResonatorFilter')
36
+ model.check_version("0.6.5") # Checks version compatibility.
36
37
 
37
38
  # --- Build geometry ------------------------------------------------------
38
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.2"
6
+ version = "0.6.5"
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.2"
239
+ version = "0.6.5"
240
240
  source = { editable = "." }
241
241
  dependencies = [
242
242
  { name = "gmsh" },
@@ -1,30 +0,0 @@
1
- [tool.bumpversion]
2
- current_version = "0.6.2"
3
- parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
- serialize = ["{major}.{minor}.{patch}"]
5
- search = "{current_version}"
6
- replace = "{new_version}"
7
- regex = false
8
- ignore_missing_version = false
9
- ignore_missing_files = false
10
- tag = true
11
- sign_tags= false
12
- tag_name = "v{new_version}"
13
- tag_message = "Bump version: {current_version} → {new_version}"
14
- allow_dirty = false
15
- commit = true
16
- message = "Bump version: {current_version} → {new_version}"
17
- moveable_tags = []
18
- commit_args = ""
19
- setup_hooks = []
20
- pre_commit_hooks = []
21
- post_commit_hooks = []
22
-
23
- [[tool.bumpversion.files]]
24
- filename = "uv.lock"
25
-
26
- [[tool.bumpversion.files]]
27
- filename = "pyproject.toml"
28
-
29
- [[tool.bumpversion.files]]
30
- filename = "src/emerge/__init__.py"
@@ -1,62 +0,0 @@
1
- # EMerge is an open source Python based FEM EM simulation module.
2
- # Copyright (C) 2025 Robert Fennis.
3
-
4
- # This program is free software; you can redistribute it and/or
5
- # modify it under the terms of the GNU General Public License
6
- # as published by the Free Software Foundation; either version 2
7
- # of the License, or (at your option) any later version.
8
-
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
-
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program; if not, see
16
- # <https://www.gnu.org/licenses/>.
17
-
18
- from __future__ import annotations
19
- import gmsh
20
- import numpy as np
21
- from typing import Literal, Callable
22
- from ..geometry import GeoEdge, GeoSurface, GeoVolume
23
-
24
-
25
-
26
- class Curve(GeoEdge):
27
-
28
-
29
- def __init__(self, xpts: np.ndarray, ypts: np.ndarray, zpts: np.ndarray,
30
- degree: int = 3,
31
- weights: list[float] | None = None,
32
- knots: list[float] | None = None,
33
- ctype: Literal['Spline','BSpline','Bezier'] = 'Bezier'):
34
- self.xpts: np.ndarray = xpts
35
- self.ypts: np.ndarray = ypts
36
- self.zpts: np.ndarray = zpts
37
-
38
- points = [gmsh.model.occ.add_point(x,y,z) for x,y,z in zip(xpts, ypts, zpts)]
39
-
40
- if ctype.lower()=='spline':
41
- tags = gmsh.model.occ.addSpline(points)
42
-
43
- elif ctype.lower()=='bspline':
44
- if weights is None:
45
- weights = []
46
- if knots is None:
47
- knots = []
48
- tags = gmsh.model.occ.addBSpline(points, degree=degree, weights=weights, knots=knots)
49
- else:
50
- tags = gmsh.model.occ.addBezier(points)
51
-
52
- tags = gmsh.model.occ.addWire([tags,])
53
- gmsh.model.occ.remove([(0,tag) for tag in points])
54
- super().__init__(tags)
55
-
56
-
57
-
58
-
59
- class Helix(GeoVolume):
60
-
61
-
62
-
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
File without changes
File without changes