emerge 0.6.3__py3-none-any.whl → 0.6.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of emerge might be problematic. Click here for more details.

emerge/__init__.py CHANGED
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "0.6.3"
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
emerge/_emerge/geo/pcb.py CHANGED
@@ -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
  ############################################################
@@ -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
 
@@ -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
+
emerge/_emerge/howto.py CHANGED
@@ -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
@@ -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.
@@ -590,3 +590,4 @@ class Selector:
590
590
  return FaceSelection([lowest_key,])
591
591
 
592
592
  SELECTOR_OBJ = Selector()
593
+
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 0.6.3
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
@@ -1,4 +1,4 @@
1
- emerge/__init__.py,sha256=pvVkVDypxJT4dkNhf0nSV6bjEseFZILcGH7XGjtQ2qI,2605
1
+ emerge/__init__.py,sha256=vDTOfX6iIL3c8TpaSgwGStFWAgPYHb-QlQ4vmhyOzAc,2642
2
2
  emerge/__main__.py,sha256=WVf16sfrOI910QWohrQDaChZdRifMNoS6VKzCT6f3ZA,92
3
3
  emerge/cli.py,sha256=NU1uhwuZ6i50680v3_I4kDZPTHqz74gOYK71UBhb8oE,666
4
4
  emerge/ext.py,sha256=IBoHH5PQFj5pYMfp6r-uMpNNgbSe8c0g9x8qjBzzVmU,223
@@ -13,16 +13,16 @@ emerge/_emerge/coord.py,sha256=BKvyrcnHY-_bgHqysnByy5k9_DK4VVfr9KKkRaawG2E,4371
13
13
  emerge/_emerge/cs.py,sha256=YNT2Nn6Dh8fYPUMlT6w0msHnQpZREbbl_ZXTGNppCVs,18392
14
14
  emerge/_emerge/dataset.py,sha256=UcSAJ_siLrOjNBBWRWsS3GUZUpayp63EM6pP6ClwKDI,1534
15
15
  emerge/_emerge/geo2d.py,sha256=e_HkX1GQ2iYrdO0zeEgzVOzfGyU1WGJyjeGBAobOttE,3323
16
- emerge/_emerge/geometry.py,sha256=_iBRMAtZB_HQCDAcQrVfwSIkilTT-up6V5-FfKMzHXI,18190
17
- emerge/_emerge/howto.py,sha256=vrymIkMcrZsf8UB8rQEvAsHv2rC4nLUgup9nW-BfkcA,8172
16
+ emerge/_emerge/geometry.py,sha256=2mxVF2Ezd_rK9y6LecgrK1wZ7NbtzROz2BXgu_mPMjg,19190
17
+ emerge/_emerge/howto.py,sha256=c4UxUNpA1tygr3OoR-LH-h0UZv-Tf9K8tpCiAU18BKE,8173
18
18
  emerge/_emerge/logsettings.py,sha256=DcUWIUUhdLe9ev5XC1bd5ZUrJz00MjABkY8rnekFrPY,3373
19
19
  emerge/_emerge/material.py,sha256=HTjQ8wDzkBwYA1sLPRuqR8lPeoXsW-4m5K3yXoKqjXQ,4022
20
20
  emerge/_emerge/mesh3d.py,sha256=Kszo-ogeByvoAVmrCIASc44PRIw-MblxNtwFB0AHa1A,34500
21
21
  emerge/_emerge/mesher.py,sha256=fKgPb6oZe_bqp0XYfZ6UNgBfRaAS3-tjUtZX8NalJe8,13199
22
22
  emerge/_emerge/periodic.py,sha256=xfdKKq3qX7iBBestnRizOzJNfXlpr9lCPkiYhfrRIR8,12013
23
23
  emerge/_emerge/plot.py,sha256=cf1I9mj7EIUJcq8vmANlUkqoV6QqVaJaP-zlC-T9E18,8041
