emerge 0.6.5__py3-none-any.whl → 0.6.7__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.5"
21
+ __version__ = "0.6.7"
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, expand_surface
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
@@ -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.
@@ -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,66 @@ 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
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
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))
@@ -965,8 +963,18 @@ class PCB:
965
963
  Returns:
966
964
  float: the z-height
967
965
  """
966
+ if layer <= 0:
967
+ return self._zs[layer]
968
968
  return self._zs[layer-1]
969
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
+
970
978
  def _get_z(self, element: RouteElement) -> float :
971
979
  """Return the z-height of a given Route Element
972
980
 
@@ -981,6 +989,31 @@ class PCB:
981
989
  return path.z
982
990
  raise RouteException('Requesting z-height of route element that is not contained in a path.')
983
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
+
984
1017
  def load(self, name: str) -> StripLine:
985
1018
  """Acquire the x,y, coordinate associated with the label name.
986
1019
 
@@ -1062,7 +1095,7 @@ class PCB:
1062
1095
  GeoSurface: _description_
1063
1096
  """
1064
1097
  if width is None or height is None or origin is None:
1065
- 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:
1066
1099
  raise RouteException('Cannot define a plane with no possible definition of its size.')
1067
1100
  width = self.width
1068
1101
  height = self.length
@@ -1077,6 +1110,7 @@ class PCB:
1077
1110
 
1078
1111
  plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0)) # type: ignore
1079
1112
  plane = change_coordinate_system(plane, self.cs) # type: ignore
1113
+ plane.set_material(COPPER)
1080
1114
  return plane # type: ignore
1081
1115
 
