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.

@@ -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().configure('MP')
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.oftype(PEC) # type: ignore
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.boundary_conditions)
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
- #beta = np.emath.sqrt(eigen_values[i])
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().configure('MT')
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' Field Amplitude = {np.abs(pfield):.3f}, Excitation = {np.abs(pmode):.2f}')
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' Field amplitude = {np.abs(pfield):.3f}, Excitation= {np.abs(pmode):.2f}')
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' Field Amplitude = {np.abs(pfield):.3f}, Excitation = {np.abs(pmode):.2f}')
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' Field amplitude = {np.abs(pfield):.3f}, Excitation= {np.abs(pmode):.2f}')
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 get_type(self, bctype: Literal['PEC','ModalPort','LumpedPort','PMC','LumpedElement','RectangularWaveguide','Periodic','FloquetPort']) -> FaceSelection:
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 = 376.730313412
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) * 4*np.pi*1e-7
185
+ return k0*299792458/self.get_beta(k0) * MU0
169
186
  elif self.modetype(k0)=='TM':
170
- return self.get_beta(k0)/(k0*299792458*8.854187818814*1e-12)
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)/376.73031341259)
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 376.73031341259
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*376.73031341259/(self.area))
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 * 4*np.pi*1e-7/self.get_beta(k0)
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 = 376.73031341259
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*376.730313412/self.surfZ
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 * (376.730313412/self.surfZ)
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 port
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
- def get_basis(self) -> np.ndarray:
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 self.cs._basis_inv
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*376.730313412/self.surfZ(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(376.730313412/(2*np.pi))
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*4*np.pi*1e-7) )
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 c8, c16, f8, i8, njit, prange, typeof, f4
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((c8[:, :], c8[:, :]))(
29
- c8[:, :],
30
- c8[:, :],
31
- f4[:, :],
32
- f4[:, :],
33
- f4[:, :],
34
- f4,
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.complex64)
90
- Hout = np.zeros((3, N)).astype(np.complex64)
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
- Z0 = np.float32(376.73031366857)
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.complex64(-1j * k0 / (4 * np.pi))
99
- ii = np.complex64(1j)
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.float32)
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.complex64),
168
- Hin.astype(np.complex64),
169
- vis.astype(np.float32),
170
- wns.astype(np.float32),
171
- tpout.astype(np.float32),
172
- np.float32(k0),
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
- yield A, b_active, self.solve_ids, reuse_factorization
103
+
104
+ yield A, b_active, self.solve_ids, reuse_factorization, aux
100
105
 
101
106
  reuse_factorization = True
102
107