24
- emerge/_emerge/selection.py,sha256=6UizuWC01sUGjFodrGTy8N1J1832vfOWA4u5paNupw4,21297
25
- emerge/_emerge/simmodel.py,sha256=JIUgSyykVTkv9_GiNq7Xq7N___-BgVDczcyB3RiyxVI,18206
24
+ emerge/_emerge/selection.py,sha256=x8cGN93BAmO80C0gn6feWcW126vorscsZVzhreTOLxs,21298
25
+ emerge/_emerge/simmodel.py,sha256=guiNnSSxdc6qABHr78LAdp9U1dVxb-SPWBgaKTxFzgw,19535
26
26
  emerge/_emerge/simulation_data.py,sha256=r9-9lpLeA1Z5HU3jDVOXV1H80GVawnXL5K81_dvmlE4,14506
27
27
  emerge/_emerge/solver.py,sha256=PGPI2LuF4XhfyS8KfC-49X9M1mtYUadgHyqopYfPj58,48542
28
28
  emerge/_emerge/system.py,sha256=p4HNz7d_LMRNE9Gk75vVdFecDH2iN_groAM9u-yQTpk,1618
@@ -32,15 +32,14 @@ emerge/_emerge/elements/index_interp.py,sha256=DlDy2KrhM5QsF6jYQIl4BJndr9F9wnjFM
32
32
  emerge/_emerge/elements/ned2_interp.py,sha256=kMhbjS1fACa1fmhSkQTU35jS3iF63dJcFe5DHl4Xo78,32541
33
33
  emerge/_emerge/elements/nedelec2.py,sha256=chU3Ewz7grKZtpyglj2qFJYHvBzCXFQZa1skzjfHoPo,6138
34
34
  emerge/_emerge/elements/nedleg2.py,sha256=qVPKtJpT7UCA5dcI_mXNX7Co4tzVCvlxQv5VtsLuKN8,8468
35
- emerge/_emerge/geo/__init__.py,sha256=dZcdXurnvJG8n-sOP55a2JyBxxPQP7LV2n7PmwSLzXc,1119
35
+ emerge/_emerge/geo/__init__.py,sha256=WAAnqNuIrbZWMc7k3mgQ3L_qXRK12gZ755N3MLBJD7I,1126
36
36
  emerge/_emerge/geo/horn.py,sha256=h4GzGzEkXgWaQgoBMvhFgGsqOrNRB3CJg3RBKXigCz0,4186
37
37
  emerge/_emerge/geo/modeler.py,sha256=gbxmwXAdR7kKW_EFbkd1QPVqJWKJpnTufI7lxaqPyjU,15573
38
- emerge/_emerge/geo/operations.py,sha256=LKA7_BU4iw6-i6KOUyuL6l10QjXSwnXsixOaTw-M6Vk,9697
39
- emerge/_emerge/geo/pcb.py,sha256=b4FZAwZaO8X2NMnRkYYDUTa8w9ZdN2XfZdmyHhZZaGY,52213
40
- emerge/_emerge/geo/pipes.py,sha256=l1Vm5lpbxMwHQqffZWLTKQU_xNnsXvHq0XNDNCpQKXY,2054
38
+ emerge/_emerge/geo/operations.py,sha256=U6F4vIBW0FnFzeYWBsxxjC3Nf5DS97okgUWYxJzN1KQ,10973
39
+ emerge/_emerge/geo/pcb.py,sha256=kzkpen96KmeRe2qZvMUhOzifPlBv43YaMpQsRG_dzto,52351
41
40
  emerge/_emerge/geo/pmlbox.py,sha256=TNjuyPtuqrAU5Yl8GrLlievuwYf3hUKJaCEz0cXKSzo,7922
42
- emerge/_emerge/geo/polybased.py,sha256=I-h7c_QTy4lH5whwT0ZWqcnM3VG7YkczH70salvsY-o,27959
43
- emerge/_emerge/geo/shapes.py,sha256=7biZx_5s3sWKgJq6U_rqOBHyMZ3Nt6kHWj7tYsHPkt8,19289
41
+ emerge/_emerge/geo/polybased.py,sha256=loVJRBjYCTUlti5tHwfH8iU-Inb6n2sOS2Cw4gVKid4,31917
42
+ emerge/_emerge/geo/shapes.py,sha256=2B_is3pzsZydWu2yC7XSJZ0T00HNQeQ8Q71EGaOIrNI,21140
44
43
  emerge/_emerge/geo/step.py,sha256=XcAiEN8W4umNmZdYmrGHX_aJUuiMgc6vgT-UIk8Gbqc,2689
