emerge 1.0.4__py3-none-any.whl → 1.0.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 +2 -2
- emerge/_emerge/geo/horn.py +17 -1
- emerge/_emerge/geo/operations.py +4 -2
- emerge/_emerge/geo/pcb.py +10 -6
- emerge/_emerge/geo/polybased.py +27 -1
- emerge/_emerge/geo/shapes.py +93 -1
- emerge/_emerge/geometry.py +22 -4
- emerge/_emerge/material.py +31 -2
- emerge/_emerge/mesher.py +2 -1
- emerge/_emerge/physics/microwave/assembly/assembler.py +1 -1
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +78 -77
- emerge/_emerge/physics/microwave/microwave_3d.py +12 -9
- emerge/_emerge/physics/microwave/microwave_bc.py +2 -2
- emerge/_emerge/physics/microwave/microwave_data.py +9 -0
- emerge/_emerge/plot/pyvista/display.py +88 -0
- emerge/_emerge/simmodel.py +3 -1
- emerge/_emerge/solver.py +96 -72
- {emerge-1.0.4.dist-info → emerge-1.0.6.dist-info}/METADATA +2 -2
- {emerge-1.0.4.dist-info → emerge-1.0.6.dist-info}/RECORD +22 -22
- {emerge-1.0.4.dist-info → emerge-1.0.6.dist-info}/WHEEL +0 -0
- {emerge-1.0.4.dist-info → emerge-1.0.6.dist-info}/entry_points.txt +0 -0
- {emerge-1.0.4.dist-info → emerge-1.0.6.dist-info}/licenses/LICENSE +0 -0
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__ = "1.0.
|
|
21
|
+
__version__ = "1.0.6"
|
|
22
22
|
|
|
23
23
|
############################################################
|
|
24
24
|
# HANDLE ENVIRONMENT VARIABLES #
|
|
@@ -33,7 +33,7 @@ os.environ["OPENBLAS_NUM_THREADS"] = NTHREADS
|
|
|
33
33
|
os.environ["VECLIB_NUM_THREADS"] = NTHREADS
|
|
34
34
|
os.environ["VECLIB_MAXIMUM_THREADS"] = NTHREADS
|
|
35
35
|
os.environ["NUMEXPR_NUM_THREADS"] = NTHREADS
|
|
36
|
-
os.environ["NUMBA_NUM_THREADS"] = "4"
|
|
36
|
+
os.environ["NUMBA_NUM_THREADS"] = os.getenv("NUMBA_NUM_THREADS", default="4")
|
|
37
37
|
os.environ.setdefault("NUMBA_THREADING_LAYER", "workqueue")
|
|
38
38
|
|
|
39
39
|
############################################################
|
emerge/_emerge/geo/horn.py
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
from ..geometry import GeoVolume
|
|
19
19
|
from ..cs import CoordinateSystem
|
|
20
|
+
from ..selection import FaceSelection
|
|
20
21
|
|
|
21
22
|
import gmsh # type: ignore
|
|
22
23
|
|
|
@@ -103,4 +104,19 @@ class Horn(GeoVolume):
|
|
|
103
104
|
|
|
104
105
|
pc = p0 + dax * height/2
|
|
105
106
|
self._add_face_pointer('front', pc - height/2*dax, -dax)
|
|
106
|
-
self._add_face_pointer('back', pc + height/2*dax, dax)
|
|
107
|
+
self._add_face_pointer('back', pc + height/2*dax, dax)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def front(self) -> FaceSelection:
|
|
111
|
+
"""The first local -Z face of the Horn."""
|
|
112
|
+
return self.face('front')
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def back(self) -> FaceSelection:
|
|
116
|
+
"""The back local +Z face of the Horn."""
|
|
117
|
+
return self.face('back')
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def sides(self) -> FaceSelection:
|
|
121
|
+
"""The outside faces excluding the top and bottom."""
|
|
122
|
+
return self.boundary(exclude=('front','back'))
|
emerge/_emerge/geo/operations.py
CHANGED
|
@@ -51,11 +51,13 @@ def add(main: T, tool: T,
|
|
|
51
51
|
GeoSurface | GeoVolume
|
|
52
52
|
A new object that is the union of the main and tool objects.
|
|
53
53
|
'''
|
|
54
|
+
|
|
54
55
|
out_dim_tags, out_dim_tags_map = gmsh.model.occ.fuse(main.dimtags, tool.dimtags, removeObject=remove_object, removeTool=remove_tool)
|
|
55
56
|
if out_dim_tags[0][0] == 3:
|
|
56
57
|
output = GeoVolume([dt[1] for dt in out_dim_tags])._take_tools(tool,main)
|
|
57
58
|
elif out_dim_tags[0][0] == 2:
|
|
58
59
|
output = GeoSurface([dt[1] for dt in out_dim_tags])._take_tools(tool,main)
|
|
60
|
+
|
|
59
61
|
if remove_object:
|
|
60
62
|
main._exists = False
|
|
61
63
|
if remove_tool:
|
|
@@ -306,10 +308,10 @@ def unite(*objects: GeoObject) -> GeoObject:
|
|
|
306
308
|
|
|
307
309
|
main._exists = False
|
|
308
310
|
dts = []
|
|
309
|
-
for other in
|
|
311
|
+
for other in objects:
|
|
310
312
|
dts.extend(other.dimtags)
|
|
311
313
|
other._exists = False
|
|
312
|
-
new_dimtags, mapping = gmsh.model.occ.fuse(main.dimtags
|
|
314
|
+
new_dimtags, mapping = gmsh.model.occ.fuse(dts, main.dimtags)
|
|
313
315
|
|
|
314
316
|
new_obj = GeoObject.from_dimtags(new_dimtags)._take_tools(*objects)
|
|
315
317
|
new_obj.set_material(main.material)
|
emerge/_emerge/geo/pcb.py
CHANGED
|
@@ -1331,7 +1331,6 @@ class PCB:
|
|
|
1331
1331
|
poly._aux_data['width'] = stripline.width*self.unit
|
|
1332
1332
|
poly._aux_data['height'] = height*self.unit
|
|
1333
1333
|
poly._aux_data['vdir'] = self.cs.zax
|
|
1334
|
-
poly._aux_data['idir'] = Axis(self.cs.xax.np*stripline.dirright[0] + self.cs.yax.np*stripline.dirright[1])
|
|
1335
1334
|
|
|
1336
1335
|
return poly
|
|
1337
1336
|
|
|
@@ -1346,6 +1345,7 @@ class PCB:
|
|
|
1346
1345
|
point: StripLine,
|
|
1347
1346
|
height: float,
|
|
1348
1347
|
width_multiplier: float = 5.0,
|
|
1348
|
+
width: float | None = None,
|
|
1349
1349
|
name: str | None = 'ModalPort'
|
|
1350
1350
|
) -> GeoSurface:
|
|
1351
1351
|
"""Generate a wave-port as a GeoSurface.
|
|
@@ -1365,11 +1365,16 @@ class PCB:
|
|
|
1365
1365
|
|
|
1366
1366
|
height = (self.thickness + height)
|
|
1367
1367
|
|
|
1368
|
+
if width is not None:
|
|
1369
|
+
W = width
|
|
1370
|
+
else:
|
|
1371
|
+
W = point.width*width_multiplier
|
|
1372
|
+
|
|
1368
1373
|
ds = point.dirright
|
|
1369
|
-
x0 = point.x - ds[0]*
|
|
1370
|
-
y0 = point.y - ds[1]*
|
|
1374
|
+
x0 = point.x - ds[0]*W/2
|
|
1375
|
+
y0 = point.y - ds[1]*W/2
|
|
1371
1376
|
z0 = - self.thickness
|
|
1372
|
-
ax1 = np.array([ds[0], ds[1], 0])*self.unit*
|
|
1377
|
+
ax1 = np.array([ds[0], ds[1], 0])*self.unit*W
|
|
1373
1378
|
ax2 = np.array([0,0,1])*height*self.unit
|
|
1374
1379
|
|
|
1375
1380
|
plate = Plate(np.array([x0,y0,z0])*self.unit, ax1, ax2, name=name)
|
|
@@ -1443,7 +1448,7 @@ class PCB:
|
|
|
1443
1448
|
tag_wire = gmsh.model.occ.addWire(ltags)
|
|
1444
1449
|
planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
|
|
1445
1450
|
poly = GeoPolygon([planetag,], name=name)
|
|
1446
|
-
poly._store('thickness', self.
|
|
1451
|
+
poly._store('thickness', self.trace_thickness)
|
|
1447
1452
|
return poly
|
|
1448
1453
|
|
|
1449
1454
|
@overload
|
|
@@ -1508,6 +1513,5 @@ class PCB:
|
|
|
1508
1513
|
|
|
1509
1514
|
if merge:
|
|
1510
1515
|
polys = unite(*polys)
|
|
1511
|
-
|
|
1512
1516
|
return polys
|
|
1513
1517
|
|
emerge/_emerge/geo/polybased.py
CHANGED
|
@@ -24,6 +24,7 @@ from typing import Generator, Callable
|
|
|
24
24
|
from ..selection import FaceSelection
|
|
25
25
|
from typing import Literal
|
|
26
26
|
from functools import reduce
|
|
27
|
+
from loguru import logger
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def _discretize_curve(xfunc: Callable, yfunc: Callable,
|
|
@@ -230,7 +231,22 @@ class GeoPrism(GeoVolume):
|
|
|
230
231
|
tagslist = [self._face_tags(name) for name in self._face_pointers.keys() if name not in exclude]
|
|
231
232
|
|
|
232
233
|
tags = list(reduce(lambda a,b: a+b, tagslist))
|
|
233
|
-
return FaceSelection(tags)
|
|
234
|
+
return FaceSelection(tags)
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def front(self) -> FaceSelection:
|
|
238
|
+
"""The first local -Z face of the prism."""
|
|
239
|
+
return self.face('front')
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def back(self) -> FaceSelection:
|
|
243
|
+
"""The back local +Z face of the prism."""
|
|
244
|
+
return self.face('back')
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def sides(self) -> FaceSelection:
|
|
248
|
+
"""The outside faces excluding the top and bottom."""
|
|
249
|
+
return self.boundary(exclude=('front','back'))
|
|
234
250
|
|
|
235
251
|
class XYPolygon:
|
|
236
252
|
"""This class generalizes a polygon in an un-embedded XY space that can be embedded in 3D space.
|
|
@@ -362,10 +378,20 @@ class XYPolygon:
|
|
|
362
378
|
self._check()
|
|
363
379
|
|
|
364
380
|
ptags = []
|
|
381
|
+
|
|
365
382
|
xg, yg, zg = cs.in_global_cs(self.x, self.y, 0*self.x)
|
|
366
383
|
|
|
367
384
|
points = dict()
|
|
368
385
|
for x,y,z in zip(xg, yg, zg):
|
|
386
|
+
reuse = False
|
|
387
|
+
for key, (px, py, pz) in points.items():
|
|
388
|
+
if ((x-px)**2 + (y-py)**2 + (z-pz)**2)**0.5 < 1e-12:
|
|
389
|
+
ptags.append(key)
|
|
390
|
+
reuse = True
|
|
391
|
+
break
|
|
392
|
+
if reuse:
|
|
393
|
+
logger.warning(f'Reusing {ptags[-1]}')
|
|
394
|
+
continue
|
|
369
395
|
ptag = gmsh.model.occ.add_point(x,y,z)
|
|
370
396
|
points[ptag] = (x,y,z)
|
|
371
397
|
ptags.append(ptag)
|
emerge/_emerge/geo/shapes.py
CHANGED
|
@@ -97,6 +97,35 @@ class Box(GeoVolume):
|
|
|
97
97
|
self._add_face_pointer('top', pc + height/2*hax, hax)
|
|
98
98
|
self._add_face_pointer('bottom', pc - height/2*hax, -hax)
|
|
99
99
|
|
|
100
|
+
@property
|
|
101
|
+
def left(self) -> FaceSelection:
|
|
102
|
+
"""The left (-X) face."""
|
|
103
|
+
return self.face('left')
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def right(self) -> FaceSelection:
|
|
107
|
+
"""The right (+X) face."""
|
|
108
|
+
return self.face('right')
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def top(self) -> FaceSelection:
|
|
112
|
+
"""The top (+Z) face."""
|
|
113
|
+
return self.face('top')
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def bottom(self) -> FaceSelection:
|
|
117
|
+
"""The bottom (-Z) face."""
|
|
118
|
+
return self.face('bottom')
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def front(self) -> FaceSelection:
|
|
122
|
+
"""The front (-Y) face."""
|
|
123
|
+
return self.face('front')
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def back(self) -> FaceSelection:
|
|
127
|
+
"""The back (+Y) face."""
|
|
128
|
+
return self.face('back')
|
|
100
129
|
|
|
101
130
|
def outside(self, *exclude: Literal['bottom','top','right','left','front','back']) -> FaceSelection:
|
|
102
131
|
"""Select all outside faces except for the once specified by outside
|
|
@@ -131,6 +160,11 @@ class Sphere(GeoVolume):
|
|
|
131
160
|
x,y,z = position
|
|
132
161
|
self.tags: list[int] = [gmsh.model.occ.addSphere(x,y,z,radius),]
|
|
133
162
|
|
|
163
|
+
@property
|
|
164
|
+
def outside(self) -> FaceSelection:
|
|
165
|
+
"""The outside boundary of the sphere.
|
|
166
|
+
"""
|
|
167
|
+
return self.boundary()
|
|
134
168
|
|
|
135
169
|
class XYPlate(GeoSurface):
|
|
136
170
|
"""Generates and XY-plane oriented plate
|
|
@@ -301,6 +335,21 @@ class Cylinder(GeoVolume):
|
|
|
301
335
|
|
|
302
336
|
xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
|
|
303
337
|
return xo, yo, zo
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def front(self) -> FaceSelection:
|
|
341
|
+
"""The first local -Z face of the cylinder."""
|
|
342
|
+
return self.face('front')
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def back(self) -> FaceSelection:
|
|
346
|
+
"""The back local +Z face of the cylinder."""
|
|
347
|
+
return self.face('back')
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def shell(self) -> FaceSelection:
|
|
351
|
+
"""The outside faces excluding the top and bottom."""
|
|
352
|
+
return self.boundary(exclude=('front','back'))
|
|
304
353
|
|
|
305
354
|
|
|
306
355
|
class CoaxCylinder(GeoVolume):
|
|
@@ -382,6 +431,16 @@ class CoaxCylinder(GeoVolume):
|
|
|
382
431
|
xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
|
|
383
432
|
return xo, yo, zo
|
|
384
433
|
|
|
434
|
+
@property
|
|
435
|
+
def front(self) -> FaceSelection:
|
|
436
|
+
"""The first local -Z face of the cylinder."""
|
|
437
|
+
return self.face('front')
|
|
438
|
+
|
|
439
|
+
@property
|
|
440
|
+
def back(self) -> FaceSelection:
|
|
441
|
+
"""The back local +Z face of the cylinder."""
|
|
442
|
+
return self.face('back')
|
|
443
|
+
|
|
385
444
|
class HalfSphere(GeoVolume):
|
|
386
445
|
"""A half sphere volume."""
|
|
387
446
|
_default_name: str = 'HalfSphere'
|
|
@@ -411,7 +470,25 @@ class HalfSphere(GeoVolume):
|
|
|
411
470
|
self._add_face_pointer('back',np.array(position), np.array(direction))
|
|
412
471
|
self._add_face_pointer('bottom',np.array(position), np.array(direction))
|
|
413
472
|
self._add_face_pointer('face',np.array(position), np.array(direction))
|
|
473
|
+
self._add_face_pointer('disc',np.array(position), np.array(direction))
|
|
414
474
|
|
|
475
|
+
@property
|
|
476
|
+
def outside(self) -> FaceSelection:
|
|
477
|
+
"""The outside of the sphere excluding the flat disc face
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
FaceSelection: _description_
|
|
481
|
+
"""
|
|
482
|
+
return self.boundary(exclude=('disc',))
|
|
483
|
+
|
|
484
|
+
@property
|
|
485
|
+
def disc(self) -> FaceSelection:
|
|
486
|
+
"""The flat disc face that cuts the sphere in half
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
FaceSelection: _description_
|
|
490
|
+
"""
|
|
491
|
+
return self.face('disc')
|
|
415
492
|
|
|
416
493
|
class OldBox(GeoVolume):
|
|
417
494
|
'''The sided box class creates a box just like the Box class but with selectable face tags.
|
|
@@ -564,4 +641,19 @@ class Cone(GeoVolume):
|
|
|
564
641
|
|
|
565
642
|
self._add_face_pointer('front', p0, ds)
|
|
566
643
|
if r2>0:
|
|
567
|
-
self._add_face_pointer('back', p0+ds, ds)
|
|
644
|
+
self._add_face_pointer('back', p0+ds, ds)
|
|
645
|
+
|
|
646
|
+
@property
|
|
647
|
+
def front(self) -> FaceSelection:
|
|
648
|
+
"""The first local -Z face of the Cone."""
|
|
649
|
+
return self.face('front')
|
|
650
|
+
|
|
651
|
+
@property
|
|
652
|
+
def back(self) -> FaceSelection:
|
|
653
|
+
"""The back local +Z face of the Cone. If the tip of the cone has a 0 radius, no back face can be selected."""
|
|
654
|
+
return self.face('back')
|
|
655
|
+
|
|
656
|
+
@property
|
|
657
|
+
def shell(self) -> FaceSelection:
|
|
658
|
+
"""The outside faces excluding the top and bottom."""
|
|
659
|
+
return self.boundary(exclude=('front','back'))
|
emerge/_emerge/geometry.py
CHANGED
|
@@ -549,17 +549,26 @@ class GeoObject:
|
|
|
549
549
|
self._priority = _GEOMANAGER.highest_priority()+10
|
|
550
550
|
return self
|
|
551
551
|
|
|
552
|
-
def boundary(self,
|
|
552
|
+
def boundary(self,
|
|
553
|
+
exclude: Iterable[FaceNames,...] | str | None = None,
|
|
553
554
|
tags: list[int] | None = None,
|
|
554
555
|
tool: GeoObject | None = None) -> FaceSelection:
|
|
555
556
|
"""Returns the complete set of boundary faces.
|
|
556
|
-
|
|
557
|
+
|
|
557
558
|
If implemented, it is possible to exclude a set of faces based on their name
|
|
558
|
-
or a list of tags
|
|
559
|
+
or a list of tags.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
exclude: (Iterable[str], str, None): A single string or list/tuple of strings.
|
|
563
|
+
tags: A list of face integers (if known)
|
|
564
|
+
tool: The tool object to base the selection face names one.
|
|
559
565
|
|
|
560
566
|
Returns:
|
|
561
567
|
FaceSelection: The selected faces
|
|
562
568
|
"""
|
|
569
|
+
if isinstance(exclude, str):
|
|
570
|
+
exclude = (exclude,)
|
|
571
|
+
|
|
563
572
|
if exclude is None:
|
|
564
573
|
exclude = tuple()
|
|
565
574
|
|
|
@@ -572,17 +581,23 @@ class GeoObject:
|
|
|
572
581
|
dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
|
|
573
582
|
return FaceSelection([t for d,t in dimtags if t not in tags])
|
|
574
583
|
|
|
575
|
-
def face(self, name: FaceNames, tool: GeoObject | None = None) -> FaceSelection:
|
|
584
|
+
def face(self, name: FaceNames = None, tool: GeoObject | None = None, no: FaceNames = None) -> FaceSelection:
|
|
576
585
|
"""Returns the FaceSelection for a given face name.
|
|
577
586
|
|
|
578
587
|
The face name must be defined for the type of geometry.
|
|
579
588
|
|
|
589
|
+
FaceNames include: front, back, left, right, top, bottom, disc
|
|
590
|
+
|
|
580
591
|
Args:
|
|
581
592
|
name (FaceNames): The name of the face to select.
|
|
593
|
+
tool (GeoObject, None): Which object should be used as a source for the face selection.
|
|
594
|
+
no (FaceNames): If everything BUT a face name should be selected, Equivalent to .boundary(exclude=name).
|
|
582
595
|
|
|
583
596
|
Returns:
|
|
584
597
|
FaceSelection: The selected face
|
|
585
598
|
"""
|
|
599
|
+
if no is not None:
|
|
600
|
+
return self.boundary(exclude=no)
|
|
586
601
|
|
|
587
602
|
return FaceSelection(self._face_tags(name, tool))
|
|
588
603
|
|
|
@@ -593,6 +608,7 @@ class GeoObject:
|
|
|
593
608
|
|
|
594
609
|
Args:
|
|
595
610
|
name (FaceNames): The name of the face to select.
|
|
611
|
+
tool (GeoObject, None): The tool object to use as source of the selection.
|
|
596
612
|
|
|
597
613
|
Returns:
|
|
598
614
|
FaceSelection: The selected face
|
|
@@ -633,10 +649,12 @@ class GeoVolume(GeoObject):
|
|
|
633
649
|
specific geometry data.'''
|
|
634
650
|
dim = 3
|
|
635
651
|
_default_name: str = 'GeoVolume'
|
|
652
|
+
|
|
636
653
|
def __init__(self, tag: int | Iterable[int], name: str | None = None):
|
|
637
654
|
super().__init__(name=name)
|
|
638
655
|
|
|
639
656
|
self.tags: list[int] = []
|
|
657
|
+
|
|
640
658
|
if isinstance(tag, Iterable):
|
|
641
659
|
self.tags = list(tag)
|
|
642
660
|
else:
|
emerge/_emerge/material.py
CHANGED
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
# You should have received a copy of the GNU General Public License
|
|
15
15
|
# along with this program; if not, see
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
|
-
|
|
17
|
+
from __future__ import annotations
|
|
18
18
|
import numpy as np
|
|
19
19
|
from typing import Callable
|
|
20
20
|
import inspect
|
|
21
|
+
from .const import C0
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def num_args(func):
|
|
@@ -328,7 +329,7 @@ class Material:
|
|
|
328
329
|
hex_str = self.color.lstrip('#')
|
|
329
330
|
self._color_rgb = tuple(int(hex_str[i:i+2], 16)/255.0 for i in (0, 2, 4))
|
|
330
331
|
self._metal: bool = _metal
|
|
331
|
-
|
|
332
|
+
|
|
332
333
|
def __getstate__(self):
|
|
333
334
|
state = self.__dict__.copy()
|
|
334
335
|
for k in self._pickle_exclude:
|
|
@@ -401,6 +402,34 @@ class Material:
|
|
|
401
402
|
def color_rgb(self) -> tuple[float,float,float]:
|
|
402
403
|
return self._color_rgb
|
|
403
404
|
|
|
405
|
+
@staticmethod
|
|
406
|
+
def drude_model(conductivity: float,
|
|
407
|
+
colission_time: float,
|
|
408
|
+
er: float = 1.0,
|
|
409
|
+
ur: float = 1.0,
|
|
410
|
+
color: str = "#aaaaaa",
|
|
411
|
+
opacity: float = 0.3,
|
|
412
|
+
metal: bool = True) -> Material:
|
|
413
|
+
"""Creates a Material using the Drume model for conductivity
|
|
414
|
+
Requires at least the DC bulk condutivity σ₀ [S/m] and the
|
|
415
|
+
collision time τ.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
conductivity (float): The DC bulk conductivity σ₀ in S/m
|
|
419
|
+
colission_time (float): The collision time.
|
|
420
|
+
er (float, optional): The dielectric constant. Defaults to 1.0.
|
|
421
|
+
ur (float, optional): The relative permeability. Defaults to 1.0.
|
|
422
|
+
color (str, optional): The material rendering color. Defaults to "#aaaaaa".
|
|
423
|
+
opacity (float, optional): The material rendering opacity. Defaults to 0.3.
|
|
424
|
+
metal (bool, optional): If it should be rendered as a metal.. Defaults to True.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Material: The resultant material.
|
|
428
|
+
"""
|
|
429
|
+
colission_dist = colission_time/C0
|
|
430
|
+
fsigma = FreqDependent(scalar = lambda f: conductivity/(1 - 1j*2*np.pi*f*colission_dist))
|
|
431
|
+
return Material(er, ur, 0.0, fsigma, color=color, opacity=opacity, _metal=metal)
|
|
432
|
+
|
|
404
433
|
AIR = Material(color="#4496f3", opacity=0.05, name='Air')
|
|
405
434
|
COPPER = Material(cond=5.8e7, color="#62290c", _metal=True, name='Copper')
|
|
406
435
|
PEC = Material(color="#ff78aa", opacity=1.0, cond=1e30, _metal=True, name="PEC")
|
emerge/_emerge/mesher.py
CHANGED
|
@@ -280,7 +280,8 @@ class Mesher:
|
|
|
280
280
|
for dimtag in dimtags:
|
|
281
281
|
gmsh.model.mesh.setSizeFromBoundary(dimtag[0], dimtag[1], 0)
|
|
282
282
|
|
|
283
|
-
def set_boundary_size(self,
|
|
283
|
+
def set_boundary_size(self,
|
|
284
|
+
boundary: GeoObject | Selection | Iterable,
|
|
284
285
|
size:float,
|
|
285
286
|
growth_rate: float = 1.4,
|
|
286
287
|
max_size: float | None = None) -> None:
|
|
@@ -244,24 +244,24 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
244
244
|
|
|
245
245
|
L2 = edge_lengths[ej]
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
Dmat[ei+0,ej+0] =
|
|
257
|
-
Dmat[ei+0,ej+10] =
|
|
258
|
-
Dmat[ei+10,ej+0] =
|
|
259
|
-
Dmat[ei+10,ej+10] =
|
|
247
|
+
erGF = matmul(Mm,GC)
|
|
248
|
+
erGC = matmul(Mm,GD)
|
|
249
|
+
erGD = dot_c(GA,erGF)
|
|
250
|
+
GE_MUL_erGF = dot_c(GA,erGC)
|
|
251
|
+
GE_MUL_erGC = dot_c(GB,erGF)
|
|
252
|
+
GA_MUL_erGF = dot_c(GB,erGC)
|
|
253
|
+
|
|
254
|
+
L12 = L1*L2
|
|
255
|
+
Factor = L12*9*dot_c(cross_c(GA,GB),matmul(Ms,cross_c(GC,GD)))
|
|
256
|
+
Dmat[ei+0,ej+0] = Factor*VAC
|
|
257
|
+
Dmat[ei+0,ej+10] = Factor*VAD
|
|
258
|
+
Dmat[ei+10,ej+0] = Factor*VBC
|
|
259
|
+
Dmat[ei+10,ej+10] = Factor*VBD
|
|
260
260
|
|
|
261
|
-
Fmat[ei+0,ej+0] =
|
|
262
|
-
Fmat[ei+0,ej+10] =
|
|
263
|
-
Fmat[ei+10,ej+0] =
|
|
264
|
-
Fmat[ei+10,ej+10] =
|
|
261
|
+
Fmat[ei+0,ej+0] = L12*(VABCD*erGD-VABCC*GE_MUL_erGF-VAACD*GE_MUL_erGC+VAACC*GA_MUL_erGF)
|
|
262
|
+
Fmat[ei+0,ej+10] = L12*(VABDD*erGD-VABCD*GE_MUL_erGF-VAADD*GE_MUL_erGC+VAACD*GA_MUL_erGF)
|
|
263
|
+
Fmat[ei+10,ej+0] = L12*(VBBCD*erGD-VBBCC*GE_MUL_erGF-VABCD*GE_MUL_erGC+VABCC*GA_MUL_erGF)
|
|
264
|
+
Fmat[ei+10,ej+10] = L12*(VBBDD*erGD-VBBCD*GE_MUL_erGF-VABDD*GE_MUL_erGC+VABCD*GA_MUL_erGF)
|
|
265
265
|
|
|
266
266
|
for ej in range(4):
|
|
267
267
|
ej1, ej2, fj = local_tri_map[:, ej]
|
|
@@ -292,29 +292,29 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
292
292
|
Lab2 = Ds[ej1, ej2]
|
|
293
293
|
Lac2 = Ds[ej1, fj]
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
295
|
+
CROSS_AE = cross_c(GA,GB)
|
|
296
|
+
CROSS_DF = dot_c(CROSS_AE,matmul(Ms,cross_c(GC,GF)))
|
|
297
|
+
CROSS_CD = dot_c(CROSS_AE,matmul(Ms,cross_c(GD,GF)))
|
|
298
|
+
AE_MUL_CF = dot_c(CROSS_AE,matmul(Ms,cross_c(GC,GD)))
|
|
299
|
+
erGF = matmul(Mm,GF)
|
|
300
|
+
erGC = matmul(Mm,GC)
|
|
301
|
+
erGD = matmul(Mm,GD)
|
|
302
|
+
GE_MUL_erGF = dot_c(GA,erGF)
|
|
303
|
+
GE_MUL_erGC = dot_c(GA,erGC)
|
|
304
|
+
GA_MUL_erGF = dot_c(GB,erGF)
|
|
305
|
+
GA_MUL_erGC = dot_c(GB,erGC)
|
|
306
|
+
GE_MUL_erGD = dot_c(GA,erGD)
|
|
307
|
+
GA_MUL_erGD = dot_c(GB,erGD)
|
|
308
308
|
|
|
309
|
-
Dmat[ei+0,ej+6] = L1*Lac2*(-6*VAD*
|
|
310
|
-
Dmat[ei+0,ej+16] = L1*Lab2*(6*VAF*
|
|
311
|
-
Dmat[ei+10,ej+6] = L1*Lac2*(-6*VBD*
|
|
312
|
-
Dmat[ei+10,ej+16] = L1*Lab2*(6*VBF*
|
|
313
|
-
|
|
314
|
-
Fmat[ei+0,ej+6] = L1*Lac2*(VABCD*
|
|
315
|
-
Fmat[ei+0,ej+16] = L1*Lab2*(VABDF*
|
|
316
|
-
Fmat[ei+10,ej+6] = L1*Lac2*(VBBCD*
|
|
317
|
-
Fmat[ei+10,ej+16] = L1*Lab2*(VBBDF*
|
|
309
|
+
Dmat[ei+0,ej+6] = L1*Lac2*(-6*VAD*CROSS_DF-3*VAC*CROSS_CD-3*VAF*AE_MUL_CF)
|
|
310
|
+
Dmat[ei+0,ej+16] = L1*Lab2*(6*VAF*AE_MUL_CF+3*VAD*CROSS_DF-3*VAC*CROSS_CD)
|
|
311
|
+
Dmat[ei+10,ej+6] = L1*Lac2*(-6*VBD*CROSS_DF-3*VBC*CROSS_CD-3*VBF*AE_MUL_CF)
|
|
312
|
+
Dmat[ei+10,ej+16] = L1*Lab2*(6*VBF*AE_MUL_CF+3*VBD*CROSS_DF-3*VBC*CROSS_CD)
|
|
313
|
+
|
|
314
|
+
Fmat[ei+0,ej+6] = L1*Lac2*(VABCD*GE_MUL_erGF-VABDF*GE_MUL_erGC-VAACD*GA_MUL_erGF+VAADF*GA_MUL_erGC)
|
|
315
|
+
Fmat[ei+0,ej+16] = L1*Lab2*(VABDF*GE_MUL_erGC-VABCF*GE_MUL_erGD-VAADF*GA_MUL_erGC+VAACF*GA_MUL_erGD)
|
|
316
|
+
Fmat[ei+10,ej+6] = L1*Lac2*(VBBCD*GE_MUL_erGF-VBBDF*GE_MUL_erGC-VABCD*GA_MUL_erGF+VABDF*GA_MUL_erGC)
|
|
317
|
+
Fmat[ei+10,ej+16] = L1*Lab2*(VBBDF*GE_MUL_erGC-VBBCF*GE_MUL_erGD-VABDF*GA_MUL_erGC+VABCF*GA_MUL_erGD)
|
|
318
318
|
|
|
319
319
|
## Mirror the transpose part of the previous iteration as its symmetrical
|
|
320
320
|
|
|
@@ -335,6 +335,7 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
335
335
|
GE = GLs[fi]
|
|
336
336
|
Lac1 = Ds[ei1, fi]
|
|
337
337
|
Lab1 = Ds[ei1, ei2]
|
|
338
|
+
|
|
338
339
|
for ej in range(4):
|
|
339
340
|
ej1, ej2, fj = local_tri_map[:, ej]
|
|
340
341
|
|
|
@@ -366,44 +367,44 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
366
367
|
Lac2 = Ds[ej1, fj]
|
|
367
368
|
Lab2 = Ds[ej1, ej2]
|
|
368
369
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
Q1 = 2*VAD*
|
|
398
|
-
|
|
399
|
-
Dmat[ei+6,ej+6] = Lac1*Lac2*(4*VBD*
|
|
400
|
-
Dmat[ei+6,ej+16] = Lac1*Lab2*(-4*VBF*
|
|
401
|
-
Dmat[ei+16,ej+6] = Lab1*Lac2*(-4*VDE*
|
|
402
|
-
Dmat[ei+16,ej+16] = Lab1*Lab2*(4*VEF*
|
|
403
|
-
Fmat[ei+6,ej+6] = Lac1*Lac2*(VABCD*
|
|
404
|
-
Fmat[ei+6,ej+16] = Lac1*Lab2*(VABDF*
|
|
405
|
-
Fmat[ei+16,ej+6] = Lab1*Lac2*(VBCDE*
|
|
406
|
-
Fmat[ei+16,ej+16] = Lab1*Lab2*(VBDEF*
|
|
370
|
+
CROSS_AE = cross_c(GA,GE)
|
|
371
|
+
CROSS_BE = cross_c(GB,GE)
|
|
372
|
+
CROSS_AB = cross_c(GA,GB)
|
|
373
|
+
CROSS_CF = matmul(Ms,cross_c(GC,GF))
|
|
374
|
+
CROSS_DF = matmul(Ms,cross_c(GD,GF))
|
|
375
|
+
CROSS_CD = matmul(Ms,cross_c(GC,GD))
|
|
376
|
+
AE_MUL_CF = dot_c(CROSS_AE,CROSS_CF)
|
|
377
|
+
AE_MUL_DF = dot_c(CROSS_AE,CROSS_DF)
|
|
378
|
+
AE_MUL_CD = dot_c(CROSS_AE,CROSS_CD)
|
|
379
|
+
BE_MUL_CF = dot_c(CROSS_BE,CROSS_CF)
|
|
380
|
+
BE_MUL_DF = dot_c(CROSS_BE,CROSS_DF)
|
|
381
|
+
BE_MUL_CD = dot_c(CROSS_BE,CROSS_CD)
|
|
382
|
+
AB_MUL_CF = dot_c(CROSS_AB,CROSS_CF)
|
|
383
|
+
AB_MUL_DF = dot_c(CROSS_AB,CROSS_DF)
|
|
384
|
+
AB_MUL_CD = dot_c(CROSS_AB,CROSS_CD)
|
|
385
|
+
erGF = matmul(Mm,GF)
|
|
386
|
+
erGC = matmul(Mm,GC)
|
|
387
|
+
erGD = matmul(Mm,GD)
|
|
388
|
+
GE_MUL_erGF = dot_c(GE,erGF)
|
|
389
|
+
GE_MUL_erGC = dot_c(GE,erGC)
|
|
390
|
+
GA_MUL_erGF = dot_c(GA,erGF)
|
|
391
|
+
GA_MUL_erGC = dot_c(GA,erGC)
|
|
392
|
+
GE_MUL_erGD = dot_c(GE,erGD)
|
|
393
|
+
GA_MUL_erGD = dot_c(GA,erGD)
|
|
394
|
+
GB_MUL_erGF = dot_c(GB,erGF)
|
|
395
|
+
GB_MUL_erGC = dot_c(GB,erGC)
|
|
396
|
+
GB_MUL_erGD = dot_c(GB,erGD)
|
|
397
|
+
|
|
398
|
+
Q1 = 2*VAD*BE_MUL_CF+VAC*BE_MUL_DF+VAF*BE_MUL_CD
|
|
399
|
+
L12 = -2*VAF*BE_MUL_CD-VAD*BE_MUL_CF+VAC*BE_MUL_DF
|
|
400
|
+
Dmat[ei+6,ej+6] = Lac1*Lac2*(4*VBD*AE_MUL_CF+2*VBC*AE_MUL_DF+2*VBF*AE_MUL_CD+Q1+2*VDE*AB_MUL_CF+VCE*AB_MUL_DF+VEF*AB_MUL_CD)
|
|
401
|
+
Dmat[ei+6,ej+16] = Lac1*Lab2*(-4*VBF*AE_MUL_CD-2*VBD*AE_MUL_CF+2*VBC*AE_MUL_DF+L12-2*VEF*AB_MUL_CD-VDE*AB_MUL_CF+VCE*AB_MUL_DF)
|
|
402
|
+
Dmat[ei+16,ej+6] = Lab1*Lac2*(-4*VDE*AB_MUL_CF-2*VCE*AB_MUL_DF-2*VEF*AB_MUL_CD-2*VBD*AE_MUL_CF-VBC*AE_MUL_DF-VBF*AE_MUL_CD+Q1)
|
|
403
|
+
Dmat[ei+16,ej+16] = Lab1*Lab2*(4*VEF*AB_MUL_CD+2*VDE*AB_MUL_CF-2*VCE*AB_MUL_DF+2*VBF*AE_MUL_CD+VBD*AE_MUL_CF-VBC*AE_MUL_DF+L12)
|
|
404
|
+
Fmat[ei+6,ej+6] = Lac1*Lac2*(VABCD*GE_MUL_erGF-VABDF*GE_MUL_erGC-VBCDE*GA_MUL_erGF+VBDEF*GA_MUL_erGC)
|
|
405
|
+
Fmat[ei+6,ej+16] = Lac1*Lab2*(VABDF*GE_MUL_erGC-VABCF*GE_MUL_erGD-VBDEF*GA_MUL_erGC+VBCEF*GA_MUL_erGD)
|
|
406
|
+
Fmat[ei+16,ej+6] = Lab1*Lac2*(VBCDE*GA_MUL_erGF-VBDEF*GA_MUL_erGC-VACDE*GB_MUL_erGF+VADEF*GB_MUL_erGC)
|
|
407
|
+
Fmat[ei+16,ej+16] = Lab1*Lab2*(VBDEF*GA_MUL_erGC-VBCEF*GA_MUL_erGD-VADEF*GB_MUL_erGC+VACEF*GB_MUL_erGD)
|
|
407
408
|
|
|
408
409
|
Dmat = Dmat*KA
|
|
409
410
|
Fmat = Fmat*KB
|