emerge 0.5.2__py3-none-any.whl → 0.5.3__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/_emerge/bc.py +3 -12
- emerge/_emerge/const.py +5 -0
- emerge/_emerge/elements/nedleg2.py +2 -2
- emerge/_emerge/geo/pcb.py +110 -13
- emerge/_emerge/geo/pcb_tools/calculator.py +2 -2
- emerge/_emerge/geometry.py +1 -1
- emerge/_emerge/logsettings.py +12 -13
- emerge/_emerge/material.py +4 -0
- emerge/_emerge/mth/integrals.py +1 -1
- emerge/_emerge/physics/microwave/adaptive_freq.py +1 -5
- emerge/_emerge/physics/microwave/assembly/assembler.py +62 -39
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +1 -8
- emerge/_emerge/physics/microwave/microwave_3d.py +33 -26
- emerge/_emerge/physics/microwave/microwave_bc.py +97 -27
- emerge/_emerge/physics/microwave/microwave_data.py +3 -5
- emerge/_emerge/physics/microwave/sc.py +26 -26
- emerge/_emerge/physics/microwave/simjob.py +8 -3
- emerge/_emerge/simmodel.py +12 -8
- emerge/_emerge/simulation_data.py +5 -1
- emerge/_emerge/solve_interfaces/cudss_interface.py +238 -0
- emerge/_emerge/solver.py +285 -107
- emerge/cli.py +1 -1
- emerge/lib.py +2 -5
- {emerge-0.5.2.dist-info → emerge-0.5.3.dist-info}/METADATA +5 -1
- {emerge-0.5.2.dist-info → emerge-0.5.3.dist-info}/RECORD +28 -26
- {emerge-0.5.2.dist-info → emerge-0.5.3.dist-info}/WHEEL +0 -0
- {emerge-0.5.2.dist-info → emerge-0.5.3.dist-info}/entry_points.txt +0 -0
- {emerge-0.5.2.dist-info → emerge-0.5.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -22,16 +22,12 @@ from numba_progress import ProgressBar, ProgressBarType
|
|
|
22
22
|
from ....mth.optimized import local_mapping, matinv, dot_c, cross_c, compute_distances
|
|
23
23
|
from numba import c16, types, f8, i8, njit, prange
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
############################################################
|
|
27
26
|
# CACHED FACTORIAL VALUES #
|
|
28
27
|
############################################################
|
|
29
28
|
|
|
30
|
-
|
|
31
29
|
_FACTORIALS = np.array([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880], dtype=np.int64)
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
31
|
############################################################
|
|
36
32
|
# INDEX MAPPING FUNCTIONS #
|
|
37
33
|
############################################################
|
|
@@ -264,7 +260,6 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
264
260
|
Dmat[ei+0,ej+10] = Q*VAD
|
|
265
261
|
Dmat[ei+10,ej+0] = Q*VBC
|
|
266
262
|
Dmat[ei+10,ej+10] = Q*VBD
|
|
267
|
-
|
|
268
263
|
|
|
269
264
|
Fmat[ei+0,ej+0] = Q2*(VABCD*BD1-VABCC*BE1-VAACD*BF1+VAACC*BG1)
|
|
270
265
|
Fmat[ei+0,ej+10] = Q2*(VABDD*BD1-VABCD*BE1-VAADD*BF1+VAACD*BG1)
|
|
@@ -313,7 +308,6 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
313
308
|
BH1 = dot_c(GB,BC1)
|
|
314
309
|
BI1 = dot_c(GA,BD1)
|
|
315
310
|
BJ1 = dot_c(GB,BD1)
|
|
316
|
-
|
|
317
311
|
|
|
318
312
|
Dmat[ei+0,ej+6] = L1*Lac2*(-6*VAD*AI1-3*VAC*AJ1-3*VAF*AK1)
|
|
319
313
|
Dmat[ei+0,ej+16] = L1*Lab2*(6*VAF*AK1+3*VAD*AI1-3*VAC*AJ1)
|
|
@@ -372,7 +366,6 @@ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_ma
|
|
|
372
366
|
VBCEF = VOLUME_COEFF_CACHE[B,E,F,C]
|
|
373
367
|
VADEF = VOLUME_COEFF_CACHE[E,A,D,F]
|
|
374
368
|
|
|
375
|
-
|
|
376
369
|
Lac2 = Ds[ej1, fj]
|
|
377
370
|
Lab2 = Ds[ej1, ej2]
|
|
378
371
|
|
|
@@ -447,7 +440,7 @@ def _matrix_builder(nodes, tets, tris, edges, all_edge_lengths, tet_to_field, te
|
|
|
447
440
|
dataB = np.empty_like(rows, dtype=np.complex128)
|
|
448
441
|
|
|
449
442
|
|
|
450
|
-
for itet in prange(nT):
|
|
443
|
+
for itet in prange(nT): # ty: ignore
|
|
451
444
|
p = itet*400
|
|
452
445
|
if np.mod(itet,10)==0:
|
|
453
446
|
pgb.update(10)
|
|
@@ -24,21 +24,22 @@ from ...elements.nedelec2 import Nedelec2
|
|
|
24
24
|
from ...solver import DEFAULT_ROUTINE, SolveRoutine
|
|
25
25
|
from ...system import called_from_main_function
|
|
26
26
|
from ...selection import FaceSelection
|
|
27
|
-
from scipy.sparse.linalg import inv as sparse_inverse # type: ignore
|
|
28
27
|
from .microwave_bc import MWBoundaryConditionSet, PEC, ModalPort, LumpedPort, PortBC
|
|
29
28
|
from .microwave_data import MWData
|
|
30
29
|
from .assembly.assembler import Assembler
|
|
31
30
|
from .port_functions import compute_avg_power_flux
|
|
32
31
|
from .simjob import SimJob
|
|
32
|
+
|
|
33
33
|
from concurrent.futures import ThreadPoolExecutor
|
|
34
|
-
import numpy as np
|
|
35
34
|
from loguru import logger
|
|
36
35
|
from typing import Callable, Literal
|
|
37
|
-
import time
|
|
38
|
-
import threading
|
|
39
36
|
import multiprocessing as mp
|
|
40
37
|
from cmath import sqrt as csqrt
|
|
41
38
|
|
|
39
|
+
import numpy as np
|
|
40
|
+
import threading
|
|
41
|
+
import time
|
|
42
|
+
|
|
42
43
|
class SimulationError(Exception):
|
|
43
44
|
pass
|
|
44
45
|
|
|
@@ -51,9 +52,10 @@ def run_job_multi(job: SimJob) -> SimJob:
|
|
|
51
52
|
Returns:
|
|
52
53
|
SimJob: The solved SimJob
|
|
53
54
|
"""
|
|
54
|
-
routine = DEFAULT_ROUTINE.duplicate().
|
|
55
|
-
for A, b, ids, reuse in job.iter_Ab():
|
|
55
|
+
routine = DEFAULT_ROUTINE.duplicate()._configure_routine('MP')
|
|
56
|
+
for A, b, ids, reuse, aux in job.iter_Ab():
|
|
56
57
|
solution, report = routine.solve(A, b, ids, reuse, id=job.id)
|
|
58
|
+
report.add(**aux)
|
|
57
59
|
job.submit_solution(solution, report)
|
|
58
60
|
return job
|
|
59
61
|
|
|
@@ -117,7 +119,6 @@ class Microwave3D:
|
|
|
117
119
|
self.bc: MWBoundaryConditionSet = MWBoundaryConditionSet(None)
|
|
118
120
|
self.basis: Nedelec2 | None = None
|
|
119
121
|
self.solveroutine: SolveRoutine = DEFAULT_ROUTINE
|
|
120
|
-
self.set_order(order)
|
|
121
122
|
self.cache_matrices: bool = True
|
|
122
123
|
|
|
123
124
|
## States
|
|
@@ -129,6 +130,8 @@ class Microwave3D:
|
|
|
129
130
|
self._simstart: float = 0.0
|
|
130
131
|
self._simend: float = 0.0
|
|
131
132
|
|
|
133
|
+
self.set_order(order)
|
|
134
|
+
|
|
132
135
|
def reset_data(self):
|
|
133
136
|
self.data = MWData()
|
|
134
137
|
|
|
@@ -352,7 +355,7 @@ class Microwave3D:
|
|
|
352
355
|
raise ValueError('The field basis is not yet defined.')
|
|
353
356
|
|
|
354
357
|
logger.debug('Finding PEC TEM conductors')
|
|
355
|
-
pecs: list[PEC] = self.bc.
|
|
358
|
+
pecs: list[PEC] = self.bc.get_conductors() # type: ignore
|
|
356
359
|
mesh = self.mesh
|
|
357
360
|
|
|
358
361
|
# Process all PEC Boundary Conditions
|
|
@@ -472,13 +475,10 @@ class Microwave3D:
|
|
|
472
475
|
if freq is None:
|
|
473
476
|
freq = self.frequencies[0]
|
|
474
477
|
|
|
475
|
-
|
|
476
478
|
k0 = 2*np.pi*freq/299792458
|
|
477
479
|
kmax = k0*np.sqrt(ermax.real*urmax.real)
|
|
478
|
-
|
|
479
|
-
logger.info('Assembling BMA Matrices')
|
|
480
480
|
|
|
481
|
-
Amatrix, Bmatrix, solve_ids, nlf = self.assembler.assemble_bma_matrices(self.basis, er, ur, cond, k0, port, self.bc
|
|
481
|
+
Amatrix, Bmatrix, solve_ids, nlf = self.assembler.assemble_bma_matrices(self.basis, er, ur, cond, k0, port, self.bc)
|
|
482
482
|
|
|
483
483
|
logger.debug(f'Total of {Amatrix.shape[0]} Degrees of freedom.')
|
|
484
484
|
logger.debug(f'Applied frequency: {freq/1e9:.2f}GHz')
|
|
@@ -515,7 +515,7 @@ class Microwave3D:
|
|
|
515
515
|
Emode = Emode * np.exp(-1j*np.angle(np.max(Emode)))
|
|
516
516
|
|
|
517
517
|
beta = min(k0*np.sqrt(ermax*urmax), np.emath.sqrt(-eigen_values[i]))
|
|
518
|
-
|
|
518
|
+
|
|
519
519
|
residuals = -1
|
|
520
520
|
|
|
521
521
|
portfE = nlf.interpolate_Ef(Emode)
|
|
@@ -628,19 +628,21 @@ class Microwave3D:
|
|
|
628
628
|
## DEFINE SOLVE FUNCTIONS
|
|
629
629
|
def get_routine():
|
|
630
630
|
if not hasattr(thread_local, "routine"):
|
|
631
|
-
thread_local.routine = self.solveroutine.duplicate().
|
|
631
|
+
thread_local.routine = self.solveroutine.duplicate()._configure_routine('MT')
|
|
632
632
|
return thread_local.routine
|
|
633
633
|
|
|
634
634
|
def run_job(job: SimJob):
|
|
635
635
|
routine = get_routine()
|
|
636
|
-
for A, b, ids, reuse in job.iter_Ab():
|
|
636
|
+
for A, b, ids, reuse, aux in job.iter_Ab():
|
|
637
637
|
solution, report = routine.solve(A, b, ids, reuse, id=job.id)
|
|
638
|
+
report.add(**aux)
|
|
638
639
|
job.submit_solution(solution, report)
|
|
639
640
|
return job
|
|
640
641
|
|
|
641
642
|
def run_job_single(job: SimJob):
|
|
642
|
-
for A, b, ids, reuse in job.iter_Ab():
|
|
643
|
+
for A, b, ids, reuse, aux in job.iter_Ab():
|
|
643
644
|
solution, report = self.solveroutine.solve(A, b, ids, reuse, id=job.id)
|
|
645
|
+
report.add(**aux)
|
|
644
646
|
job.submit_solution(solution, report)
|
|
645
647
|
return job
|
|
646
648
|
|
|
@@ -753,6 +755,15 @@ class Microwave3D:
|
|
|
753
755
|
thread_local.__dict__.clear()
|
|
754
756
|
logger.info('Solving complete')
|
|
755
757
|
|
|
758
|
+
for freq, job in zip(self.frequencies, results):
|
|
759
|
+
self.data.setreport(job.reports, freq=freq, **self._params)
|
|
760
|
+
|
|
761
|
+
for variables, data in self.data.sim.iterate():
|
|
762
|
+
logger.trace(f'Sim variable: {variables}')
|
|
763
|
+
for item in data['report']:
|
|
764
|
+
item.pretty_print(logger.trace)
|
|
765
|
+
|
|
766
|
+
self.solveroutine.reset()
|
|
756
767
|
### Compute S-parameters and return
|
|
757
768
|
self._post_process(results, er, ur, cond)
|
|
758
769
|
return self.data
|
|
@@ -883,8 +894,6 @@ class Microwave3D:
|
|
|
883
894
|
fielddata._der = np.squeeze(er[0,0,:])
|
|
884
895
|
fielddata._dur = np.squeeze(ur[0,0,:])
|
|
885
896
|
|
|
886
|
-
self.data.setreport(job.reports, freq=freq, **self._params)
|
|
887
|
-
|
|
888
897
|
logger.info(f'Post Processing simulation frequency = {freq/1e9:.3f} GHz')
|
|
889
898
|
|
|
890
899
|
# Recording port information
|
|
@@ -915,20 +924,18 @@ class Microwave3D:
|
|
|
915
924
|
Pout = 0.0 + 0j
|
|
916
925
|
|
|
917
926
|
# Active port power
|
|
918
|
-
logger.debug('Active ports:')
|
|
919
927
|
tris = mesh.get_triangles(active_port.tags)
|
|
920
928
|
tri_vertices = mesh.tris[:,tris]
|
|
921
929
|
pfield, pmode = self._compute_s_data(active_port, fieldf, tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
|
|
922
|
-
logger.debug(f'
|
|
930
|
+
logger.debug(f'[{active_port.port_number}] Active port amplitude = {np.abs(pfield):.3f} (Excitation = {np.abs(pmode):.2f})')
|
|
923
931
|
Pout = pmode
|
|
924
932
|
|
|
925
933
|
#Passive ports
|
|
926
|
-
logger.debug('Passive ports:')
|
|
927
934
|
for bc in all_ports:
|
|
928
935
|
tris = mesh.get_triangles(bc.tags)
|
|
929
936
|
tri_vertices = mesh.tris[:,tris]
|
|
930
937
|
pfield, pmode = self._compute_s_data(bc, fieldf,tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
|
|
931
|
-
logger.debug(f'
|
|
938
|
+
logger.debug(f'[{bc.port_number}] Passive amplitude = {np.abs(pfield):.3f}')
|
|
932
939
|
scalardata.write_S(bc.port_number, active_port.port_number, pfield/Pout)
|
|
933
940
|
active_port.active=False
|
|
934
941
|
|
|
@@ -988,8 +995,8 @@ class Microwave3D:
|
|
|
988
995
|
elif bc.modetype(k0) == 'TM':
|
|
989
996
|
const = 1/((erp[0,0,:] + erp[1,1,:] + erp[2,2,:])/3)
|
|
990
997
|
const = np.squeeze(const)
|
|
991
|
-
field_p = sparam_field_power(self.mesh.nodes, tri_vertices, bc, k0, fieldfunction, const)
|
|
992
|
-
mode_p = sparam_mode_power(self.mesh.nodes, tri_vertices, bc, k0, const)
|
|
998
|
+
field_p = sparam_field_power(self.mesh.nodes, tri_vertices, bc, k0, fieldfunction, const, 5)
|
|
999
|
+
mode_p = sparam_mode_power(self.mesh.nodes, tri_vertices, bc, k0, const, 5)
|
|
993
1000
|
return field_p, mode_p
|
|
994
1001
|
|
|
995
1002
|
# def frequency_domain_single(self, automatic_modal_analysis: bool = False) -> MWData:
|
|
@@ -1099,7 +1106,7 @@ class Microwave3D:
|
|
|
1099
1106
|
# tris = mesh.get_triangles(active_port.tags)
|
|
1100
1107
|
# tri_vertices = mesh.tris[:,tris]
|
|
1101
1108
|
# pfield, pmode = self._compute_s_data(active_port, fieldf, tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
|
|
1102
|
-
# logger.debug(f'
|
|
1109
|
+
# logger.debug(f'Field Amplitude = {np.abs(pfield):.3f}, Excitation = {np.abs(pmode):.2f}')
|
|
1103
1110
|
# Pout = pmode
|
|
1104
1111
|
|
|
1105
1112
|
# #Passive ports
|
|
@@ -1108,7 +1115,7 @@ class Microwave3D:
|
|
|
1108
1115
|
# tris = mesh.get_triangles(bc.tags)
|
|
1109
1116
|
# tri_vertices = mesh.tris[:,tris]
|
|
1110
1117
|
# pfield, pmode = self._compute_s_data(bc, fieldf, tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
|
|
1111
|
-
# logger.debug(f'
|
|
1118
|
+
# logger.debug(f'Field amplitude = {np.abs(pfield):.3f}, Excitation= {np.abs(pmode):.2f}')
|
|
1112
1119
|
# scalardata.write_S(bc.port_number, active_port.port_number, pfield/Pout)
|
|
1113
1120
|
|
|
1114
1121
|
# active_port.active=False
|
|
@@ -27,6 +27,8 @@ from dataclasses import dataclass
|
|
|
27
27
|
from collections import defaultdict
|
|
28
28
|
from ...bc import BoundaryCondition, BoundaryConditionSet, Periodic
|
|
29
29
|
from ...periodic import PeriodicCell, HexCell, RectCell
|
|
30
|
+
from ...material import Material
|
|
31
|
+
from ...const import Z0, C0, PI, EPS0, MU0
|
|
30
32
|
|
|
31
33
|
class MWBoundaryConditionSet(BoundaryConditionSet):
|
|
32
34
|
|
|
@@ -39,13 +41,28 @@ class MWBoundaryConditionSet(BoundaryConditionSet):
|
|
|
39
41
|
self.ModalPort: type[ModalPort] = self._construct_bc(ModalPort)
|
|
40
42
|
self.LumpedPort: type[LumpedPort] = self._construct_bc(LumpedPort)
|
|
41
43
|
self.LumpedElement: type[LumpedElement] = self._construct_bc(LumpedElement)
|
|
44
|
+
self.SurfaceImpedance: type[SurfaceImpedance] = self._construct_bc(SurfaceImpedance)
|
|
42
45
|
self.RectangularWaveguide: type[RectangularWaveguide] = self._construct_bc(RectangularWaveguide)
|
|
43
46
|
self.Periodic: type[Periodic] = self._construct_bc(Periodic)
|
|
44
47
|
self.FloquetPort: type[FloquetPort] = self._construct_bc(FloquetPort)
|
|
45
48
|
|
|
46
49
|
self._cell: PeriodicCell | None = None
|
|
47
50
|
|
|
48
|
-
def
|
|
51
|
+
def get_conductors(self) -> list[BoundaryCondition]:
|
|
52
|
+
"""Returns a list of all boundary conditions that ought to be considered as a "conductor"
|
|
53
|
+
for the purpose of modal analyses.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
list[BoundaryCondition]: All conductor like boundary conditions
|
|
57
|
+
"""
|
|
58
|
+
bcs = self.oftype(PEC)
|
|
59
|
+
for bc in self.oftype(SurfaceImpedance):
|
|
60
|
+
if bc.material.cond > 1e3:
|
|
61
|
+
bcs.append(bc)
|
|
62
|
+
|
|
63
|
+
return bcs
|
|
64
|
+
|
|
65
|
+
def get_type(self, bctype: Literal['PEC','ModalPort','LumpedPort','PMC','LumpedElement','RectangularWaveguide','Periodic','FloquetPort','SurfaceImpedance']) -> FaceSelection:
|
|
49
66
|
tags = []
|
|
50
67
|
for bc in self.boundary_conditions:
|
|
51
68
|
if bctype in str(bc.__class__):
|
|
@@ -119,7 +136,7 @@ class RobinBC(BoundaryCondition):
|
|
|
119
136
|
raise NotImplementedError('get_Uinc not implemented for Port class')
|
|
120
137
|
|
|
121
138
|
class PortBC(RobinBC):
|
|
122
|
-
Zvac: float =
|
|
139
|
+
Zvac: float = Z0
|
|
123
140
|
def __init__(self, face: FaceSelection | GeoSurface):
|
|
124
141
|
"""(DO NOT USE) A generalization of the Port boundary condition.
|
|
125
142
|
|
|
@@ -165,9 +182,9 @@ class PortBC(RobinBC):
|
|
|
165
182
|
if self.modetype(k0)=='TEM':
|
|
166
183
|
return self.Zvac
|
|
167
184
|
elif self.modetype(k0)=='TE':
|
|
168
|
-
return k0*299792458/self.get_beta(k0) *
|
|
185
|
+
return k0*299792458/self.get_beta(k0) * MU0
|
|
169
186
|
elif self.modetype(k0)=='TM':
|
|
170
|
-
return self.get_beta(k0)/(k0*299792458*
|
|
187
|
+
return self.get_beta(k0)/(k0*299792458*EPS0)
|
|
171
188
|
else:
|
|
172
189
|
raise ValueError(f'Port mode type should be TEM, TE or TM but instead is {self.modetype(k0)}')
|
|
173
190
|
|
|
@@ -183,7 +200,7 @@ class PortBC(RobinBC):
|
|
|
183
200
|
Returns:
|
|
184
201
|
float: The mode amplitude correction factor.
|
|
185
202
|
"""
|
|
186
|
-
return np.sqrt(self.Zmode(k0)/
|
|
203
|
+
return np.sqrt(self.Zmode(k0)/Z0)
|
|
187
204
|
|
|
188
205
|
@property
|
|
189
206
|
def mode_number(self) -> int:
|
|
@@ -300,7 +317,7 @@ class PortMode:
|
|
|
300
317
|
|
|
301
318
|
def set_power(self, power: complex) -> None:
|
|
302
319
|
self.norm_factor = np.sqrt(1/np.abs(power))
|
|
303
|
-
logger.info(f'Setting port mode amplitude to: {self.norm_factor} ')
|
|
320
|
+
logger.info(f'Setting port mode amplitude to: {self.norm_factor:.2f} ')
|
|
304
321
|
|
|
305
322
|
class FloquetPort(PortBC):
|
|
306
323
|
_include_stiff: bool = True
|
|
@@ -334,7 +351,7 @@ class FloquetPort(PortBC):
|
|
|
334
351
|
self.cs = GCS
|
|
335
352
|
|
|
336
353
|
def portZ0(self, k0: float | None = None) -> complex | float | None:
|
|
337
|
-
return
|
|
354
|
+
return Z0
|
|
338
355
|
|
|
339
356
|
def get_amplitude(self, k0: float) -> float:
|
|
340
357
|
return 1.0
|
|
@@ -372,7 +389,7 @@ class FloquetPort(PortBC):
|
|
|
372
389
|
P = self.pol_p
|
|
373
390
|
S = self.pol_s
|
|
374
391
|
|
|
375
|
-
E0 = self.get_amplitude(k0)*np.sqrt(2*
|
|
392
|
+
E0 = self.get_amplitude(k0)*np.sqrt(2*Z0/(self.area))
|
|
376
393
|
Ex = E0*(-S*np.sin(self.scan_phi) - P*np.cos(self.scan_theta)*np.cos(self.scan_phi))*phi
|
|
377
394
|
Ey = E0*(S*np.cos(self.scan_phi) - P*np.cos(self.scan_theta)*np.sin(self.scan_phi))*phi
|
|
378
395
|
Ez = E0*(-P*E0*np.sin(self.scan_theta))*phi
|
|
@@ -646,13 +663,13 @@ class RectangularWaveguide(PortBC):
|
|
|
646
663
|
return self.cs._basis_inv
|
|
647
664
|
|
|
648
665
|
def portZ0(self, k0: float) -> complex:
|
|
649
|
-
return k0*299792458 *
|
|
666
|
+
return k0*299792458 * MU0/self.get_beta(k0)
|
|
650
667
|
|
|
651
668
|
def modetype(self, k0):
|
|
652
669
|
return self.type
|
|
653
670
|
|
|
654
671
|
def get_amplitude(self, k0: float) -> float:
|
|
655
|
-
Zte =
|
|
672
|
+
Zte = Z0
|
|
656
673
|
amplitude= np.sqrt(self.power*4*Zte/(self.dims[0]*self.dims[1]))
|
|
657
674
|
return amplitude
|
|
658
675
|
|
|
@@ -807,10 +824,10 @@ class LumpedPort(PortBC):
|
|
|
807
824
|
Returns:
|
|
808
825
|
complex: The γ-constant
|
|
809
826
|
"""
|
|
810
|
-
return 1j*k0*
|
|
827
|
+
return 1j*k0*Z0/self.surfZ
|
|
811
828
|
|
|
812
829
|
def get_Uinc(self, x_local, y_local, k0) -> np.ndarray:
|
|
813
|
-
Emag = -1j*2*k0 * self.voltage/self.height * (
|
|
830
|
+
Emag = -1j*2*k0 * self.voltage/self.height * (Z0/self.surfZ)
|
|
814
831
|
return Emag*self.port_mode_3d(x_local, y_local, k0)
|
|
815
832
|
|
|
816
833
|
def port_mode_3d(self,
|
|
@@ -899,16 +916,9 @@ class LumpedElement(RobinBC):
|
|
|
899
916
|
self.Z0: Callable = impedance_function # type: ignore
|
|
900
917
|
self.width: float = width # type: ignore
|
|
901
918
|
self.height: float = height # type: ignore
|
|
902
|
-
|
|
903
|
-
logger.info('Constructing coordinate system from normal port')
|
|
904
|
-
self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
|
|
905
|
-
|
|
906
|
-
self.vintline: Line | None = None
|
|
907
|
-
self.v_integration = True
|
|
908
|
-
self.iintline: Line | None = None
|
|
909
919
|
|
|
910
920
|
def surfZ(self, k0: float) -> float:
|
|
911
|
-
"""The surface sheet impedance for the lumped
|
|
921
|
+
"""The surface sheet impedance for the lumped Element
|
|
912
922
|
|
|
913
923
|
Returns:
|
|
914
924
|
float: The surface sheet impedance
|
|
@@ -916,18 +926,17 @@ class LumpedElement(RobinBC):
|
|
|
916
926
|
Z0 = self.Z0(k0*299792458/(2*np.pi))*self.width/self.height
|
|
917
927
|
return Z0
|
|
918
928
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
return self.cs._basis
|
|
929
|
+
def get_basis(self) -> np.ndarray | None:
|
|
930
|
+
return None
|
|
922
931
|
|
|
923
|
-
def get_inv_basis(self) -> np.ndarray:
|
|
924
|
-
return
|
|
932
|
+
def get_inv_basis(self) -> np.ndarray | None:
|
|
933
|
+
return None
|
|
925
934
|
|
|
926
935
|
def get_beta(self, k0: float) -> float:
|
|
927
936
|
''' Return the out of plane propagation constant. βz.'''
|
|
928
937
|
|
|
929
938
|
return k0
|
|
930
|
-
|
|
939
|
+
|
|
931
940
|
def get_gamma(self, k0: float) -> complex:
|
|
932
941
|
"""Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
|
|
933
942
|
|
|
@@ -937,6 +946,67 @@ class LumpedElement(RobinBC):
|
|
|
937
946
|
Returns:
|
|
938
947
|
complex: The γ-constant
|
|
939
948
|
"""
|
|
940
|
-
return 1j*k0*
|
|
949
|
+
return 1j*k0*Z0/self.surfZ(k0)
|
|
950
|
+
|
|
941
951
|
|
|
942
952
|
|
|
953
|
+
class SurfaceImpedance(RobinBC):
|
|
954
|
+
|
|
955
|
+
_include_stiff: bool = True
|
|
956
|
+
_include_mass: bool = False
|
|
957
|
+
_include_force: bool = False
|
|
958
|
+
|
|
959
|
+
def __init__(self,
|
|
960
|
+
face: FaceSelection | GeoSurface,
|
|
961
|
+
material: Material | None = None,
|
|
962
|
+
):
|
|
963
|
+
"""Generates a lumped power boundary condition.
|
|
964
|
+
|
|
965
|
+
The lumped port boundary condition assumes a uniform E-field along the "direction" axis.
|
|
966
|
+
The port with and height must be provided manually in meters. The height is the size
|
|
967
|
+
in the "direction" axis along which the potential is imposed. The width dimension
|
|
968
|
+
is orthogonal to that. For a rectangular face its the width and for a cyllindrical face
|
|
969
|
+
its the circumpherance.
|
|
970
|
+
|
|
971
|
+
Args:
|
|
972
|
+
face (FaceSelection, GeoSurface): The port surface
|
|
973
|
+
port_number (int): The port number
|
|
974
|
+
width (float): The port width (meters).
|
|
975
|
+
height (float): The port height (meters).
|
|
976
|
+
direction (Axis): The port direction as an Axis object (em.Axis(..) or em.ZAX)
|
|
977
|
+
active (bool, optional): Whether the port is active. Defaults to False.
|
|
978
|
+
power (float, optional): The port output power. Defaults to 1.
|
|
979
|
+
Z0 (float, optional): The port impedance. Defaults to 50.
|
|
980
|
+
"""
|
|
981
|
+
super().__init__(face)
|
|
982
|
+
|
|
983
|
+
self.material: Material = material
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
def get_basis(self) -> np.ndarray | None:
|
|
987
|
+
return None
|
|
988
|
+
|
|
989
|
+
def get_inv_basis(self) -> np.ndarray | None:
|
|
990
|
+
return None
|
|
991
|
+
|
|
992
|
+
def get_beta(self, k0: float) -> float:
|
|
993
|
+
''' Return the out of plane propagation constant. βz.'''
|
|
994
|
+
|
|
995
|
+
return k0
|
|
996
|
+
|
|
997
|
+
def get_gamma(self, k0: float) -> complex:
|
|
998
|
+
"""Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
|
|
999
|
+
|
|
1000
|
+
Args:
|
|
1001
|
+
k0 (float): The free space propagation constant.
|
|
1002
|
+
|
|
1003
|
+
Returns:
|
|
1004
|
+
complex: The γ-constant
|
|
1005
|
+
"""
|
|
1006
|
+
w0 = k0*C0
|
|
1007
|
+
sigma = self.material.cond
|
|
1008
|
+
rho = 1/sigma
|
|
1009
|
+
d_skin = (2*rho/(w0*MU0) * ((1+(w0*EPS0*rho)**2)**0.5 + rho*w0*EPS0))**0.5
|
|
1010
|
+
d_skin = (2*rho/(w0*MU0))**0.5
|
|
1011
|
+
R = rho/d_skin
|
|
1012
|
+
return 1j*k0*Z0/R
|
|
@@ -27,6 +27,7 @@ from ...cs import Axis, _parse_axis
|
|
|
27
27
|
from ...selection import FaceSelection
|
|
28
28
|
from ...geometry import GeoSurface
|
|
29
29
|
from ...mesh3d import Mesh3D
|
|
30
|
+
from ...const import Z0, MU0, EPS0
|
|
30
31
|
|
|
31
32
|
EMField = Literal[
|
|
32
33
|
"er", "ur", "freq", "k0",
|
|
@@ -36,9 +37,6 @@ EMField = Literal[
|
|
|
36
37
|
"mode", "beta",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
|
-
EPS0 = 8.854187818814e-12
|
|
40
|
-
MU0 = 1.2566370612720e-6
|
|
41
|
-
|
|
42
40
|
def arc_on_plane(ref_dir, normal, angle_range_deg, num_points=100):
|
|
43
41
|
"""
|
|
44
42
|
Generate theta/phi coordinates of an arc on a plane.
|
|
@@ -297,7 +295,7 @@ class FarfieldData:
|
|
|
297
295
|
logger.warning('Defaulting to normE')
|
|
298
296
|
F = np.sqrt(np.abs(self.E[0,:])**2 + np.abs(self.E[1,:])**2 + np.abs(self.E[2,:])**2)
|
|
299
297
|
if isotropic:
|
|
300
|
-
F = F/np.sqrt(
|
|
298
|
+
F = F/np.sqrt(Z0/(2*np.pi))
|
|
301
299
|
if dB:
|
|
302
300
|
F = 20*np.log10(np.clip(np.abs(F), a_min=10**(dBfloor/20), a_max = 1e9))-dBfloor
|
|
303
301
|
if rmax is not None:
|
|
@@ -662,7 +660,7 @@ class MWField:
|
|
|
662
660
|
self.Ez = Ez.reshape(shp)
|
|
663
661
|
|
|
664
662
|
|
|
665
|
-
constants = 1/ (-1j*2*np.pi*self.freq*(self._dur*
|
|
663
|
+
constants = 1/ (-1j*2*np.pi*self.freq*(self._dur*MU0) )
|
|
666
664
|
Hx, Hy, Hz = self.basis.interpolate_curl(self._field, xf, yf, zf, constants)
|
|
667
665
|
ids = self.basis.interpolate_index(xf, yf, zf)
|
|
668
666
|
self.er = self._der[ids].reshape(shp)
|
|
@@ -19,19 +19,21 @@ from numba_progress import ProgressBar
|
|
|
19
19
|
from ...mesh3d import SurfaceMesh
|
|
20
20
|
import numpy as np
|
|
21
21
|
from loguru import logger
|
|
22
|
-
from numba import
|
|
23
|
-
from numba.types import Tuple as TupleType
|
|
22
|
+
from numba import c16, njit, prange, f8
|
|
23
|
+
from numba.types import Tuple as TupleType # ty: ignore
|
|
24
24
|
from numba_progress.progress import ProgressBarType
|
|
25
|
+
from ...const import Z0
|
|
25
26
|
|
|
26
27
|
LR = 0.001
|
|
28
|
+
|
|
27
29
|
@njit(
|
|
28
|
-
TupleType((
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
TupleType((c16[:, :], c16[:, :]))(
|
|
31
|
+
c16[:, :],
|
|
32
|
+
c16[:, :],
|
|
33
|
+
f8[:, :],
|
|
34
|
+
f8[:, :],
|
|
35
|
+
f8[:, :],
|
|
36
|
+
f8,
|
|
35
37
|
ProgressBarType,
|
|
36
38
|
),
|
|
37
39
|
parallel=True,
|
|
@@ -86,17 +88,15 @@ def stratton_chu_ff(Ein, Hin, vis, wns, tpout, k0, pgb):
|
|
|
86
88
|
|
|
87
89
|
N = tpout.shape[1]
|
|
88
90
|
|
|
89
|
-
Eout = np.zeros((3, N)).astype(np.
|
|
90
|
-
Hout = np.zeros((3, N)).astype(np.
|
|
91
|
-
|
|
92
|
-
Eoutx = np.zeros((N,)).astype(np.complex64)
|
|
93
|
-
Eouty = np.zeros((N,)).astype(np.complex64)
|
|
94
|
-
Eoutz = np.zeros((N,)).astype(np.complex64)
|
|
91
|
+
Eout = np.zeros((3, N)).astype(np.complex128)
|
|
92
|
+
Hout = np.zeros((3, N)).astype(np.complex128)
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
Eoutx = np.zeros((N,)).astype(np.complex128)
|
|
95
|
+
Eouty = np.zeros((N,)).astype(np.complex128)
|
|
96
|
+
Eoutz = np.zeros((N,)).astype(np.complex128)
|
|
97
97
|
|
|
98
|
-
Q = np.
|
|
99
|
-
ii = np.
|
|
98
|
+
Q = np.complex128(-1j * k0 / (4 * np.pi))
|
|
99
|
+
ii = np.complex128(1j)
|
|
100
100
|
|
|
101
101
|
NxHx = ny * Hz - nz * Hy
|
|
102
102
|
NxHy = nz * Hx - nx * Hz
|
|
@@ -106,7 +106,7 @@ def stratton_chu_ff(Ein, Hin, vis, wns, tpout, k0, pgb):
|
|
|
106
106
|
NxEy = nz * Ex - nx * Ez
|
|
107
107
|
NxEz = nx * Ey - ny * Ex
|
|
108
108
|
|
|
109
|
-
for j in prange(Nids):
|
|
109
|
+
for j in prange(Nids): # ty: ignore
|
|
110
110
|
xi = vx[j]
|
|
111
111
|
yi = vy[j]
|
|
112
112
|
zi = vz[j]
|
|
@@ -147,7 +147,7 @@ def stratton_chu(Ein, Hin, mesh: SurfaceMesh, theta: np.ndarray, phi: np.ndarray
|
|
|
147
147
|
areas = mesh.areas
|
|
148
148
|
vis = mesh.edge_centers
|
|
149
149
|
|
|
150
|
-
wns = np.zeros_like(vis).astype(np.
|
|
150
|
+
wns = np.zeros_like(vis).astype(np.float64)
|
|
151
151
|
|
|
152
152
|
tri_normals = mesh.normals
|
|
153
153
|
tri_ids = mesh.tri_to_edge
|
|
@@ -164,12 +164,12 @@ def stratton_chu(Ein, Hin, mesh: SurfaceMesh, theta: np.ndarray, phi: np.ndarray
|
|
|
164
164
|
tpout = np.array([theta, phi])
|
|
165
165
|
with ProgressBar(total=Ntot, ncols=100, dynamic_ncols=False) as pgb:
|
|
166
166
|
Eout, Hout = stratton_chu_ff(
|
|
167
|
-
Ein.astype(np.
|
|
168
|
-
Hin.astype(np.
|
|
169
|
-
vis.astype(np.
|
|
170
|
-
wns.astype(np.
|
|
171
|
-
tpout.astype(np.
|
|
172
|
-
np.
|
|
167
|
+
Ein.astype(np.complex128),
|
|
168
|
+
Hin.astype(np.complex128),
|
|
169
|
+
vis.astype(np.float64),
|
|
170
|
+
wns.astype(np.float64),
|
|
171
|
+
tpout.astype(np.float64),
|
|
172
|
+
np.float64(k0),
|
|
173
173
|
pgb,
|
|
174
174
|
)
|
|
175
175
|
return Eout.astype(np.complex128), Hout.astype(np.complex128)
|
|
@@ -27,7 +27,7 @@ class SimJob:
|
|
|
27
27
|
b: np.ndarray | None,
|
|
28
28
|
freq: float,
|
|
29
29
|
cache_factorization: bool,
|
|
30
|
-
B: csr_matrix = None
|
|
30
|
+
B: csr_matrix | None = None
|
|
31
31
|
):
|
|
32
32
|
|
|
33
33
|
self.A: csr_matrix = A
|
|
@@ -88,15 +88,20 @@ class SimJob:
|
|
|
88
88
|
|
|
89
89
|
b_active = self.b + mode
|
|
90
90
|
A = self.load_if_needed('A')
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
aux = {
|
|
93
|
+
'Active port': str(key),
|
|
94
|
+
}
|
|
92
95
|
|
|
93
96
|
if self.has_periodic:
|
|
94
97
|
P = self.load_if_needed('P')
|
|
95
98
|
Pd = self.load_if_needed('Pd')
|
|
96
99
|
b_active = Pd @ b_active
|
|
97
100
|
A = Pd @ A @ P
|
|
101
|
+
aux['Periodic reduction'] = str(P.shape)
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
|
|
104
|
+
yield A, b_active, self.solve_ids, reuse_factorization, aux
|
|
100
105
|
|
|
101
106
|
reuse_factorization = True
|
|
102
107
|
|