emerge 0.6.11__py3-none-any.whl → 1.0.1__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/bc.py +8 -3
- emerge/_emerge/cacherun.py +79 -0
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/pcb.py +160 -71
- emerge/_emerge/geo/pcb_tools/dxf.py +360 -0
- emerge/_emerge/geo/polybased.py +21 -15
- emerge/_emerge/geo/shapes.py +31 -16
- emerge/_emerge/geometry.py +147 -21
- emerge/_emerge/material.py +2 -1
- emerge/_emerge/mesh3d.py +39 -12
- emerge/_emerge/periodic.py +19 -17
- emerge/_emerge/physics/microwave/assembly/assembler.py +12 -12
- emerge/_emerge/physics/microwave/microwave_3d.py +29 -6
- emerge/_emerge/physics/microwave/microwave_bc.py +22 -9
- emerge/_emerge/physics/microwave/microwave_data.py +3 -0
- emerge/_emerge/plot/pyvista/display.py +20 -6
- emerge/_emerge/plot/pyvista/display_settings.py +2 -1
- emerge/_emerge/plot/simple_plots.py +4 -1
- emerge/_emerge/selection.py +10 -8
- emerge/_emerge/settings.py +12 -0
- emerge/_emerge/simmodel.py +182 -48
- emerge/_emerge/solver.py +9 -2
- emerge/beta/dxf.py +1 -0
- emerge/lib.py +4 -1
- emerge/materials/__init__.py +1 -0
- emerge/materials/isola.py +294 -0
- emerge/materials/rogers.py +58 -0
- {emerge-0.6.11.dist-info → emerge-1.0.1.dist-info}/METADATA +6 -8
- {emerge-0.6.11.dist-info → emerge-1.0.1.dist-info}/RECORD +33 -26
- {emerge-0.6.11.dist-info → emerge-1.0.1.dist-info}/WHEEL +0 -0
- {emerge-0.6.11.dist-info → emerge-1.0.1.dist-info}/entry_points.txt +0 -0
- {emerge-0.6.11.dist-info → emerge-1.0.1.dist-info}/licenses/LICENSE +0 -0
emerge/_emerge/geometry.py
CHANGED
|
@@ -33,6 +33,7 @@ def _map_tags(tags: list[int], mapping: dict[int, list[int]]):
|
|
|
33
33
|
|
|
34
34
|
def _bbcenter(x1, y1, z1, x2, y2, z2):
|
|
35
35
|
return np.array([(x1+x2)/2, (y1+y2)/2, (z1+z2)/2])
|
|
36
|
+
|
|
36
37
|
FaceNames = Literal['back','front','left','right','top','bottom']
|
|
37
38
|
|
|
38
39
|
class _KEY_GENERATOR:
|
|
@@ -47,29 +48,50 @@ class _KEY_GENERATOR:
|
|
|
47
48
|
class _GeometryManager:
|
|
48
49
|
|
|
49
50
|
def __init__(self):
|
|
50
|
-
self.geometry_list: dict[str,
|
|
51
|
+
self.geometry_list: dict[str, dict[str, GeoObject]] = dict()
|
|
51
52
|
self.active: str = ''
|
|
53
|
+
self.geometry_names: dict[str, set[str]] = dict()
|
|
52
54
|
|
|
55
|
+
def get_surfaces(self) -> list[GeoSurface]:
|
|
56
|
+
return [geo for geo in self.all_geometries() if geo.dim==2]
|
|
57
|
+
|
|
53
58
|
def all_geometries(self, model: str | None = None) -> list[GeoObject]:
|
|
54
59
|
if model is None:
|
|
55
60
|
model = self.active
|
|
56
|
-
return [geo for geo in self.geometry_list[model] if geo._exists]
|
|
61
|
+
return [geo for geo in self.geometry_list[model].values() if geo._exists]
|
|
57
62
|
|
|
63
|
+
def all_names(self, model: str | None = None) -> set[str]:
|
|
64
|
+
if model is None:
|
|
65
|
+
model = self.active
|
|
66
|
+
return self.geometry_names[model]
|
|
67
|
+
|
|
68
|
+
def get_name(self, suggestion: str, model: str | None = None) -> str:
|
|
69
|
+
names = self.all_names(model)
|
|
70
|
+
if suggestion not in names:
|
|
71
|
+
return suggestion
|
|
72
|
+
for i in range(1_000_000):
|
|
73
|
+
if f'{suggestion}_{i}' not in names:
|
|
74
|
+
return f'{suggestion}_{i}'
|
|
75
|
+
raise RuntimeError('Cannot generate a unique name.')
|
|
76
|
+
|
|
58
77
|
def submit_geometry(self, geo: GeoObject, model: str | None = None) -> None:
|
|
59
78
|
if model is None:
|
|
60
79
|
model = self.active
|
|
61
|
-
self.geometry_list[model].
|
|
80
|
+
self.geometry_list[model][geo.name] = geo
|
|
81
|
+
self.geometry_names[model].add(geo.name)
|
|
62
82
|
|
|
63
83
|
def sign_in(self, modelname: str) -> None:
|
|
64
84
|
# if modelname not in self.geometry_list:
|
|
65
85
|
# self.geometry_list[modelname] = []
|
|
66
86
|
if modelname is self.geometry_list:
|
|
67
87
|
logger.warning(f'{modelname} already exist, Geometries will be reset.')
|
|
68
|
-
self.geometry_list[modelname] =
|
|
88
|
+
self.geometry_list[modelname] = dict()
|
|
89
|
+
self.geometry_names[modelname] = set()
|
|
69
90
|
self.active = modelname
|
|
70
91
|
|
|
71
92
|
def reset(self, modelname: str) -> None:
|
|
72
|
-
self.geometry_list[modelname] =
|
|
93
|
+
self.geometry_list[modelname] = dict()
|
|
94
|
+
self.geometry_names[modelname] = set()
|
|
73
95
|
|
|
74
96
|
def lowest_priority(self) -> int:
|
|
75
97
|
return min([geo._priority for geo in self.all_geometries()])
|
|
@@ -216,7 +238,8 @@ class GeoObject:
|
|
|
216
238
|
"""A generalization of any OpenCASCADE entity described by a dimension and a set of tags.
|
|
217
239
|
"""
|
|
218
240
|
dim: int = -1
|
|
219
|
-
|
|
241
|
+
_default_name: str = 'GeoObject'
|
|
242
|
+
def __init__(self, tags: list[int] | None = None, name: str | None = None):
|
|
220
243
|
if tags is None:
|
|
221
244
|
tags = []
|
|
222
245
|
self.old_tags: list[int] = []
|
|
@@ -235,18 +258,63 @@ class GeoObject:
|
|
|
235
258
|
self._priority: int = 10
|
|
236
259
|
|
|
237
260
|
self._exists: bool = True
|
|
261
|
+
|
|
262
|
+
self.give_name(name)
|
|
238
263
|
_GEOMANAGER.submit_geometry(self)
|
|
239
264
|
|
|
265
|
+
def _store(self, name: str, data: Any) -> None:
|
|
266
|
+
"""Store a property as auxilliary data under a given name
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
name (str): Name field
|
|
270
|
+
data (Any): Data to store
|
|
271
|
+
"""
|
|
272
|
+
self._aux_data[name] = data
|
|
273
|
+
|
|
274
|
+
def _load(self, name: str) -> Any | None:
|
|
275
|
+
"""Load data with a given name. If it doesn't exist, it returns None
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
name (str): The property to retreive
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Any | None: The property
|
|
282
|
+
"""
|
|
283
|
+
return self._aux_data.get(name, None)
|
|
284
|
+
|
|
285
|
+
def give_name(self, name: str | None = None) -> GeoObject:
|
|
286
|
+
"""Assign a name to this object
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
name (str | None, optional): The name for the object. Defaults to None.
|
|
290
|
+
"""
|
|
291
|
+
if name is None:
|
|
292
|
+
name = self._default_name
|
|
293
|
+
self.name: str = _GEOMANAGER.get_name(name)
|
|
294
|
+
return self
|
|
295
|
+
|
|
240
296
|
@property
|
|
241
297
|
def color_rgb(self) -> tuple[float, float, float]:
|
|
298
|
+
"""The color of the object in RGB float tuple
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
tuple[float, float, float]: The color
|
|
302
|
+
"""
|
|
242
303
|
return self.material.color_rgb
|
|
243
304
|
|
|
244
305
|
@property
|
|
245
306
|
def opacity(self) -> float:
|
|
307
|
+
"""The opacity of the object
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
float: The opacity
|
|
311
|
+
"""
|
|
246
312
|
return self.material.opacity
|
|
247
313
|
|
|
248
314
|
@property
|
|
249
315
|
def _metal(self) -> bool:
|
|
316
|
+
"""If the material should be rendered as metal
|
|
317
|
+
"""
|
|
250
318
|
return self.material._metal
|
|
251
319
|
|
|
252
320
|
@property
|
|
@@ -263,6 +331,14 @@ class GeoObject:
|
|
|
263
331
|
|
|
264
332
|
@staticmethod
|
|
265
333
|
def merged(objects: list[GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject]) -> list[GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject] | GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject:
|
|
334
|
+
"""Create a GeoObject by merging an iterable of GeoObjects
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
objects (list[GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject]): A list of geo objects
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
GeoPoint | GeoEdge | GeoSurface | GeoVolume | GeoObject: The resultant object
|
|
341
|
+
"""
|
|
266
342
|
dim = objects[0].dim
|
|
267
343
|
tags = []
|
|
268
344
|
out: GeoObject | None = None
|
|
@@ -288,6 +364,17 @@ class GeoObject:
|
|
|
288
364
|
origin: np.ndarray | None = None,
|
|
289
365
|
normal: np.ndarray | None = None,
|
|
290
366
|
tag: int | None = None):
|
|
367
|
+
"""Adds a face identifier (face pointer) to this object
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
name (str): The name for the face
|
|
371
|
+
origin (np.ndarray | None, optional): A point on the object. Defaults to None.
|
|
372
|
+
normal (np.ndarray | None, optional): The normal of the face. Defaults to None.
|
|
373
|
+
tag (int | None, optional): The tace tag used to extract the origin and normal. Defaults to None.
|
|
374
|
+
|
|
375
|
+
Raises:
|
|
376
|
+
ValueError: _description_
|
|
377
|
+
"""
|
|
291
378
|
if tag is not None:
|
|
292
379
|
o = gmsh.model.occ.get_center_of_mass(2, tag)
|
|
293
380
|
n = gmsh.model.get_normal(tag, (0,0))
|
|
@@ -299,6 +386,7 @@ class GeoObject:
|
|
|
299
386
|
raise ValueError('Eitehr a tag or an origin + normal must be provided!')
|
|
300
387
|
|
|
301
388
|
def make_copy(self) -> GeoObject:
|
|
389
|
+
""" Copies this object and returns a new object (also in GMSH)"""
|
|
302
390
|
new_dimtags = gmsh.model.occ.copy(self.dimtags)
|
|
303
391
|
new_obj = GeoObject.from_dimtags(new_dimtags)
|
|
304
392
|
new_obj.material = self.material
|
|
@@ -316,6 +404,11 @@ class GeoObject:
|
|
|
316
404
|
return new_obj
|
|
317
405
|
|
|
318
406
|
def replace_tags(self, tagmap: dict[int, list[int]]):
|
|
407
|
+
"""Replaces the GMSH tags assigned to this objects
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
tagmap (dict[int, list[int]]): A map that shows which tag is mapped to which set of new tags.
|
|
411
|
+
"""
|
|
319
412
|
self.old_tags = self.tags
|
|
320
413
|
newtags = []
|
|
321
414
|
for tag in self.tags:
|
|
@@ -391,6 +484,30 @@ class GeoObject:
|
|
|
391
484
|
self._priority = level
|
|
392
485
|
return self
|
|
393
486
|
|
|
487
|
+
def above(self, other: GeoObject) -> GeoObject:
|
|
488
|
+
"""Puts the priority of this object one higher than the other, then returns this object
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
other (GeoObject): The other object to put below this object
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
GeoObject: This object
|
|
495
|
+
"""
|
|
496
|
+
self._priority = other._priority + 1
|
|
497
|
+
return self
|
|
498
|
+
|
|
499
|
+
def below(self, other: GeoObject) -> GeoObject:
|
|
500
|
+
"""Puts the priority of this object one lower than the other, then returns this object
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
other (GeoObject): The other object to put above this object
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
GeoObject: This object
|
|
507
|
+
"""
|
|
508
|
+
self._priority = other._priority -1
|
|
509
|
+
return self
|
|
510
|
+
|
|
394
511
|
def prio_up(self) -> GeoObject:
|
|
395
512
|
"""Increases the material selection priority by 1
|
|
396
513
|
|
|
@@ -427,7 +544,9 @@ class GeoObject:
|
|
|
427
544
|
self._priority = _GEOMANAGER.highest_priority()+10
|
|
428
545
|
return self
|
|
429
546
|
|
|
430
|
-
def boundary(self, exclude: tuple[FaceNames,...] | None = None,
|
|
547
|
+
def boundary(self, exclude: tuple[FaceNames,...] | None = None,
|
|
548
|
+
tags: list[int] | None = None,
|
|
549
|
+
tool: GeoObject | None = None) -> FaceSelection:
|
|
431
550
|
"""Returns the complete set of boundary faces.
|
|
432
551
|
|
|
433
552
|
If implemented, it is possible to exclude a set of faces based on their name
|
|
@@ -444,7 +563,7 @@ class GeoObject:
|
|
|
444
563
|
|
|
445
564
|
|
|
446
565
|
for name in exclude:
|
|
447
|
-
tags.extend(self.face(name).tags)
|
|
566
|
+
tags.extend(self.face(name, tool=tool).tags)
|
|
448
567
|
dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
|
|
449
568
|
return FaceSelection([t for d,t in dimtags if t not in tags])
|
|
450
569
|
|
|
@@ -508,8 +627,10 @@ class GeoVolume(GeoObject):
|
|
|
508
627
|
'''GeoVolume is an interface to the GMSH CAD kernel. It does not represent EMerge
|
|
509
628
|
specific geometry data.'''
|
|
510
629
|
dim = 3
|
|
511
|
-
|
|
512
|
-
|
|
630
|
+
_default_name: str = 'GeoVolume'
|
|
631
|
+
def __init__(self, tag: int | Iterable[int], name: str | None = None):
|
|
632
|
+
super().__init__(name=name)
|
|
633
|
+
|
|
513
634
|
self.tags: list[int] = []
|
|
514
635
|
if isinstance(tag, Iterable):
|
|
515
636
|
self.tags = list(tag)
|
|
@@ -522,13 +643,14 @@ class GeoVolume(GeoObject):
|
|
|
522
643
|
|
|
523
644
|
class GeoPoint(GeoObject):
|
|
524
645
|
dim = 0
|
|
525
|
-
|
|
646
|
+
_default_name: str = 'GeoPoint'
|
|
647
|
+
|
|
526
648
|
@property
|
|
527
649
|
def selection(self) -> PointSelection:
|
|
528
650
|
return PointSelection(self.tags)
|
|
529
651
|
|
|
530
|
-
def __init__(self, tag: int | list[int]):
|
|
531
|
-
super().__init__()
|
|
652
|
+
def __init__(self, tag: int | list[int], name: str | None = None):
|
|
653
|
+
super().__init__(name=name)
|
|
532
654
|
|
|
533
655
|
self.tags: list[int] = []
|
|
534
656
|
if isinstance(tag, Iterable):
|
|
@@ -538,13 +660,14 @@ class GeoPoint(GeoObject):
|
|
|
538
660
|
|
|
539
661
|
class GeoEdge(GeoObject):
|
|
540
662
|
dim = 1
|
|
541
|
-
|
|
663
|
+
_default_name: str = 'GeoEdge'
|
|
664
|
+
|
|
542
665
|
@property
|
|
543
666
|
def selection(self) -> EdgeSelection:
|
|
544
667
|
return EdgeSelection(self.tags)
|
|
545
668
|
|
|
546
|
-
def __init__(self, tag: int | list[int]):
|
|
547
|
-
super().__init__()
|
|
669
|
+
def __init__(self, tag: int | list[int], name: str | None = None):
|
|
670
|
+
super().__init__(name=name)
|
|
548
671
|
self.tags: list[int] = []
|
|
549
672
|
if isinstance(tag, Iterable):
|
|
550
673
|
self.tags = list(tag)
|
|
@@ -556,13 +679,14 @@ class GeoSurface(GeoObject):
|
|
|
556
679
|
'''GeoVolume is an interface to the GMSH CAD kernel. It does not reprsent Emerge
|
|
557
680
|
specific geometry data.'''
|
|
558
681
|
dim = 2
|
|
559
|
-
|
|
682
|
+
_default_name: str = 'GeoSurface'
|
|
683
|
+
|
|
560
684
|
@property
|
|
561
685
|
def selection(self) -> FaceSelection:
|
|
562
686
|
return FaceSelection(self.tags)
|
|
563
687
|
|
|
564
|
-
def __init__(self, tag: int | list[int]):
|
|
565
|
-
super().__init__()
|
|
688
|
+
def __init__(self, tag: int | list[int], name: str | None = None):
|
|
689
|
+
super().__init__(name=name)
|
|
566
690
|
self.tags: list[int] = []
|
|
567
691
|
if isinstance(tag, Iterable):
|
|
568
692
|
self.tags = list(tag)
|
|
@@ -570,10 +694,12 @@ class GeoSurface(GeoObject):
|
|
|
570
694
|
self.tags = [tag,]
|
|
571
695
|
|
|
572
696
|
class GeoPolygon(GeoSurface):
|
|
697
|
+
_default_name: str = 'GeoPolygon'
|
|
573
698
|
|
|
574
699
|
def __init__(self,
|
|
575
|
-
tags: list[int]
|
|
576
|
-
|
|
700
|
+
tags: list[int],
|
|
701
|
+
name: str | None = None):
|
|
702
|
+
super().__init__(tags, name=name)
|
|
577
703
|
self.points: list[int] = []
|
|
578
704
|
self.lines: list[int] = []
|
|
579
705
|
|
emerge/_emerge/material.py
CHANGED
|
@@ -381,4 +381,5 @@ class Material:
|
|
|
381
381
|
return self._color_rgb
|
|
382
382
|
|
|
383
383
|
AIR = Material(color="#4496f3", opacity=0.05, name='Air')
|
|
384
|
-
COPPER = Material(cond=5.8e7, color="#62290c", _metal=True, name='Copper')
|
|
384
|
+
COPPER = Material(cond=5.8e7, color="#62290c", _metal=True, name='Copper')
|
|
385
|
+
PEC = Material(color="#ff78aa", opacity=1.0, cond=1e30, _metal=True, name="PEC")
|
emerge/_emerge/mesh3d.py
CHANGED
|
@@ -122,7 +122,6 @@ class Mesh3D(Mesh):
|
|
|
122
122
|
self.inv_tets: dict = dict()
|
|
123
123
|
|
|
124
124
|
# Mappings
|
|
125
|
-
|
|
126
125
|
self.tet_to_edge: np.ndarray = np.array([])
|
|
127
126
|
self.tet_to_edge_sign: np.ndarray = np.array([])
|
|
128
127
|
self.tet_to_tri: np.ndarray = np.array([])
|
|
@@ -133,7 +132,6 @@ class Mesh3D(Mesh):
|
|
|
133
132
|
self.node_to_edge: defaultdict | dict = defaultdict()
|
|
134
133
|
|
|
135
134
|
# Physics mappings
|
|
136
|
-
|
|
137
135
|
self.tet_to_field: np.ndarray = np.array([])
|
|
138
136
|
self.edge_to_field: np.ndarray = np.array([])
|
|
139
137
|
self.tri_to_field: np.ndarray = np.array([])
|
|
@@ -149,6 +147,10 @@ class Mesh3D(Mesh):
|
|
|
149
147
|
self.vtag_to_tet: dict[int, list[int]] = dict()
|
|
150
148
|
self.etag_to_edge: dict[int, list[int]] = dict()
|
|
151
149
|
|
|
150
|
+
## Dervied
|
|
151
|
+
self.dimtag_to_center: dict[tuple[int, int], tuple[float, float, float]] = dict()
|
|
152
|
+
self.dimtag_to_edges: dict[tuple[int, int], np.ndarray] = dict()
|
|
153
|
+
|
|
152
154
|
self.exterior_face_tags: list[int] = []
|
|
153
155
|
|
|
154
156
|
@property
|
|
@@ -242,7 +244,7 @@ class Mesh3D(Mesh):
|
|
|
242
244
|
|
|
243
245
|
return np.array(indices)
|
|
244
246
|
|
|
245
|
-
def
|
|
247
|
+
def _domain_edge(self, dimtag: tuple[int,int]) -> np.ndarray:
|
|
246
248
|
"""Returns a np.ndarray of all edge indices corresponding to a set of dimension tags.
|
|
247
249
|
|
|
248
250
|
Args:
|
|
@@ -252,21 +254,39 @@ class Mesh3D(Mesh):
|
|
|
252
254
|
np.ndarray: The list of mesh edge element indices.
|
|
253
255
|
"""
|
|
254
256
|
dimtags_edge = []
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
257
|
+
d,t = dimtag
|
|
258
|
+
if d==0:
|
|
259
|
+
return np.ndarray([], dtype=np.int64)
|
|
260
|
+
if d==1:
|
|
261
|
+
dimtags_edge.append((1,t))
|
|
262
|
+
if d==2:
|
|
263
|
+
dimtags_edge.extend(gmsh.model.getBoundary([(d,t),], False, False))
|
|
264
|
+
if d==3:
|
|
265
|
+
dts = gmsh.model.getBoundary([(d,t),], False, False)
|
|
266
|
+
dimtags_edge.extend(gmsh.model.getBoundary(dts, False, False))
|
|
267
|
+
|
|
264
268
|
edge_ids = []
|
|
265
269
|
for tag in dimtags_edge:
|
|
266
270
|
edge_ids.extend(self.etag_to_edge[tag[1]])
|
|
267
271
|
edge_ids = np.array(edge_ids)
|
|
268
272
|
return edge_ids
|
|
273
|
+
|
|
274
|
+
def domain_edges(self, dimtags: list[tuple[int,int]]) -> np.ndarray:
|
|
275
|
+
"""Returns a np.ndarray of all edge indices corresponding to a set of dimension tags.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
dimtags (list[tuple[int,int]]): A list of dimtags.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
np.ndarray: The list of mesh edge element indices.
|
|
282
|
+
"""
|
|
269
283
|
|
|
284
|
+
edge_ids = []
|
|
285
|
+
for dt in dimtags:
|
|
286
|
+
edge_ids.extend(self.dimtag_to_edges[dt])
|
|
287
|
+
edge_ids = np.array(edge_ids)
|
|
288
|
+
return edge_ids
|
|
289
|
+
|
|
270
290
|
def get_face_tets(self, *taglist: list[int]) -> np.ndarray:
|
|
271
291
|
''' Return a list of a tetrahedrons that share a node with any of the nodes in the provided face.'''
|
|
272
292
|
nodes: set = set()
|
|
@@ -487,6 +507,13 @@ class Mesh3D(Mesh):
|
|
|
487
507
|
self.vtag_to_tet[t] = [self.get_tet(node_tags[0,i], node_tags[1,i], node_tags[2,i], node_tags[3,i]) for i in range(node_tags.shape[1])]
|
|
488
508
|
|
|
489
509
|
self.defined = True
|
|
510
|
+
|
|
511
|
+
for dim in (0,1,2,3):
|
|
512
|
+
dts= gmsh.model.get_entities(dim)
|
|
513
|
+
for dt in dts:
|
|
514
|
+
self.dimtag_to_center[dt] = gmsh.model.occ.get_center_of_mass(*dt)
|
|
515
|
+
self.dimtag_to_edges[dt] = self._domain_edge(dt)
|
|
516
|
+
|
|
490
517
|
logger.trace('Done analyzing mesh.')
|
|
491
518
|
|
|
492
519
|
|
emerge/_emerge/periodic.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
from .cs import Axis, _parse_axis, GCS, _parse_vector
|
|
19
|
-
from .selection import SELECTOR_OBJ, Selection
|
|
19
|
+
from .selection import SELECTOR_OBJ, Selection, FaceSelection
|
|
20
20
|
from .geo import GeoPrism, XYPolygon, Alignment, XYPlate
|
|
21
21
|
from .bc import BoundaryCondition
|
|
22
22
|
from typing import Generator
|
|
@@ -50,8 +50,8 @@ def _pair_selection(f1: Selection,
|
|
|
50
50
|
for t1, c1 in zip(f1.tags, c1s):
|
|
51
51
|
for t2, c2 in zip(f2.tags, c2s):
|
|
52
52
|
if np.linalg.norm((c1 + ds)-c2) < 1e-8:
|
|
53
|
-
f1s.append(
|
|
54
|
-
f2s.append(
|
|
53
|
+
f1s.append(FaceSelection([t1,]))
|
|
54
|
+
f2s.append(FaceSelection([t2,]))
|
|
55
55
|
return f1s, f2s
|
|
56
56
|
|
|
57
57
|
|
|
@@ -69,7 +69,7 @@ class PeriodicCell:
|
|
|
69
69
|
|
|
70
70
|
self.origins: list[tuple[float, float, float]] = [_parse_vector(origin) for origin in origins] # type: ignore
|
|
71
71
|
self.vectors: list[Axis] = [_parse_axis(vec) for vec in vectors]
|
|
72
|
-
self.
|
|
72
|
+
self.included_faces: Selection | None = None
|
|
73
73
|
self._bcs: list[Periodic] = []
|
|
74
74
|
self._ports: list[BoundaryCondition] = []
|
|
75
75
|
|
|
@@ -105,9 +105,11 @@ class PeriodicCell:
|
|
|
105
105
|
for f1, f2, a in self.cell_data():
|
|
106
106
|
f1_new = f1
|
|
107
107
|
f2_new = f2
|
|
108
|
-
if self.
|
|
109
|
-
f1_new = f1
|
|
110
|
-
f2_new = f2
|
|
108
|
+
if self.included_faces is not None:
|
|
109
|
+
f1_new = f1 & self.included_faces # type: ignore
|
|
110
|
+
f2_new = f2 & self.included_faces # type: ignore
|
|
111
|
+
if len(f1_new.tags)==0:
|
|
112
|
+
continue
|
|
111
113
|
bcs.append(Periodic(f1_new, f2_new, tuple(a)))
|
|
112
114
|
self._bcs = bcs
|
|
113
115
|
return bcs
|
|
@@ -191,16 +193,16 @@ class RectCell(PeriodicCell):
|
|
|
191
193
|
return XYPlate(self.width, self.height, position=(0,0,z), alignment=Alignment.CENTER)
|
|
192
194
|
|
|
193
195
|
def cell_data(self):
|
|
194
|
-
f1s = SELECTOR_OBJ.inplane(*self.fleft[0],
|
|
195
|
-
f2s = SELECTOR_OBJ.inplane(*self.fright[0],
|
|
196
|
+
f1s = SELECTOR_OBJ.inplane(*self.fleft[0], self.fleft[1])
|
|
197
|
+
f2s = SELECTOR_OBJ.inplane(*self.fright[0], self.fright[1])
|
|
196
198
|
vec = (self.fright[0][0]-self.fleft[0][0],
|
|
197
199
|
self.fright[0][1]-self.fleft[0][1],
|
|
198
200
|
self.fright[0][2]-self.fleft[0][2])
|
|
199
201
|
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
|
|
200
202
|
yield f1, f2, vec
|
|
201
203
|
|
|
202
|
-
f1s = SELECTOR_OBJ.inplane(*self.fbot[0],
|
|
203
|
-
f2s = SELECTOR_OBJ.inplane(*self.ftop[0],
|
|
204
|
+
f1s = SELECTOR_OBJ.inplane(*self.fbot[0], self.fbot[1])
|
|
205
|
+
f2s = SELECTOR_OBJ.inplane(*self.ftop[0], self.ftop[1])
|
|
204
206
|
vec = (self.ftop[0][0]-self.fbot[0][0],
|
|
205
207
|
self.ftop[0][1]-self.fbot[0][1],
|
|
206
208
|
self.ftop[0][2]-self.fbot[0][2])
|
|
@@ -273,9 +275,9 @@ class HexCell(PeriodicCell):
|
|
|
273
275
|
o = self.o1[:-1]
|
|
274
276
|
n = self.f11[1][:-1]
|
|
275
277
|
w = nrm(self.p2-self.p1)/2
|
|
276
|
-
f1s = SELECTOR_OBJ.inplane(*self.f11[0],
|
|
278
|
+
f1s = SELECTOR_OBJ.inplane(*self.f11[0], self.f11[1])\
|
|
277
279
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
|
|
278
|
-
f2s = SELECTOR_OBJ.inplane(*self.f12[0],
|
|
280
|
+
f2s = SELECTOR_OBJ.inplane(*self.f12[0], self.f12[1])\
|
|
279
281
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
280
282
|
vec = - (self.p1 + self.p2)
|
|
281
283
|
|
|
@@ -285,9 +287,9 @@ class HexCell(PeriodicCell):
|
|
|
285
287
|
o = self.o2[:-1]
|
|
286
288
|
n = self.f21[1][:-1]
|
|
287
289
|
w = nrm(self.p3-self.p2)/2
|
|
288
|
-
f1s = SELECTOR_OBJ.inplane(*self.f21[0],
|
|
290
|
+
f1s = SELECTOR_OBJ.inplane(*self.f21[0], self.f21[1])\
|
|
289
291
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
|
|
290
|
-
f2s = SELECTOR_OBJ.inplane(*self.f22[0],
|
|
292
|
+
f2s = SELECTOR_OBJ.inplane(*self.f22[0], self.f22[1])\
|
|
291
293
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
292
294
|
vec = - (self.p2 + self.p3)
|
|
293
295
|
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
|
|
@@ -296,9 +298,9 @@ class HexCell(PeriodicCell):
|
|
|
296
298
|
o = self.o3[:-1]
|
|
297
299
|
n = self.f31[1][:-1]
|
|
298
300
|
w = nrm(-self.p1-self.p3)/2
|
|
299
|
-
f1s = SELECTOR_OBJ.inplane(*self.f31[0],
|
|
301
|
+
f1s = SELECTOR_OBJ.inplane(*self.f31[0], self.f31[1])\
|
|
300
302
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
|
|
301
|
-
f2s = SELECTOR_OBJ.inplane(*self.f32[0],
|
|
303
|
+
f2s = SELECTOR_OBJ.inplane(*self.f32[0], self.f32[1])\
|
|
302
304
|
.exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
303
305
|
vec = - (self.p3 - self.p1)
|
|
304
306
|
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
|
|
@@ -22,6 +22,7 @@ from ....elements.nedleg2 import NedelecLegrange2
|
|
|
22
22
|
from ....mth.optimized import gaus_quad_tri
|
|
23
23
|
from ....mth.pairing import pair_coordinates
|
|
24
24
|
from ....material import Material
|
|
25
|
+
from ....settings import Settings
|
|
25
26
|
from scipy.sparse import csr_matrix
|
|
26
27
|
from loguru import logger
|
|
27
28
|
from ..simjob import SimJob
|
|
@@ -102,13 +103,10 @@ class Assembler:
|
|
|
102
103
|
|
|
103
104
|
It stores some cached properties to accellerate preformance.
|
|
104
105
|
"""
|
|
105
|
-
def __init__(self):
|
|
106
|
+
def __init__(self, settings: Settings):
|
|
106
107
|
|
|
107
108
|
self.cached_matrices = None
|
|
108
|
-
self.
|
|
109
|
-
# Currently not used.
|
|
110
|
-
#self._Pmat_cache: dict[tuple[int,int], csr_matrix] = dict()
|
|
111
|
-
#self._remove_cache: list[int] = []
|
|
109
|
+
self.settings = settings
|
|
112
110
|
|
|
113
111
|
def assemble_bma_matrices(self,
|
|
114
112
|
field: Nedelec2,
|
|
@@ -155,20 +153,22 @@ class Assembler:
|
|
|
155
153
|
E, B = generelized_eigenvalue_matrix(nedlegfield, ermesh, urmesh, port.cs._basis, k0)
|
|
156
154
|
|
|
157
155
|
# TODO: Simplified to all "conductors" loosely defined. Must change to implementing line robin boundary conditions.
|
|
158
|
-
pecs: list[BoundaryCondition] = bc_set.get_conductors()
|
|
156
|
+
pecs: list[BoundaryCondition] = bc_set.get_conductors()
|
|
159
157
|
if len(pecs) > 0:
|
|
160
|
-
logger.debug(f'.total of equiv. {len(pecs)} PEC BCs implemented')
|
|
158
|
+
logger.debug(f'.total of equiv. {len(pecs)} PEC BCs implemented for BMA')
|
|
161
159
|
|
|
162
160
|
pec_ids = []
|
|
163
161
|
|
|
164
162
|
# Process all concutors. Everything above the conductivity limit is considered pec.
|
|
165
163
|
for it in range(boundary_surface.n_tris):
|
|
166
|
-
if sigmesh[it] > self.
|
|
164
|
+
if sigmesh[it] > self.settings.mw_3d_peclim:
|
|
167
165
|
pec_ids.extend(list(nedlegfield.tri_to_field[:,it]))
|
|
168
166
|
|
|
169
167
|
# Process all PEC Boundary Conditions
|
|
170
168
|
for pec in pecs:
|
|
171
169
|
logger.trace(f'.implementing {pec}')
|
|
170
|
+
if len(pec.tags)==0:
|
|
171
|
+
continue
|
|
172
172
|
face_tags = pec.tags
|
|
173
173
|
tri_ids = mesh.get_triangles(face_tags)
|
|
174
174
|
edge_ids = list(mesh.tri_to_edge[:,tri_ids].flatten())
|
|
@@ -235,7 +235,7 @@ class Assembler:
|
|
|
235
235
|
|
|
236
236
|
er = er*(1-1j*tand) - 1j*cond/(W0*EPS0)
|
|
237
237
|
|
|
238
|
-
is_frequency_dependent = is_frequency_dependent or np.any((cond > 0) & (cond < self.
|
|
238
|
+
is_frequency_dependent = is_frequency_dependent or np.any((cond > 0) & (cond < self.settings.mw_3d_peclim)) # type: ignore
|
|
239
239
|
|
|
240
240
|
if cache_matrices and not is_frequency_dependent and self.cached_matrices is not None:
|
|
241
241
|
# IF CACHED AND AVAILABLE PULL E AND B FROM CACHE
|
|
@@ -273,11 +273,11 @@ class Assembler:
|
|
|
273
273
|
# Conductivity above al imit, consider it all PEC
|
|
274
274
|
ipec = 0
|
|
275
275
|
for itet in range(field.n_tets):
|
|
276
|
-
if cond[0,0,itet] > self.
|
|
276
|
+
if cond[0,0,itet] > self.settings.mw_3d_peclim:
|
|
277
277
|
ipec+=1
|
|
278
278
|
pec_ids.extend(field.tet_to_field[:,itet])
|
|
279
279
|
if ipec>0:
|
|
280
|
-
logger.trace(f'Extended PEC with {ipec} tets with a conductivity > {self.
|
|
280
|
+
logger.trace(f'Extended PEC with {ipec} tets with a conductivity > {self.settings.mw_3d_peclim}.')
|
|
281
281
|
|
|
282
282
|
for pec in pec_bcs:
|
|
283
283
|
logger.trace(f'Implementing: {pec}')
|
|
@@ -466,7 +466,7 @@ class Assembler:
|
|
|
466
466
|
|
|
467
467
|
# Conductivity above a limit, consider it all PEC
|
|
468
468
|
for itet in range(field.n_tets):
|
|
469
|
-
if cond[0,0,itet] > self.
|
|
469
|
+
if cond[0,0,itet] > self.settings.mw_3d_peclim:
|
|
470
470
|
pec_ids.extend(field.tet_to_field[:,itet])
|
|
471
471
|
|
|
472
472
|
# PEC Boundary conditions
|