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.

@@ -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, list[GeoObject]] = dict()
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].append(geo)
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
- def __init__(self, tags: list[int] | None = None):
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, tags: list[int] | None = None) -> FaceSelection:
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
- def __init__(self, tag: int | Iterable[int]):
512
- super().__init__()
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
- super().__init__(tags)
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
 
@@ -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 domain_edges(self, dimtags: list[tuple[int,int]]) -> np.ndarray:
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
- for (d,t) in dimtags:
256
- if d==1:
257
- dimtags_edge.append(t)
258
- if d==2:
259
- dimtags_edge.extend(gmsh.model.getBoundary([(d,t),], False, False))
260
- if d==3:
261
- dts = gmsh.model.getBoundary([(d,t),], False, False)
262
- dimtags_edge.extend(gmsh.model.getBoundary(dts, False, False))
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
 
@@ -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(Selection([t1,]))
54
- f2s.append(Selection([t2,]))
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.excluded_faces: Selection | None = None
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.excluded_faces is not None:
109
- f1_new = f1 - self.excluded_faces # type: ignore
110
- f2_new = f2 - self.excluded_faces # type: ignore
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], *self.fleft[1])
195
- f2s = SELECTOR_OBJ.inplane(*self.fright[0], *self.fright[1])
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], *self.fbot[1])
203
- f2s = SELECTOR_OBJ.inplane(*self.ftop[0], *self.ftop[1])
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], *self.f11[1])\
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], *self.f12[1])\
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], *self.f21[1])\
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], *self.f22[1])\
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], *self.f31[1])\
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], *self.f32[1])\
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.conductivity_limit = 1e7
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()#[bc for bc in bcs if isinstance(bc,PEC)]
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.conductivity_limit:
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.conductivity_limit)) # type: ignore
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.conductivity_limit:
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.conductivity_limit}.')
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.conductivity_limit:
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