1082
1116
  def generate_pcb(self,
@@ -1123,7 +1157,7 @@ class PCB:
1123
1157
  """Generate the Air Block object
1124
1158
 
1125
1159
  This requires that the width, depth and origin are deterimed. This
1126
- can either be done manually or via the .determine_boudns() method.
1160
+ can either be done manually or via the .determine_bounds() method.
1127
1161
 
1128
1162
  Returns:
1129
1163
  GeoVolume: The PCB Block
@@ -1247,7 +1281,13 @@ class PCB:
1247
1281
  plate = change_coordinate_system(plate, self.cs)
1248
1282
  return plate # type: ignore
1249
1283
 
1250
- def generate_vias(self, merge=False) -> list[Cylinder] | Cylinder:
1284
+ @overload
1285
+ def generate_vias(self, merge=Literal[True]) -> GeoVolume: ...
1286
+
1287
+ @overload
1288
+ def generate_vias(self, merge=Literal[False]) -> list[Cylinder]: ...
1289
+
1290
+ def generate_vias(self, merge=False) -> list[Cylinder] | GeoVolume:
1251
1291
  """Generates the via objects.
1252
1292
 
1253
1293
  Args:
@@ -1321,7 +1361,7 @@ class PCB:
1321
1361
  Returns:
1322
1362
  list[Polygon] | GeoSurface: The output stripline polygons possibly merged if merge = True.
1323
1363
  """
1324
- polys = []
1364
+ polys: list[GeoSurface] = []
1325
1365
  allx = []
1326
1366
  ally = []
1327
1367
 
@@ -1353,19 +1393,19 @@ class PCB:
1353
1393
  poly = self._gen_poly(pcbpoly.xys, pcbpoly.z)
1354
1394
  poly.material = pcbpoly.material
1355
1395
  polys.append(poly)
1396
+ xs, ys = zip(*pcbpoly.xys)
1397
+ allx.extend(xs)
1398
+ ally.extend(ys)
1399
+
1356
1400
 
1357
1401
  self.xs = allx
1358
1402
  self.ys = ally
1359
1403
 
1360
1404
  self.traces = polys
1405
+
1361
1406
  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
1407
+ polys = unite(*polys)
1408
+
1369
1409
  return polys
1370
1410
 
1371
1411
  ############################################################
@@ -30,10 +30,20 @@ class Alignment(Enum):
30
30
  CENTER = 1
31
31
  CORNER = 2
32
32
 
33
+
33
34
  class Box(GeoVolume):
34
- """ A class that represents a box shaped volume
35
+ """Creates a box volume object.
36
+ Specify the alignment of the box with the provided position. The options are CORNER (default)
37
+ for the front-left-bottom node of the box or CENTER for the center of the box.
35
38
 
36
- """
39
+ Args:
40
+ width (float): The x-size
41
+ depth (float): The y-size
42
+ height (float): The z-size
43
+ position (tuple, optional): The position of the box. Defaults to (0,0,0).
44
+ alignment (Alignment, optional): Which point of the box is placed at the position.
45
+ Defaults to Alignment.CORNER.
46
+ """
37
47
 
38
48
  def __init__(self,
39
49
  width: float,
@@ -91,10 +101,15 @@ class Box(GeoVolume):
91
101
 
92
102
  tags = list(reduce(lambda a,b: a+b, tagslist))
93
103
  return FaceSelection(tags)
94
-
104
+
95
105
 
96
106
  class Sphere(GeoVolume):
107
+ """Generates a sphere objected centered ont he position with the given radius
97
108
 
109
+ Args:
110
+ radius (float): The sphere radius
111
+ position (tuple, optional): The center position. Defaults to (0,0,0).
112
+ """
98
113
  def __init__(self,
99
114
  radius: float,
100
115
  position: tuple = (0,0,0)):
@@ -108,7 +123,19 @@ class Sphere(GeoVolume):
108
123
  x,y,z = position
109
124
  self.tags: list[int] = [gmsh.model.occ.addSphere(x,y,z,radius),]
110
125
 
126
+
111
127
  class XYPlate(GeoSurface):
128
+ """Generates and XY-plane oriented plate
129
+
130
+ Specify the alignment of the plate with the provided position. The options are CORNER (default)
131
+ for the front-left node of the plate or CENTER for the center of the plate.
132
+
133
+ Args:
134
+ width (float): The x-size of the plate
135
+ depth (float): The y-size of the plate
136
+ position (tuple, optional): The position of the alignment node. Defaults to (0,0,0).
137
+ alignment (Alignment, optional): Which node to align to. Defaults to Alignment.CORNER.
138
+ """
112
139
  def __init__(self,
113
140
  width: float,
114
141
  depth: float,
@@ -134,7 +161,19 @@ class XYPlate(GeoSurface):
134
161
 
135
162
 
136
163
  class Plate(GeoSurface):
137
-
164
+ """A generalized 2D rectangular plate in XYZ-space.
165
+
166
+ The plate is specified by an origin (o) in meters coordinate plus two vectors (u,v) in meters
167
+ that span two of the sides such that all points of the plate are defined by:
168
+ p1 = o
169
+ p2 = o+u
170
+ p3 = o+v
171
+ p4 = o+u+v
172
+ Args:
173
+ origin (tuple[float, float, float]): The origin of the plate in meters
174
+ u (tuple[float, float, float]): The u-axis of the plate
175
+ v (tuple[float, float, float]): The v-axis of the plate
176
+ """
138
177
  def __init__(self,
139
178
  origin: tuple[float, float, float],
140
179
  u: tuple[float, float, float],
@@ -172,27 +211,43 @@ class Plate(GeoSurface):
172
211
  tags: list[int] = [gmsh.model.occ.addPlaneSurface([tag_wire,]),]
173
212
  super().__init__(tags)
174
213
 
214
+
175
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.
176
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
+ """
177
232
  def __init__(self,
178
233
  radius: float,
179
234
  height: float,
180
- cs: CoordinateSystem = None,
181
- Nsections: int = None):
235
+ cs: CoordinateSystem = GCS,
236
+ Nsections: int | None = None):
182
237
  """Generates a Cylinder object in 3D space.
183
- The cyllinder will always be placed in the origin of the provided CoordinateSystem.
184
- 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
185
240
  oriented along the Z-axis.
186
241
 
187
- By default the cyllinder uses the Open Cascade modeling for a cyllinder. In this representation
188
- 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.
189
244
  As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
190
245
  by an extrusion of a regular N-sided polygon.
191
246
 
192
247
  Args:
193
248
  radius (float): The radius of the Cylinder
194
249
  height (float): The height of the Cylinder
195
- cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
250
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
196
251
  Nsections (int, optional): The number of sections. Defaults to None.
197
252
  """
198
253
  ax = cs.zax.np