45
44
  emerge/_emerge/geo/pcb_tools/calculator.py,sha256=eGYUXdXmHUJCPlfJyY96S87wjeAVFG-e4sPOwbYj0eA,832
46
45
  emerge/_emerge/geo/pcb_tools/macro.py,sha256=0g-0anOFyxrEkFobiSu0cwWFRQ32xB8Az24mmwo0z6M,2992
@@ -51,9 +50,9 @@ emerge/_emerge/mth/pairing.py,sha256=i8bBvTeMmzgF0JdiDNJiTXxx913x4f10777pzD6FJo0
51
50
  emerge/_emerge/physics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
51
  emerge/_emerge/physics/microwave/__init__.py,sha256=QHeILGYWmvbfLl1o9wrTiWLm0evfXDgS0JiikUoMTts,28
53
52
  emerge/_emerge/physics/microwave/adaptive_freq.py,sha256=aWhijhCVAbnuwkru-I1AaRdY20uyozf6OWRIh9r2ijg,9786
54
- emerge/_emerge/physics/microwave/microwave_3d.py,sha256=zKIvFa9WDfDSP6XkYQ-kc_0umZnOIfe0mpBku0piI-4,42000
53
+ emerge/_emerge/physics/microwave/microwave_3d.py,sha256=5P8yyr6e6hFHBS1JAFHq8gsU1CxFG1LTelKVggv7AjY,42007
55
54
  emerge/_emerge/physics/microwave/microwave_bc.py,sha256=-fxeZREL42snL4XvXuUqo4QJh24o8lHJ_IcWxkt5yAU,42484
56
- emerge/_emerge/physics/microwave/microwave_data.py,sha256=Wwqu3RAVuMNCUeFfcMGsf4nAs117sFx_xozpoSeMJLM,45945
55
+ emerge/_emerge/physics/microwave/microwave_data.py,sha256=rIxHAaVFhKgedeEdEGqEd-2zXT0ZRUtL588NCA_d4XQ,46722
57
56
  emerge/_emerge/physics/microwave/periodic.py,sha256=wYSUgLFVtCLqSG3EDKoCDRU93iPUzBdXzVRdHTRmbpI,3000
58
57
  emerge/_emerge/physics/microwave/port_functions.py,sha256=aVU__AkVk8b1kH2J_oDLF5iNReCxC9nzCtesFSSSSQo,2112
59
58
  emerge/_emerge/physics/microwave/sc.py,sha256=WZvoPhmHkfEv619RhmN09sXDBV0ryTqybwErA8Rc7lU,4735
@@ -79,8 +78,8 @@ emerge/_emerge/projects/_load_base.txt,sha256=94o0eSWoDKlNR336EmhpG_S5syQHIUPHQx
79
78
  emerge/_emerge/projects/generate_project.py,sha256=TNw-0SpLc82MBq0bd9hB_yqvBZCgmuPonCBsHTp91uk,1450
80
79
  emerge/_emerge/solve_interfaces/cudss_interface.py,sha256=-SjiTNIyE7iJ8Bm14Cva5e2lpJDgfiS2Mvz1Bgy-UL4,9688
81
80
  emerge/_emerge/solve_interfaces/pardiso_interface.py,sha256=iVFxToMmIzhj3hcAP-O_MDHKz82ePFIHY1us11kzUBU,15305
82
- emerge-0.6.3.dist-info/METADATA,sha256=KSst45Vl8PLGSgEXm_VcfSZbV6N_osO-H8CVA8g2rz0,3304
83
- emerge-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
- emerge-0.6.3.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
85
- emerge-0.6.3.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
86
- emerge-0.6.3.dist-info/RECORD,,
81
+ emerge-0.6.6.dist-info/METADATA,sha256=d_K1k2mCNNqBkh6A5SX85lvlwYNR0mVP6e54QX-bX30,3304
82
+ emerge-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
+ emerge-0.6.6.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
84
+ emerge-0.6.6.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
85
+ emerge-0.6.6.dist-info/RECORD,,
@@ -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