emerge 1.0.0__py3-none-any.whl → 1.0.2__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 +7 -8
- emerge/_emerge/elements/femdata.py +4 -3
- emerge/_emerge/elements/nedelec2.py +8 -4
- emerge/_emerge/elements/nedleg2.py +6 -2
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/pcb.py +149 -66
- emerge/_emerge/geo/pcb_tools/dxf.py +361 -0
- emerge/_emerge/geo/polybased.py +23 -74
- emerge/_emerge/geo/shapes.py +31 -16
- emerge/_emerge/geometry.py +120 -21
- emerge/_emerge/mesh3d.py +62 -43
- emerge/_emerge/{_cache_check.py → mth/_cache_check.py} +2 -2
- emerge/_emerge/mth/optimized.py +69 -3
- emerge/_emerge/periodic.py +19 -17
- emerge/_emerge/physics/microwave/__init__.py +0 -1
- emerge/_emerge/physics/microwave/assembly/assembler.py +27 -5
- emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +2 -3
- emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -1
- emerge/_emerge/physics/microwave/assembly/robin_abc_order2.py +375 -0
- emerge/_emerge/physics/microwave/assembly/robinbc.py +37 -38
- emerge/_emerge/physics/microwave/microwave_3d.py +11 -19
- emerge/_emerge/physics/microwave/microwave_bc.py +38 -21
- emerge/_emerge/physics/microwave/microwave_data.py +3 -26
- emerge/_emerge/physics/microwave/port_functions.py +4 -4
- emerge/_emerge/plot/pyvista/display.py +13 -2
- emerge/_emerge/plot/simple_plots.py +4 -1
- emerge/_emerge/selection.py +12 -9
- emerge/_emerge/simmodel.py +68 -34
- emerge/_emerge/solver.py +28 -16
- emerge/beta/dxf.py +1 -0
- emerge/lib.py +1 -0
- emerge/materials/__init__.py +1 -0
- emerge/materials/isola.py +294 -0
- emerge/materials/rogers.py +58 -0
- {emerge-1.0.0.dist-info → emerge-1.0.2.dist-info}/METADATA +18 -4
- {emerge-1.0.0.dist-info → emerge-1.0.2.dist-info}/RECORD +39 -33
- {emerge-1.0.0.dist-info → emerge-1.0.2.dist-info}/WHEEL +0 -0
- {emerge-1.0.0.dist-info → emerge-1.0.2.dist-info}/entry_points.txt +0 -0
- {emerge-1.0.0.dist-info → emerge-1.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -312,34 +312,31 @@ def compute_bc_entries_excited(vertices_global, tris, Bmat, Bvec, surf_triangle_
|
|
|
312
312
|
|
|
313
313
|
|
|
314
314
|
@njit(c16[:,:](f8[:,:], f8[:], c16), cache=True, nogil=True, parallel=False)
|
|
315
|
-
def ned2_tri_stiff(
|
|
315
|
+
def ned2_tri_stiff(glob_vertices, edge_lengths, gamma):
|
|
316
316
|
''' Nedelec-2 Triangle Stiffness matrix and forcing vector (For Boundary Condition of the Third Kind)
|
|
317
317
|
|
|
318
318
|
'''
|
|
319
319
|
local_edge_map = np.array([[0,1,0],[1,2,2]])
|
|
320
320
|
Bmat = np.zeros((8,8), dtype=np.complex128)
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
axn = cross(ax1, ax2)
|
|
332
|
-
ax2 = -cross(axn, ax1)
|
|
322
|
+
orig = glob_vertices[:,0]
|
|
323
|
+
v2 = glob_vertices[:,1]
|
|
324
|
+
v3 = glob_vertices[:,2]
|
|
325
|
+
|
|
326
|
+
e1 = v2-orig
|
|
327
|
+
e2 = v3-orig
|
|
328
|
+
zhat = normalize(cross(e1, e2))
|
|
329
|
+
xhat = normalize(e1)
|
|
330
|
+
yhat = normalize(cross(zhat, xhat))
|
|
333
331
|
basis = np.zeros((3,3), dtype=np.float64)
|
|
334
|
-
basis[
|
|
335
|
-
basis[
|
|
336
|
-
basis[
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
332
|
+
basis[0,:] = xhat
|
|
333
|
+
basis[1,:] = yhat
|
|
334
|
+
basis[2,:] = zhat
|
|
335
|
+
lcs_vertices = optim_matmul(basis, glob_vertices - orig[:,np.newaxis])
|
|
336
|
+
|
|
340
337
|
xs = lcs_vertices[0,:]
|
|
341
338
|
ys = lcs_vertices[1,:]
|
|
342
|
-
|
|
339
|
+
|
|
343
340
|
x1, x2, x3 = xs
|
|
344
341
|
y1, y2, y3 = ys
|
|
345
342
|
|
|
@@ -359,7 +356,7 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
359
356
|
GLs = (GL1, GL2, GL3)
|
|
360
357
|
|
|
361
358
|
Area = 0.5 * np.abs((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1))
|
|
362
|
-
|
|
359
|
+
|
|
363
360
|
letters = [1,2,3,4,5,6]
|
|
364
361
|
|
|
365
362
|
tA, tB, tC = letters[0], letters[1], letters[2]
|
|
@@ -367,11 +364,12 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
367
364
|
|
|
368
365
|
Lt1, Lt2 = Ds[2, 0], Ds[1, 0]
|
|
369
366
|
|
|
367
|
+
|
|
370
368
|
COEFF = gamma/(2*Area)**2
|
|
371
369
|
AREA_COEFF = AREA_COEFF_CACHE_BASE * Area
|
|
372
370
|
for ei in range(3):
|
|
373
371
|
ei1, ei2 = local_edge_map[:, ei]
|
|
374
|
-
Li =
|
|
372
|
+
Li = Ds[ei1, ei2]
|
|
375
373
|
|
|
376
374
|
A = letters[ei1]
|
|
377
375
|
B = letters[ei2]
|
|
@@ -381,24 +379,25 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
381
379
|
|
|
382
380
|
for ej in range(3):
|
|
383
381
|
ej1, ej2 = local_edge_map[:, ej]
|
|
384
|
-
Lj =
|
|
382
|
+
Lj = Ds[ej1, ej2]
|
|
385
383
|
|
|
386
384
|
C = letters[ej1]
|
|
387
385
|
D = letters[ej2]
|
|
388
386
|
|
|
389
387
|
GC = GLs[ej1]
|
|
390
388
|
GD = GLs[ej2]
|
|
389
|
+
|
|
391
390
|
DAC = dot(GA,GC)
|
|
392
391
|
DAD = dot(GA,GD)
|
|
393
392
|
DBC = dot(GB,GC)
|
|
394
393
|
DBD = dot(GB,GD)
|
|
395
394
|
LL = Li*Lj
|
|
396
|
-
|
|
397
|
-
Bmat[ei,ej] += LL*
|
|
398
|
-
Bmat[ei,ej+4] += LL*
|
|
399
|
-
Bmat[ei+4,ej] += LL*
|
|
400
|
-
Bmat[ei+4,ej+4] += LL*
|
|
401
|
-
|
|
395
|
+
|
|
396
|
+
Bmat[ei,ej] += LL*(AREA_COEFF[A,B,C,D]*DAC-AREA_COEFF[A,B,C,C]*DAD-AREA_COEFF[A,A,C,D]*DBC+AREA_COEFF[A,A,C,C]*DBD)
|
|
397
|
+
Bmat[ei,ej+4] += LL*(AREA_COEFF[A,B,D,D]*DAC-AREA_COEFF[A,B,C,D]*DAD-AREA_COEFF[A,A,D,D]*DBC+AREA_COEFF[A,A,C,D]*DBD)
|
|
398
|
+
Bmat[ei+4,ej] += LL*(AREA_COEFF[B,B,C,D]*DAC-AREA_COEFF[B,B,C,C]*DAD-AREA_COEFF[A,B,C,D]*DBC+AREA_COEFF[A,B,C,C]*DBD)
|
|
399
|
+
Bmat[ei+4,ej+4] += LL*(AREA_COEFF[B,B,D,D]*DAC-AREA_COEFF[B,B,C,D]*DAD-AREA_COEFF[A,B,D,D]*DBC+AREA_COEFF[A,B,C,D]*DBD)
|
|
400
|
+
|
|
402
401
|
FA = dot(GA,GtC)
|
|
403
402
|
FB = dot(GA,GtA)
|
|
404
403
|
FC = dot(GB,GtC)
|
|
@@ -406,15 +405,15 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
406
405
|
FE = dot(GA,GtB)
|
|
407
406
|
FF = dot(GB,GtB)
|
|
408
407
|
|
|
409
|
-
Bmat[ei,3] += Li*Lt1*
|
|
410
|
-
Bmat[ei,7] += Li*Lt2*
|
|
411
|
-
Bmat[3,ei] += Lt1*Li*
|
|
412
|
-
Bmat[7,ei] += Lt2*Li*
|
|
413
|
-
Bmat[ei+4,3] += Li*Lt1*
|
|
414
|
-
Bmat[ei+4,7] += Li*Lt2*
|
|
415
|
-
Bmat[3,ei+4] += Lt1*Li*
|
|
416
|
-
Bmat[7,ei+4] += Lt2*Li*
|
|
417
|
-
|
|
408
|
+
Bmat[ei,3] += Li*Lt1*(AREA_COEFF[A,B,tA,tB]*FA-AREA_COEFF[A,B,tB,tC]*FB-AREA_COEFF[A,A,tA,tB]*FC+AREA_COEFF[A,A,tB,tC]*FD)
|
|
409
|
+
Bmat[ei,7] += Li*Lt2*(AREA_COEFF[A,B,tB,tC]*FB-AREA_COEFF[A,B,tC,tA]*FE-AREA_COEFF[A,A,tB,tC]*FD+AREA_COEFF[A,A,tC,tA]*FF)
|
|
410
|
+
Bmat[3,ei] += Lt1*Li*(AREA_COEFF[tA,tB,A,B]*FA-AREA_COEFF[tA,tB,A,A]*FC-AREA_COEFF[tB,tC,A,B]*FB+AREA_COEFF[tB,tC,A,A]*FD)
|
|
411
|
+
Bmat[7,ei] += Lt2*Li*(AREA_COEFF[tB,tC,A,B]*FB-AREA_COEFF[tB,tC,A,A]*FD-AREA_COEFF[tC,tA,A,B]*FE+AREA_COEFF[tC,tA,A,A]*FF)
|
|
412
|
+
Bmat[ei+4,3] += Li*Lt1*(AREA_COEFF[B,B,tA,tB]*FA-AREA_COEFF[B,B,tB,tC]*FB-AREA_COEFF[A,B,tA,tB]*FC+AREA_COEFF[A,B,tB,tC]*FD)
|
|
413
|
+
Bmat[ei+4,7] += Li*Lt2*(AREA_COEFF[B,B,tB,tC]*FB-AREA_COEFF[B,B,tC,tA]*FE-AREA_COEFF[A,B,tB,tC]*FD+AREA_COEFF[A,B,tC,tA]*FF)
|
|
414
|
+
Bmat[3,ei+4] += Lt1*Li*(AREA_COEFF[tA,tB,B,B]*FA-AREA_COEFF[tA,tB,A,B]*FC-AREA_COEFF[tB,tC,B,B]*FB+AREA_COEFF[tB,tC,A,B]*FD)
|
|
415
|
+
Bmat[7,ei+4] += Lt2*Li*(AREA_COEFF[tB,tC,B,B]*FB-AREA_COEFF[tB,tC,A,B]*FD-AREA_COEFF[tC,tA,B,B]*FE+AREA_COEFF[tC,tA,A,B]*FF)
|
|
416
|
+
|
|
418
417
|
H1 = dot(GtA,GtC)
|
|
419
418
|
H2 = dot(GtA,GtA)
|
|
420
419
|
H3 = dot(GtA,GtB)
|
|
@@ -423,8 +422,8 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
423
422
|
Bmat[3,7] += Lt1*Lt2*(AREA_COEFF[tA,tB,tB,tC]*H1-AREA_COEFF[tA,tB,tC,tA]*dot(GtB,GtC)-AREA_COEFF[tB,tC,tB,tC]*H2+AREA_COEFF[tB,tC,tC,tA]*H3)
|
|
424
423
|
Bmat[7,3] += Lt2*Lt1*(AREA_COEFF[tB,tC,tA,tB]*H1-AREA_COEFF[tB,tC,tB,tC]*H2-AREA_COEFF[tC,tA,tA,tB]*dot(GtB,GtC)+AREA_COEFF[tC,tA,tB,tC]*H3)
|
|
425
424
|
Bmat[7,7] += Lt2*Lt2*(AREA_COEFF[tB,tC,tB,tC]*H2-AREA_COEFF[tB,tC,tC,tA]*H3-AREA_COEFF[tC,tA,tB,tC]*H3+AREA_COEFF[tC,tA,tC,tA]*dot(GtB,GtB))
|
|
426
|
-
|
|
427
425
|
|
|
426
|
+
Bmat = Bmat * COEFF
|
|
428
427
|
return Bmat
|
|
429
428
|
|
|
430
429
|
@njit(c16[:](f8[:,:], i8[:,:], c16[:], f8[:,:], i8[:], c16), cache=True, nogil=True, parallel=False)
|
|
@@ -25,7 +25,6 @@ from ...elements.nedelec2 import Nedelec2
|
|
|
25
25
|
from ...solver import DEFAULT_ROUTINE, SolveRoutine
|
|
26
26
|
from ...system import called_from_main_function
|
|
27
27
|
from ...selection import FaceSelection
|
|
28
|
-
from ...mth.optimized import compute_distances
|
|
29
28
|
from ...settings import Settings
|
|
30
29
|
from .microwave_bc import MWBoundaryConditionSet, PEC, ModalPort, LumpedPort, PortBC
|
|
31
30
|
from .microwave_data import MWData
|
|
@@ -101,21 +100,6 @@ def shortest_path(xyz1: np.ndarray, xyz2: np.ndarray, Npts: int) -> np.ndarray:
|
|
|
101
100
|
path = (1 - t) * p1[:, np.newaxis] + t * p2[:, np.newaxis]
|
|
102
101
|
|
|
103
102
|
return path
|
|
104
|
-
|
|
105
|
-
def _pick_central(vertices: np.ndarray) -> np.ndarray:
|
|
106
|
-
"""Computes the coordinate in the vertex set that has the shortest square distance to all other points.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
vertices (np.ndarray): The set of coordinates [3,:]
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
np.ndarray: The most central point
|
|
114
|
-
"""
|
|
115
|
-
Ds = compute_distances(vertices[0,:], vertices[1,:], vertices[2,:])
|
|
116
|
-
sumDs = np.sum(Ds**2, axis=1)
|
|
117
|
-
id_central = np.argwhere(sumDs==np.min(sumDs)).flatten()[0]
|
|
118
|
-
return vertices[:, id_central].squeeze()
|
|
119
103
|
|
|
120
104
|
class Microwave3D:
|
|
121
105
|
"""The Electrodynamics time harmonic physics class.
|
|
@@ -248,6 +232,14 @@ class Microwave3D:
|
|
|
248
232
|
self.set_frequency(np.linspace(fmin, fmax, Npoints))
|
|
249
233
|
|
|
250
234
|
def fdense(self, Npoints: int) -> np.ndarray:
|
|
235
|
+
"""Return a resampled version of the current frequency range
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
Npoints (int): The new number of points
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
np.ndarray: The new frequency axis
|
|
242
|
+
"""
|
|
251
243
|
if len(self.frequencies) == 1:
|
|
252
244
|
raise ValueError('Only 1 frequency point known. At least two need to be defined.')
|
|
253
245
|
fmin = min(self.frequencies)
|
|
@@ -497,7 +489,7 @@ class Microwave3D:
|
|
|
497
489
|
if freq is None:
|
|
498
490
|
freq = self.frequencies[0]
|
|
499
491
|
|
|
500
|
-
materials = self.mesh.
|
|
492
|
+
materials = self.mesh._get_material_assignment(self.mesher.volumes)
|
|
501
493
|
|
|
502
494
|
ertet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
503
495
|
tandtet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
@@ -655,7 +647,7 @@ class Microwave3D:
|
|
|
655
647
|
if self.basis is None:
|
|
656
648
|
raise SimulationError('Cannot proceed, the simulation basis class is undefined.')
|
|
657
649
|
|
|
658
|
-
materials = self.mesh.
|
|
650
|
+
materials = self.mesh._get_material_assignment(self.mesher.volumes)
|
|
659
651
|
|
|
660
652
|
### Does this move
|
|
661
653
|
logger.debug('Initializing frequency domain sweep.')
|
|
@@ -853,7 +845,7 @@ class Microwave3D:
|
|
|
853
845
|
if self.basis is None:
|
|
854
846
|
raise SimulationError('Cannot proceed. The simulation basis class is undefined.')
|
|
855
847
|
|
|
856
|
-
materials = self.mesh.
|
|
848
|
+
materials = self.mesh._get_material_assignment(self.mesher.volumes)
|
|
857
849
|
|
|
858
850
|
# er = self.mesh.retreive(lambda mat,x,y,z: mat.fer3d_mat(x,y,z), self.mesher.volumes)
|
|
859
851
|
# ur = self.mesh.retreive(lambda mat,x,y,z: mat.fur3d_mat(x,y,z), self.mesher.volumes)
|
|
@@ -19,19 +19,17 @@ from __future__ import annotations
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
from loguru import logger
|
|
21
21
|
from typing import Callable, Literal
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from collections import defaultdict
|
|
22
24
|
from ...selection import Selection, FaceSelection
|
|
23
25
|
from ...cs import CoordinateSystem, Axis, GCS, _parse_axis
|
|
24
26
|
from ...coord import Line
|
|
25
27
|
from ...geometry import GeoSurface, GeoObject
|
|
26
|
-
from dataclasses import dataclass
|
|
27
|
-
from collections import defaultdict
|
|
28
28
|
from ...bc import BoundaryCondition, BoundaryConditionSet, Periodic
|
|
29
29
|
from ...periodic import PeriodicCell, HexCell, RectCell
|
|
30
30
|
from ...material import Material
|
|
31
31
|
from ...const import Z0, C0, EPS0, MU0
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
33
|
############################################################
|
|
36
34
|
# UTILITY FUNCTIONS #
|
|
37
35
|
############################################################
|
|
@@ -125,6 +123,7 @@ class RobinBC(BoundaryCondition):
|
|
|
125
123
|
_include_stiff: bool = False
|
|
126
124
|
_include_mass: bool = False
|
|
127
125
|
_include_force: bool = False
|
|
126
|
+
_isabc: bool = False
|
|
128
127
|
|
|
129
128
|
def __init__(self, selection: GeoSurface | Selection):
|
|
130
129
|
"""A Generalization of any boundary condition of the third kind (Robin).
|
|
@@ -264,11 +263,13 @@ class AbsorbingBoundary(RobinBC):
|
|
|
264
263
|
_include_stiff: bool = True
|
|
265
264
|
_include_mass: bool = True
|
|
266
265
|
_include_force: bool = False
|
|
267
|
-
|
|
266
|
+
_isabc: bool = True
|
|
267
|
+
|
|
268
268
|
def __init__(self,
|
|
269
269
|
face: FaceSelection | GeoSurface,
|
|
270
|
-
order: int =
|
|
271
|
-
origin: tuple | None = None
|
|
270
|
+
order: int = 2,
|
|
271
|
+
origin: tuple | None = None,
|
|
272
|
+
abctype: Literal['A','B','C','D','E'] = 'B'):
|
|
272
273
|
"""Creates an AbsorbingBoundary condition.
|
|
273
274
|
|
|
274
275
|
Currently only a first order boundary condition is possible. Second order will be supported later.
|
|
@@ -286,7 +287,15 @@ class AbsorbingBoundary(RobinBC):
|
|
|
286
287
|
self.order: int = order
|
|
287
288
|
self.origin: tuple = origin
|
|
288
289
|
self.cs: CoordinateSystem = GCS
|
|
289
|
-
|
|
290
|
+
|
|
291
|
+
self.abctype: Literal['A','B','C','D','E'] = abctype
|
|
292
|
+
self.o2coeffs: tuple[float, float] = {'A': (1.0, -0.5),
|
|
293
|
+
'B': (1.00023, -0.51555),
|
|
294
|
+
'C': (1.03084, -0.73631),
|
|
295
|
+
'D': (1.06103, -0.84883),
|
|
296
|
+
'E': (1.12500, -1.00000)
|
|
297
|
+
}
|
|
298
|
+
|
|
290
299
|
def get_basis(self) -> np.ndarray:
|
|
291
300
|
return np.eye(3)
|
|
292
301
|
|
|
@@ -306,15 +315,10 @@ class AbsorbingBoundary(RobinBC):
|
|
|
306
315
|
Returns:
|
|
307
316
|
complex: The γ-constant
|
|
308
317
|
"""
|
|
309
|
-
if self.order
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
ky = k0*0.5
|
|
314
|
-
return 1j*k0*p0 - 1j*p2*ky**2/k0
|
|
315
|
-
else:
|
|
316
|
-
Factor = 1
|
|
317
|
-
return 1j*self.get_beta(k0)*Factor
|
|
318
|
+
if self.order==1:
|
|
319
|
+
return 1j*k0
|
|
320
|
+
|
|
321
|
+
return 1j*k0*self.o2coeffs[self.abctype][0]
|
|
318
322
|
|
|
319
323
|
|
|
320
324
|
@dataclass
|
|
@@ -1002,6 +1006,7 @@ class SurfaceImpedance(RobinBC):
|
|
|
1002
1006
|
material: Material | None = None,
|
|
1003
1007
|
surface_conductance: float | None = None,
|
|
1004
1008
|
surface_roughness: float = 0,
|
|
1009
|
+
thickness: float | None = None,
|
|
1005
1010
|
sr_model: Literal['Hammerstad-Jensen'] = 'Hammerstad-Jensen',
|
|
1006
1011
|
):
|
|
1007
1012
|
"""Generates a SurfaceImpedance bounary condition.
|
|
@@ -1021,7 +1026,8 @@ class SurfaceImpedance(RobinBC):
|
|
|
1021
1026
|
material (Material | None, optional): The matrial to assign. Defaults to None.
|
|
1022
1027
|
surface_conductance (float | None, optional): The specific bulk conductivity to use. Defaults to None.
|
|
1023
1028
|
surface_roughness (float, optional): The surface roughness. Defaults to 0.
|
|
1024
|
-
|
|
1029
|
+
thickness (float | None, optional): The layer thickness. Defaults to None
|
|
1030
|
+
sr_model (Literal["Hammerstad-Jensen", optional): The surface roughness model. Defaults to 'Hammerstad-Jensen'.
|
|
1025
1031
|
"""
|
|
1026
1032
|
super().__init__(face)
|
|
1027
1033
|
|
|
@@ -1029,7 +1035,11 @@ class SurfaceImpedance(RobinBC):
|
|
|
1029
1035
|
self._mur: float | complex = 1.0
|
|
1030
1036
|
self._epsr: float | complex = 1.0
|
|
1031
1037
|
self.sigma: float = 0.0
|
|
1038
|
+
self.thickness: float | None = thickness
|
|
1032
1039
|
|
|
1040
|
+
if isinstance(face, GeoObject) and thickness is None:
|
|
1041
|
+
self.thickness = face._load('thickness')
|
|
1042
|
+
|
|
1033
1043
|
if material is not None:
|
|
1034
1044
|
self.sigma = material.cond.scalar(1e9)
|
|
1035
1045
|
self._mur = material.ur
|
|
@@ -1067,10 +1077,17 @@ class SurfaceImpedance(RobinBC):
|
|
|
1067
1077
|
sigma = self.sigma
|
|
1068
1078
|
mur = self._material.ur.scalar(f0)
|
|
1069
1079
|
er = self._material.er.scalar(f0)
|
|
1070
|
-
|
|
1080
|
+
eps = EPS0*er
|
|
1081
|
+
mu = MU0*mur
|
|
1071
1082
|
rho = 1/sigma
|
|
1072
|
-
d_skin = (2*rho/(w0*
|
|
1073
|
-
|
|
1083
|
+
d_skin = (2*rho/(w0*mu) * ((1+(w0*eps*rho)**2)**0.5 + rho*w0*eps))**0.5
|
|
1084
|
+
logger.debug(f'Computed skin depth δ={d_skin*1e6:.2}μm')
|
|
1085
|
+
R = (1+1j)*rho/d_skin
|
|
1086
|
+
if self.thickness is not None:
|
|
1087
|
+
eps_c = eps - 1j * sigma / w0
|
|
1088
|
+
gamma_m = 1j * w0 * np.sqrt(mu*eps_c)
|
|
1089
|
+
R = R / np.tanh(gamma_m * self.thickness)
|
|
1090
|
+
logger.debug(f'Impedance scaler due to thickness: {1/ np.tanh(gamma_m * self.thickness) :.4f}')
|
|
1074
1091
|
if self._sr_model=='Hammerstad-Jensen' and self._sr > 0.0:
|
|
1075
1092
|
R = R * (1 + 2/np.pi * np.arctan(1.4*(self._sr/d_skin)**2))
|
|
1076
1093
|
return 1j*k0*Z0/R
|
|
@@ -1036,32 +1036,6 @@ class MWField:
|
|
|
1036
1036
|
return E[0,:], E[1,:], E[2,:], H[0,:], H[1,:], H[2,:]
|
|
1037
1037
|
|
|
1038
1038
|
return dict(freq=freq, ff_function=function)
|
|
1039
|
-
|
|
1040
|
-
# def surface_integral(self, faces: FaceSelection | GeoSurface, fieldfunction: Callable) -> float | complex:
|
|
1041
|
-
# """Computes a surface integral on the selected faces.
|
|
1042
|
-
|
|
1043
|
-
# The fieldfunction argument must be a callable of a single argument x, which will
|
|
1044
|
-
# be of type EHField which is restuned by the field.interpolate(x,y,z) function. It has
|
|
1045
|
-
# fields like Ez, Ey, Sx etc that can be called.
|
|
1046
|
-
|
|
1047
|
-
# Args:
|
|
1048
|
-
# faces (FaceSelection | GeoSurface): _description_
|
|
1049
|
-
# fieldfunction (Callable): _description_
|
|
1050
|
-
|
|
1051
|
-
# Returns:
|
|
1052
|
-
# float | complex: _description_
|
|
1053
|
-
# """
|
|
1054
|
-
# from ...mth.integrals import surface_integral
|
|
1055
|
-
|
|
1056
|
-
# def ff(x, y, z):
|
|
1057
|
-
# fieldobj = self.interpolate(x,y,z)
|
|
1058
|
-
# return fieldfunction(fieldobj)
|
|
1059
|
-
|
|
1060
|
-
# nodes = self.mesh.get_nodes(faces.tags)
|
|
1061
|
-
# triangles = self.mesh.get_triangles(faces.tags)
|
|
1062
|
-
|
|
1063
|
-
# return surface_integral(nodes, triangles, ff)
|
|
1064
|
-
|
|
1065
1039
|
|
|
1066
1040
|
class MWScalar:
|
|
1067
1041
|
"""The MWDataSet class stores solution data of FEM Time Harmonic simulations.
|
|
@@ -1137,6 +1111,9 @@ class MWScalarNdim:
|
|
|
1137
1111
|
self._portmap: dict[int, float|int] = dict()
|
|
1138
1112
|
self._portnumbers: list[int | float] = []
|
|
1139
1113
|
|
|
1114
|
+
def dense_f(self, N: int) -> np.ndarray:
|
|
1115
|
+
return np.linspace(np.min(self.freq), np.max(self.freq), N)
|
|
1116
|
+
|
|
1140
1117
|
def S(self, i1: int, i2: int) -> np.ndarray:
|
|
1141
1118
|
return self.Sp[...,self._portmap[i1], self._portmap[i2]]
|
|
1142
1119
|
|
|
@@ -17,11 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
from ...elements.nedleg2 import NedelecLegrange2
|
|
20
|
-
from ...mth.integrals import surface_integral
|
|
21
|
-
|
|
22
20
|
|
|
23
21
|
def compute_avg_power_flux(field: NedelecLegrange2, mode: np.ndarray, k0: float, ur: np.ndarray, beta: float):
|
|
24
|
-
|
|
22
|
+
from ...mth.integrals import surface_integral
|
|
23
|
+
|
|
25
24
|
Efunc = field.interpolate_Ef(mode)
|
|
26
25
|
Hfunc = field.interpolate_Hf(mode, k0, ur, beta)
|
|
27
26
|
nx, ny, nz = field.mesh.normals[:,0]
|
|
@@ -37,7 +36,8 @@ def compute_avg_power_flux(field: NedelecLegrange2, mode: np.ndarray, k0: float,
|
|
|
37
36
|
return Ptot
|
|
38
37
|
|
|
39
38
|
def compute_peak_power_flux(field: NedelecLegrange2, mode: np.ndarray, k0: float, ur: np.ndarray, beta: float):
|
|
40
|
-
|
|
39
|
+
from ...mth.integrals import surface_integral
|
|
40
|
+
|
|
41
41
|
Efunc = field.interpolate_Ef(mode)
|
|
42
42
|
Hfunc = field.interpolate_Hf(mode, k0, ur, beta)
|
|
43
43
|
nx, ny, nz = field.mesh.normals[:,0]
|
|
@@ -435,7 +435,7 @@ class PVDisplay(BaseDisplay):
|
|
|
435
435
|
return None
|
|
436
436
|
|
|
437
437
|
## OBLIGATORY METHODS
|
|
438
|
-
def add_object(self, obj: GeoObject | Selection, mesh: bool = False, volume_mesh: bool = True, *args, **kwargs):
|
|
438
|
+
def add_object(self, obj: GeoObject | Selection, mesh: bool = False, volume_mesh: bool = True, label: bool = False, *args, **kwargs):
|
|
439
439
|
|
|
440
440
|
show_edges = False
|
|
441
441
|
opacity = obj.opacity
|
|
@@ -471,7 +471,10 @@ class PVDisplay(BaseDisplay):
|
|
|
471
471
|
line_width=line_width,
|
|
472
472
|
show_edges=show_edges,
|
|
473
473
|
pickable=True,
|
|
474
|
+
smooth_shading=False,
|
|
475
|
+
split_sharp_edges=True,
|
|
474
476
|
style=style)
|
|
477
|
+
|
|
475
478
|
mesh_obj = self.mesh(obj)
|
|
476
479
|
|
|
477
480
|
if mesh is True and volume_mesh is True:
|
|
@@ -485,7 +488,15 @@ class PVDisplay(BaseDisplay):
|
|
|
485
488
|
mapper.SetRelativeCoincidentTopologyPolygonOffsetParameters(1,0.5)
|
|
486
489
|
|
|
487
490
|
self._plot.add_mesh(self._volume_edges(_select(obj)), color='#000000', line_width=2, show_edges=True)
|
|
488
|
-
|
|
491
|
+
|
|
492
|
+
if isinstance(obj, GeoObject) and label:
|
|
493
|
+
points = []
|
|
494
|
+
labels = []
|
|
495
|
+
for dt in obj.dimtags:
|
|
496
|
+
points.append(self._mesh.dimtag_to_center[dt])
|
|
497
|
+
labels.append(obj.name)
|
|
498
|
+
self._plot.add_point_labels(points, labels, shape_color='white')
|
|
499
|
+
|
|
489
500
|
def add_objects(self, *objects, **kwargs) -> None:
|
|
490
501
|
"""Add a series of objects provided as a list of arguments
|
|
491
502
|
"""
|
|
@@ -317,6 +317,7 @@ and sparse frequency annotations (e.g., labeled by frequency).
|
|
|
317
317
|
colors_list = _broadcast(colors, None, 'colors')
|
|
318
318
|
lw_list = _broadcast(linewidth, None, 'linewidth')
|
|
319
319
|
labels_list: Optional[List[Optional[str]]]
|
|
320
|
+
|
|
320
321
|
if labels is None:
|
|
321
322
|
labels_list = None
|
|
322
323
|
else:
|
|
@@ -377,7 +378,9 @@ and sparse frequency annotations (e.g., labeled by frequency).
|
|
|
377
378
|
|
|
378
379
|
# frequency labels (sparse)
|
|
379
380
|
fi = fs_list[i]
|
|
380
|
-
if fi
|
|
381
|
+
if fi is None:
|
|
382
|
+
continue
|
|
383
|
+
if n_flabels > 0 and len(s) > 0 and len(fi) > 0:
|
|
381
384
|
n = min(len(s), len(fi))
|
|
382
385
|
step = max(1, int(round(n / n_flabels))) if n_flabels > 0 else n # avoid step=0
|
|
383
386
|
idx = np.arange(0, n, step)
|
emerge/_emerge/selection.py
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
import gmsh # type: ignore
|
|
20
20
|
import numpy as np
|
|
21
|
-
from scipy.spatial import ConvexHull # type: ignore
|
|
22
21
|
from .cs import Axis, CoordinateSystem, _parse_vector, Plane
|
|
23
22
|
from typing import Callable, TypeVar, Iterable, Any
|
|
24
23
|
|
|
@@ -34,6 +33,8 @@ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, An
|
|
|
34
33
|
"""
|
|
35
34
|
|
|
36
35
|
# 1. centroid
|
|
36
|
+
from scipy.spatial import ConvexHull # type: ignore
|
|
37
|
+
|
|
37
38
|
Omat = np.squeeze(np.mean(pts3d, axis=0))
|
|
38
39
|
|
|
39
40
|
# 2. build e_x, e_y
|
|
@@ -537,9 +538,8 @@ class Selector:
|
|
|
537
538
|
x: float,
|
|
538
539
|
y: float,
|
|
539
540
|
z: float,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
nz: float,
|
|
541
|
+
normal_axis: Axis | tuple[float, float, float] | None = None,
|
|
542
|
+
plane: Plane | None = None,
|
|
543
543
|
tolerance: float = 1e-8) -> FaceSelection:
|
|
544
544
|
"""Returns a FaceSelection for all faces that lie in a provided infinite plane
|
|
545
545
|
specified by an origin plus a plane normal vector.
|
|
@@ -548,17 +548,20 @@ class Selector:
|
|
|
548
548
|
x (float): The plane origin X-coordinate
|
|
549
549
|
y (float): The plane origin Y-coordinate
|
|
550
550
|
z (float): The plane origin Z-coordinate
|
|
551
|
-
|
|
552
|
-
ny (float): The plane normal Y-component
|
|
553
|
-
nz (float): The plane normal Z-component
|
|
551
|
+
normal_axis (Axis, tuple): The plane normal vector
|
|
554
552
|
tolerance (float, optional): An in plane tolerance (displacement and normal dot product). Defaults to 1e-6.
|
|
555
553
|
|
|
556
554
|
Returns:
|
|
557
555
|
FaceSelection: All faces that lie in the specified plane
|
|
558
556
|
"""
|
|
559
557
|
orig = np.array([x,y,z])
|
|
560
|
-
|
|
561
|
-
|
|
558
|
+
if plane is not None:
|
|
559
|
+
norm = plane.normal.np
|
|
560
|
+
elif normal_axis is not None:
|
|
561
|
+
norm = _parse_vector(normal_axis)
|
|
562
|
+
norm = norm/np.linalg.norm(norm)
|
|
563
|
+
else:
|
|
564
|
+
raise RuntimeError('No plane or axis defined for selection.')
|
|
562
565
|
|
|
563
566
|
dimtags = gmsh.model.getEntities(2)
|
|
564
567
|
coords = [gmsh.model.occ.getCenterOfMass(*tag) for tag in dimtags]
|