@@ -233,33 +288,46 @@ class Cylinder(GeoVolume):
233
288
  xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
234
289
  return xo, yo, zo
235
290
 
291
+
236
292
  class CoaxCylinder(GeoVolume):
237
- """A coaxial cylinder with an inner and outer radius."""
238
-
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
+ """
239
309
  def __init__(self,
240
310
  rout: float,
241
311
  rin: float,
242
312
  height: float,
243
- cs: CoordinateSystem = None,
244
- Nsections: int = None):
245
- """Generates a Coaxial cyllinder object in 3D space.
246
- 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.
247
317
  The bottom coax plane is always placed in the XY-plane. The lenth of the coax is
248
318
  oriented along the Z-axis.
249
319
 
250
- By default the coax uses the Open Cascade modeling for a cyllinder. In this representation
251
- 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.
252
322
  As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
253
323
  by an extrusion of a regular N-sided polygon.
254
324
 
255
325
  Args:
256
326
  radius (float): The radius of the Cylinder
257
327
  height (float): The height of the Cylinder
258
- cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
328
+ cs (CoordinateSystem, optional): The coordinate system. Defaults to GCS.
259
329
  Nsections (int, optional): The number of sections. Defaults to None.
260
330
  """
261
- if cs is None:
262
- cs = GCS
263
331
  if rout <= rin:
264
332
  raise ValueError("Outer radius must be greater than inner radius.")
265
333
 
@@ -297,10 +365,9 @@ class CoaxCylinder(GeoVolume):
297
365
 
298
366
  xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
299
367
  return xo, yo, zo
300
- return super().boundary()
301
368
 
302
369
  class HalfSphere(GeoVolume):
303
-
370
+ """A half sphere volume."""
304
371
  def __init__(self,
305
372
  radius: float,
306
373
  position: tuple = (0,0,0),
@@ -326,8 +393,6 @@ class HalfSphere(GeoVolume):
326
393
  self._add_face_pointer('back',np.array(position), np.array(direction))
327
394
  self._add_face_pointer('bottom',np.array(position), np.array(direction))
328
395
  self._add_face_pointer('face',np.array(position), np.array(direction))
329
-
330
-
331
396
 
332
397
 
333
398
  class OldBox(GeoVolume):
@@ -353,8 +418,6 @@ class OldBox(GeoVolume):
353
418
  alignment (Alignment, optional): Which point of the box is placed at the position.
354
419
  Defaults to Alignment.CORNER.
355
420
  """
356
-
357
-
358
421
  if alignment is Alignment.CORNER:
359
422
  position = (position[0]+width/2, position[1]+depth/2, position[2])
360
423
  elif alignment is Alignment.CENTER:
@@ -436,7 +499,6 @@ class OldBox(GeoVolume):
436
499
  self._add_face_pointer('right', pc + width/2*wax, wax)
437
500
  self._add_face_pointer('top', pc + height/2*hax, hax)
438
501
  self._add_face_pointer('bottom', pc - height/2*hax, -hax)
439
-
440
502
 
441
503
  def outside(self, *exclude: Literal['bottom','top','right','left','front','back']) -> FaceSelection:
442
504
  """Select all outside faces except for the once specified by outside
@@ -449,8 +511,17 @@ class OldBox(GeoVolume):
449
511
  tags = list(reduce(lambda a,b: a+b, tagslist))
450
512
  return FaceSelection(tags)
451
513
 
514
+
452
515
  class Cone(GeoVolume):
453
-
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
+ """
454
525
  def __init__(self, p0: tuple[float, float, float],
455
526
  direction: tuple[float, float, float],
456
527
  r1: float,
@@ -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
@@ -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,
@@ -590,3 +590,4 @@ class Selector:
590
590
  return FaceSelection([lowest_key,])
591
591
 
592
592
  SELECTOR_OBJ = Selector()
593
+
@@ -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()
emerge/_emerge/solver.py CHANGED
@@ -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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 0.6.5
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
@@ -1,4 +1,4 @@
1
- emerge/__init__.py,sha256=NGSPQ4EPfFHaKDQJrKTqMiBfEdvgxZhsgNxt4SoubIw,2605
1
+ emerge/__init__.py,sha256=YUioR8ldVN1lYGhEP8GA_DZ5XDBpsoEczbrssVIEg3o,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,18 +13,18 @@ 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=guiNnSSxdc6qABHr78LAdp9U1dVxb-SPWBgaKTxFzgw,19535
24
+ emerge/_emerge/selection.py,sha256=x8cGN93BAmO80C0gn6feWcW126vorscsZVzhreTOLxs,21298
25
+ emerge/_emerge/simmodel.py,sha256=v1cTMuyknJ5yvd6-AyjWFTd4BdYp31laNa0N8jv04QQ,19743
26
26
  emerge/_emerge/simulation_data.py,sha256=r9-9lpLeA1Z5HU3jDVOXV1H80GVawnXL5K81_dvmlE4,14506
27
- emerge/_emerge/solver.py,sha256=PGPI2LuF4XhfyS8KfC-49X9M1mtYUadgHyqopYfPj58,48542
27
+ emerge/_emerge/solver.py,sha256=77qhdRBdZJCneGYEQXkqWvY7btNDrebSJ4Vo124L-ak,48604
28
28
  emerge/_emerge/system.py,sha256=p4HNz7d_LMRNE9Gk75vVdFecDH2iN_groAM9u-yQTpk,1618
29
29
  emerge/_emerge/elements/__init__.py,sha256=I3n9aic6lJW-oGeqTEZ-Fpxvyl2i-WqsHdnrM3v1oB8,799
30
30
  emerge/_emerge/elements/femdata.py,sha256=3rFVz1-PLtqxnDFvTH1Q9dA98lO5VyjcFKecCJCwfQ4,8020
@@ -32,14 +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=TQyNbiBOlSYBTKm5Tk2egBqKlqnwnNUt1sHqiYsvpWA,1142
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
38
+ emerge/_emerge/geo/operations.py,sha256=7u3KNa7QbFq358m7FjBX1J6b3APPkzMXTjYn7f6qfpM,12004
39
+ emerge/_emerge/geo/pcb.py,sha256=MmY-A-Xnd85aQw6ouOnsZKcui_kH86fw4l0GoR4qQu0,53442
40
40
  emerge/_emerge/geo/pmlbox.py,sha256=TNjuyPtuqrAU5Yl8GrLlievuwYf3hUKJaCEz0cXKSzo,7922
41
41
  emerge/_emerge/geo/polybased.py,sha256=loVJRBjYCTUlti5tHwfH8iU-Inb6n2sOS2Cw4gVKid4,31917
42
- emerge/_emerge/geo/shapes.py,sha256=7biZx_5s3sWKgJq6U_rqOBHyMZ3Nt6kHWj7tYsHPkt8,19289
42
+ emerge/_emerge/geo/shapes.py,sha256=-ct-TJh69oj5fqJcQql4pNk3acJWTIQzYOQeAFXDfWk,23319
43
43
  emerge/_emerge/geo/step.py,sha256=XcAiEN8W4umNmZdYmrGHX_aJUuiMgc6vgT-UIk8Gbqc,2689
44
44
  emerge/_emerge/geo/pcb_tools/calculator.py,sha256=eGYUXdXmHUJCPlfJyY96S87wjeAVFG-e4sPOwbYj0eA,832
45
45
  emerge/_emerge/geo/pcb_tools/macro.py,sha256=0g-0anOFyxrEkFobiSu0cwWFRQ32xB8Az24mmwo0z6M,2992
@@ -51,8 +51,8 @@ emerge/_emerge/physics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
51
51
  emerge/_emerge/physics/microwave/__init__.py,sha256=QHeILGYWmvbfLl1o9wrTiWLm0evfXDgS0JiikUoMTts,28
52
52
  emerge/_emerge/physics/microwave/adaptive_freq.py,sha256=aWhijhCVAbnuwkru-I1AaRdY20uyozf6OWRIh9r2ijg,9786
53
53
  emerge/_emerge/physics/microwave/microwave_3d.py,sha256=5P8yyr6e6hFHBS1JAFHq8gsU1CxFG1LTelKVggv7AjY,42007
54
- emerge/_emerge/physics/microwave/microwave_bc.py,sha256=-fxeZREL42snL4XvXuUqo4QJh24o8lHJ_IcWxkt5yAU,42484
55
- emerge/_emerge/physics/microwave/microwave_data.py,sha256=rIxHAaVFhKgedeEdEGqEd-2zXT0ZRUtL588NCA_d4XQ,46722
54
+ emerge/_emerge/physics/microwave/microwave_bc.py,sha256=itUDUVGPda_qdHyl45PVsc2OSKgjDwHHgmwYflN4l5Y,42316
55
+ emerge/_emerge/physics/microwave/microwave_data.py,sha256=cgm3i5-MbB6x_7znxAeDw2IpOPwP9cue0vWI68udwcM,47386
56
56
  emerge/_emerge/physics/microwave/periodic.py,sha256=wYSUgLFVtCLqSG3EDKoCDRU93iPUzBdXzVRdHTRmbpI,3000
57
57
  emerge/_emerge/physics/microwave/port_functions.py,sha256=aVU__AkVk8b1kH2J_oDLF5iNReCxC9nzCtesFSSSSQo,2112
58
58
  emerge/_emerge/physics/microwave/sc.py,sha256=WZvoPhmHkfEv619RhmN09sXDBV0ryTqybwErA8Rc7lU,4735
@@ -70,7 +70,7 @@ emerge/_emerge/plot/display.py,sha256=TQLlKb-LkaG5ZOSLfxp9KXPlZPRFTxNj1LhVQ-Lp1-
70
70
  emerge/_emerge/plot/simple_plots.py,sha256=q3IuoW4qrp5xv2S3vsVgATdE1QajWmA5_59Ma7I6GA0,24433
71
71
  emerge/_emerge/plot/matplotlib/mpldisplay.py,sha256=szKafDrgdAW5Nyc5UOHuJC87n0WGkXYackOVv182TDQ,8671
72
72
  emerge/_emerge/plot/pyvista/__init__.py,sha256=CPclatEu6mFnJZzCQk09g6T6Fh20WTbiLAJGSwAnPXU,30
73
- emerge/_emerge/plot/pyvista/display.py,sha256=L7_cy7C6EnIIj_-V5E_-j5ORJtIXscvYCmBE03-eCK4,34692
73
+ emerge/_emerge/plot/pyvista/display.py,sha256=5IYVtT8ZwFu1NRwwzrzoY_FmPwjeRAfsQbg9NK383zE,37098
74
74
  emerge/_emerge/plot/pyvista/display_settings.py,sha256=K2OhzKqeFzMXlEfZ5F4CQ9sN3l7nOgVjLplZBuMPjvE,899
75
75
  emerge/_emerge/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  emerge/_emerge/projects/_gen_base.txt,sha256=DqQz36PZg6v1ovQjHvPjd0t4AIbmikZdb9dmrNYsK3w,598
@@ -78,8 +78,8 @@ emerge/_emerge/projects/_load_base.txt,sha256=94o0eSWoDKlNR336EmhpG_S5syQHIUPHQx
78
78
  emerge/_emerge/projects/generate_project.py,sha256=TNw-0SpLc82MBq0bd9hB_yqvBZCgmuPonCBsHTp91uk,1450
79
79
  emerge/_emerge/solve_interfaces/cudss_interface.py,sha256=-SjiTNIyE7iJ8Bm14Cva5e2lpJDgfiS2Mvz1Bgy-UL4,9688
80
80
  emerge/_emerge/solve_interfaces/pardiso_interface.py,sha256=iVFxToMmIzhj3hcAP-O_MDHKz82ePFIHY1us11kzUBU,15305
81
- emerge-0.6.5.dist-info/METADATA,sha256=Kf2Xyi7EWIR34D7554jWXG0-HscLA6PR3MdQ9r-TjhM,3304
82
- emerge-0.6.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
- emerge-0.6.5.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
84
- emerge-0.6.5.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
85
- emerge-0.6.5.dist-info/RECORD,,
81
+ emerge-0.6.7.dist-info/METADATA,sha256=J3r6jeI0a4A9x5fzG1t-h1ZL28y1SJizCz17i0Hwdrk,3304
82
+ emerge-0.6.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
+ emerge-0.6.7.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
84
+ emerge-0.6.7.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
85
+ emerge-0.6.7.dist-info/RECORD,,
File without changes