emerge 0.5.0__py3-none-any.whl → 0.5.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.

Files changed (47) hide show
  1. emerge/_emerge/bc.py +11 -8
  2. emerge/_emerge/cs.py +2 -2
  3. emerge/_emerge/elements/femdata.py +14 -14
  4. emerge/_emerge/elements/index_interp.py +1 -1
  5. emerge/_emerge/elements/ned2_interp.py +1 -1
  6. emerge/_emerge/elements/nedelec2.py +4 -4
  7. emerge/_emerge/elements/nedleg2.py +9 -9
  8. emerge/_emerge/geo/horn.py +1 -1
  9. emerge/_emerge/geo/modeler.py +18 -19
  10. emerge/_emerge/geo/operations.py +13 -10
  11. emerge/_emerge/geo/pcb.py +70 -69
  12. emerge/_emerge/geo/pcb_tools/macro.py +14 -13
  13. emerge/_emerge/geo/pmlbox.py +1 -1
  14. emerge/_emerge/geometry.py +46 -32
  15. emerge/_emerge/logsettings.py +3 -3
  16. emerge/_emerge/material.py +11 -11
  17. emerge/_emerge/mesh3d.py +81 -59
  18. emerge/_emerge/mesher.py +26 -21
  19. emerge/_emerge/mth/pairing.py +2 -2
  20. emerge/_emerge/periodic.py +34 -31
  21. emerge/_emerge/physics/microwave/adaptive_freq.py +14 -11
  22. emerge/_emerge/physics/microwave/assembly/assembler.py +61 -57
  23. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
  24. emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
  25. emerge/_emerge/physics/microwave/microwave_3d.py +40 -20
  26. emerge/_emerge/physics/microwave/microwave_bc.py +114 -95
  27. emerge/_emerge/physics/microwave/microwave_data.py +33 -33
  28. emerge/_emerge/physics/microwave/simjob.py +12 -12
  29. emerge/_emerge/physics/microwave/sparam.py +12 -12
  30. emerge/_emerge/physics/microwave/touchstone.py +1 -1
  31. emerge/_emerge/plot/display.py +12 -6
  32. emerge/_emerge/plot/pyvista/display.py +44 -39
  33. emerge/_emerge/plot/pyvista/display_settings.py +1 -1
  34. emerge/_emerge/plot/simple_plots.py +15 -15
  35. emerge/_emerge/selection.py +35 -39
  36. emerge/_emerge/simmodel.py +29 -39
  37. emerge/_emerge/simulation_data.py +19 -14
  38. emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
  39. emerge/_emerge/solver.py +52 -52
  40. emerge/lib.py +243 -243
  41. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/METADATA +1 -1
  42. emerge-0.5.2.dist-info/RECORD +81 -0
  43. emerge/_emerge/plot/grapher.py +0 -93
  44. emerge-0.5.0.dist-info/RECORD +0 -82
  45. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/WHEEL +0 -0
  46. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/entry_points.txt +0 -0
  47. {emerge-0.5.0.dist-info → emerge-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -30,7 +30,7 @@ from ...periodic import PeriodicCell, HexCell, RectCell
30
30
 
31
31
  class MWBoundaryConditionSet(BoundaryConditionSet):
32
32
 
33
- def __init__(self, periodic_cell: PeriodicCell):
33
+ def __init__(self, periodic_cell: PeriodicCell | None):
34
34
  super().__init__()
35
35
 
36
36
  self.PEC: type[PEC] = self._construct_bc(PEC)
@@ -43,7 +43,7 @@ class MWBoundaryConditionSet(BoundaryConditionSet):
43
43
  self.Periodic: type[Periodic] = self._construct_bc(Periodic)
44
44
  self.FloquetPort: type[FloquetPort] = self._construct_bc(FloquetPort)
45
45
 
46
- self._cell: PeriodicCell = None
46
+ self._cell: PeriodicCell | None = None
47
47
 
48
48
  def get_type(self, bctype: Literal['PEC','ModalPort','LumpedPort','PMC','LumpedElement','RectangularWaveguide','Periodic','FloquetPort']) -> FaceSelection:
49
49
  tags = []
@@ -101,21 +101,21 @@ class RobinBC(BoundaryCondition):
101
101
  """
102
102
  super().__init__(selection)
103
103
  self.v_integration: bool = False
104
- self.vintline: Line = None
104
+ self.vintline: Line | None = None
105
105
 
106
106
  def get_basis(self) -> np.ndarray:
107
- return None
107
+ raise NotImplementedError('This method is not implemented')
108
108
 
109
- def get_inv_basis(self) -> np.ndarray:
110
- return None
109
+ def get_inv_basis(self) -> np.ndarray | None :
110
+ raise NotImplementedError('This method is not implemented')
111
111
 
112
- def get_beta(self, k0) -> float:
112
+ def get_beta(self, k0: float) -> float:
113
113
  raise NotImplementedError('get_beta not implemented for Port class')
114
114
 
115
- def get_gamma(self, k0) -> float:
115
+ def get_gamma(self, k0: float) -> complex:
116
116
  raise NotImplementedError('get_gamma not implemented for Port class')
117
117
 
118
- def get_Uinc(self, k0) -> np.ndarray:
118
+ def get_Uinc(self, x_local: np.ndarray, y_local: np.ndarray, k0: float) -> np.ndarray:
119
119
  raise NotImplementedError('get_Uinc not implemented for Port class')
120
120
 
121
121
  class PortBC(RobinBC):
@@ -130,19 +130,24 @@ class PortBC(RobinBC):
130
130
  face (FaceSelection | GeoSurface): The port face
131
131
  """
132
132
  super().__init__(face)
133
- self.port_number: int = None
134
- self.cs: CoordinateSystem = None
133
+ self.port_number: int = -1
134
+ self.cs: CoordinateSystem = GCS
135
135
  self.selected_mode: int = 0
136
- self.Z0 = None
137
- self.active: bool = False
136
+ self.Z0: complex | float | None = None
137
+ self.active: bool | None = False
138
+ self.power: float = 1.0
138
139
 
140
+ @property
141
+ def voltage(self) -> complex | None:
142
+ return None
143
+
139
144
  def get_basis(self) -> np.ndarray:
140
145
  return self.cs._basis
141
146
 
142
147
  def get_inv_basis(self) -> np.ndarray:
143
148
  return self.cs._basis_inv
144
149
 
145
- def portZ0(self, k0: float = None) -> complex:
150
+ def portZ0(self, k0: float) -> complex | float | None:
146
151
  """Returns the port characteristic impedance given a phase constant
147
152
 
148
153
  Args:
@@ -164,7 +169,7 @@ class PortBC(RobinBC):
164
169
  elif self.modetype(k0)=='TM':
165
170
  return self.get_beta(k0)/(k0*299792458*8.854187818814*1e-12)
166
171
  else:
167
- return ValueError(f'Port mode type should be TEM, TE or TM but instead is {self.modetype(k0)}')
172
+ raise ValueError(f'Port mode type should be TEM, TE or TM but instead is {self.modetype(k0)}')
168
173
 
169
174
  def _qmode(self, k0: float) -> float:
170
175
  """Computes a mode amplitude correction factor.
@@ -188,7 +193,7 @@ class PortBC(RobinBC):
188
193
  ''' Return the out of plane propagation constant. βz.'''
189
194
  return k0
190
195
 
191
- def get_gamma(self, k0):
196
+ def get_gamma(self, k0) -> complex:
192
197
  """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
193
198
 
194
199
  Args:
@@ -226,7 +231,7 @@ class AbsorbingBoundary(RobinBC):
226
231
  def __init__(self,
227
232
  face: FaceSelection | GeoSurface,
228
233
  order: int = 1,
229
- origin: tuple = None):
234
+ origin: tuple | None = None):
230
235
  """Creates an AbsorbingBoundary condition.
231
236
 
232
237
  Currently only a first order boundary condition is possible. Second order will be supported later.
@@ -239,19 +244,23 @@ class AbsorbingBoundary(RobinBC):
239
244
  origin (tuple, optional): The radiation origin. Defaults to None.
240
245
  """
241
246
  super().__init__(face)
242
-
247
+ if origin is None:
248
+ origin = (0., 0., 0.)
243
249
  self.order: int = order
244
250
  self.origin: tuple = origin
245
251
  self.cs: CoordinateSystem = GCS
246
252
 
247
253
  def get_basis(self) -> np.ndarray:
248
254
  return np.eye(3)
255
+
256
+ def get_inv_basis(self) -> np.ndarray | None:
257
+ return None
249
258
 
250
- def get_beta(self, k0) -> float:
259
+ def get_beta(self, k0: float) -> float:
251
260
  ''' Return the out of plane propagation constant. βz.'''
252
261
  return k0
253
262
 
254
- def get_gamma(self, k0):
263
+ def get_gamma(self, k0: float) -> complex:
255
264
  """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
256
265
 
257
266
  Args:
@@ -262,7 +271,7 @@ class AbsorbingBoundary(RobinBC):
262
271
  """
263
272
  return 1j*self.get_beta(k0)
264
273
 
265
- def get_Uinc(self, x_local, y_local, k0) -> np.ndarray:
274
+ def get_Uinc(self, x_local: np.ndarray, y_local: np.ndarray, k0: float) -> np.ndarray:
266
275
  return np.zeros((3, len(x_local)), dtype=np.complex128)
267
276
 
268
277
  @dataclass
@@ -273,13 +282,13 @@ class PortMode:
273
282
  k0: float
274
283
  beta: float
275
284
  residual: float
276
- energy: float = None
285
+ energy: float = 0
277
286
  norm_factor: float = 1
278
- freq: float = None
279
- neff: float = None
280
- TEM: bool = None
281
- Z0: float = None
282
- polarity: float = 1
287
+ freq: float = 0
288
+ neff: float = 1
289
+ TEM: bool = True
290
+ Z0: float = 50.0
291
+ polarity: float = 1.0
283
292
  modetype: Literal['TEM','TE','TM'] = 'TEM'
284
293
 
285
294
  def __post_init__(self):
@@ -301,27 +310,30 @@ class FloquetPort(PortBC):
301
310
  def __init__(self,
302
311
  face: FaceSelection | GeoSurface,
303
312
  port_number: int,
304
- cs: CoordinateSystem = None,
313
+ cs: CoordinateSystem | None = None,
305
314
  power: float = 1.0,
306
315
  er: float = 1.0):
307
316
  super().__init__(face)
317
+ if cs is None:
318
+ cs = GCS
308
319
  self.port_number: int= port_number
309
320
  self.active: bool = True
310
321
  self.power: float = power
311
322
  self.type: str = 'TEM'
312
- self._field_amplitude: np.ndarray = None
313
323
  self.mode: tuple[int,int] = (1,0)
314
324
  self.cs: CoordinateSystem = cs
315
325
  self.scan_theta: float = 0
316
326
  self.scan_phi: float = 0
317
- self.pol_s: complex = 1.0
318
- self.pol_p: complex = 0.0
319
- self.Zdir: Axis = -1
327
+ self.pol_s: complex = 1.0 + 0j
328
+ self.pol_p: complex = 0j
320
329
  self.area: float = 1
330
+ self.width: float | None = None
331
+ self.height: float | None = None
332
+
321
333
  if self.cs is None:
322
334
  self.cs = GCS
323
335
 
324
- def portZ0(self, k0: float = None) -> complex:
336
+ def portZ0(self, k0: float | None = None) -> complex | float | None:
325
337
  return 376.73031341259
326
338
 
327
339
  def get_amplitude(self, k0: float) -> float:
@@ -331,7 +343,7 @@ class FloquetPort(PortBC):
331
343
  ''' Return the out of plane propagation constant. βz.'''
332
344
  return k0*np.cos(self.scan_theta)
333
345
 
334
- def get_gamma(self, k0: float):
346
+ def get_gamma(self, k0: float) -> complex:
335
347
  """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
336
348
 
337
349
  Args:
@@ -374,6 +386,8 @@ class FloquetPort(PortBC):
374
386
  k0: float,
375
387
  which: Literal['E','H'] = 'E') -> np.ndarray:
376
388
  '''Compute the port mode field for global xyz coordinates.'''
389
+ if self.cs is None:
390
+ raise ValueError('No coordinate system is defined for this FloquetPort')
377
391
  xl, yl, _ = self.cs.in_local_cs(x_global, y_global, z_global)
378
392
  Ex, Ey, Ez = self.port_mode_3d(xl, yl, k0)
379
393
  Exg, Eyg, Ezg = self.cs.in_global_basis(Ex, Ey, Ez)
@@ -389,7 +403,7 @@ class ModalPort(PortBC):
389
403
  face: FaceSelection | GeoSurface,
390
404
  port_number: int,
391
405
  active: bool = False,
392
- cs: CoordinateSystem = None,
406
+ cs: CoordinateSystem | None = None,
393
407
  power: float = 1,
394
408
  TEM: bool = False,
395
409
  mixed_materials: bool = False):
@@ -418,7 +432,7 @@ class ModalPort(PortBC):
418
432
  self.port_number: int= port_number
419
433
  self.active: bool = active
420
434
  self.power: float = power
421
- self.cs: CoordinateSystem = cs
435
+
422
436
 
423
437
  self.selected_mode: int = 0
424
438
  self.modes: dict[float, list[PortMode]] = defaultdict(list)
@@ -426,17 +440,18 @@ class ModalPort(PortBC):
426
440
  self.TEM: bool = TEM
427
441
  self.mixed_materials: bool = mixed_materials
428
442
  self.initialized: bool = False
429
- self._first_k0: float = None
430
- self._last_k0: float = None
443
+ self._first_k0: float | None = None
444
+ self._last_k0: float | None = None
431
445
 
432
- if self.cs is None:
446
+ if cs is None:
433
447
  logger.info('Constructing coordinate system from normal port')
434
- self.cs = Axis(self.selection.normal).construct_cs()
435
-
436
- self._er: np.ndarray = None
437
- self._ur: np.ndarray = None
448
+ self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
449
+ else:
450
+ raise ValueError('No Coordinate System could be derived.')
451
+ self._er: np.ndarray | None = None
452
+ self._ur: np.ndarray | None = None
438
453
 
439
- def portZ0(self, k0: float) -> complex:
454
+ def portZ0(self, k0: float) -> complex | float | None:
440
455
  return self.get_mode(k0).Z0
441
456
 
442
457
  def modetype(self, k0: float) -> Literal['TEM','TE','TM']:
@@ -444,8 +459,15 @@ class ModalPort(PortBC):
444
459
 
445
460
  @property
446
461
  def nmodes(self) -> int:
462
+ if self._last_k0 is None:
463
+ raise ValueError('ModalPort is not properly configured. No modes are defined.')
447
464
  return len(self.modes[self._last_k0])
448
465
 
466
+ @property
467
+ def voltage(self) -> complex:
468
+ mode = self.get_mode(0)
469
+ return np.sqrt(mode.Z0)
470
+
449
471
  def sort_modes(self) -> None:
450
472
  """Sorts the port modes based on total energy
451
473
  """
@@ -476,7 +498,7 @@ class ModalPort(PortBC):
476
498
 
477
499
  def clear_modes(self) -> None:
478
500
  """Clear all port mode data"""
479
- self.modes: dict[float, list[PortMode]] = defaultdict(list)
501
+ self.modes = defaultdict(list)
480
502
  self.initialized = False
481
503
 
482
504
  def add_mode(self,
@@ -487,7 +509,7 @@ class ModalPort(PortBC):
487
509
  k0: float,
488
510
  residual: float,
489
511
  TEM: bool,
490
- freq: float) -> PortMode:
512
+ freq: float) -> PortMode | None:
491
513
  """Add a mode function to the ModalPort
492
514
 
493
515
  Args:
@@ -523,6 +545,9 @@ class ModalPort(PortBC):
523
545
 
524
546
  def get_basis(self) -> np.ndarray:
525
547
  return self.cs._basis
548
+
549
+ def get_inv_basis(self) -> np.ndarray:
550
+ return self.cs._basis_inv
526
551
 
527
552
  def get_beta(self, k0: float) -> float:
528
553
  mode = self.get_mode(k0)
@@ -533,7 +558,7 @@ class ModalPort(PortBC):
533
558
  beta = np.sqrt(mode.beta**2 + k0**2 * (1-((mode.freq/freq)**2)))
534
559
  return beta
535
560
 
536
- def get_gamma(self, k0: float):
561
+ def get_gamma(self, k0: float) -> complex:
537
562
  return 1j*self.get_beta(k0)
538
563
 
539
564
  def get_Uinc(self, x_local, y_local, k0) -> np.ndarray:
@@ -573,8 +598,8 @@ class RectangularWaveguide(PortBC):
573
598
  face: FaceSelection | GeoSurface,
574
599
  port_number: int,
575
600
  active: bool = False,
576
- cs: CoordinateSystem = None,
577
- dims: tuple[float, float] = None,
601
+ cs: CoordinateSystem | None = None,
602
+ dims: tuple[float, float] | None = None,
578
603
  power: float = 1):
579
604
  """Creates a rectangular waveguide as a port boundary condition.
580
605
 
@@ -593,19 +618,17 @@ class RectangularWaveguide(PortBC):
593
618
  power (float): The port power. Default to 1.
594
619
  """
595
620
  super().__init__(face)
596
-
621
+
597
622
  self.port_number: int= port_number
598
623
  self.active: bool = active
599
624
  self.power: float = power
600
625
  self.type: str = 'TE'
601
- self._field_amplitude: np.ndarray = None
602
626
  self.mode: tuple[int,int] = (1,0)
603
- self.cs: CoordinateSystem = cs
604
627
 
605
628
  if dims is None:
606
629
  logger.info("Determining port face based on selection")
607
- cs, (width, height) = face.rect_basis()
608
- self.cs = cs
630
+ cs, (width, height) = self.selection.rect_basis() # type: ignore
631
+ self.cs = cs # type: ignore
609
632
  self.dims = (width, height)
610
633
  logger.debug(f'Port CS: {self.cs}')
611
634
  logger.debug(f'Detected port {self.port_number} size = {width*1000:.1f} mm x {height*1000:.1f} mm')
@@ -614,9 +637,15 @@ class RectangularWaveguide(PortBC):
614
637
  logger.info('Constructing coordinate system from normal port')
615
638
  self.cs = Axis(self.selection.normal).construct_cs()
616
639
  else:
617
- self.cs: CoordinateSystem = cs
640
+ self.cs: CoordinateSystem = cs # type: ignore
618
641
 
619
- def portZ0(self, k0: float = None) -> complex:
642
+ def get_basis(self) -> np.ndarray:
643
+ return self.cs._basis
644
+
645
+ def get_inv_basis(self) -> np.ndarray:
646
+ return self.cs._basis_inv
647
+
648
+ def portZ0(self, k0: float) -> complex:
620
649
  return k0*299792458 * 4*np.pi*1e-7/self.get_beta(k0)
621
650
 
622
651
  def modetype(self, k0):
@@ -626,20 +655,7 @@ class RectangularWaveguide(PortBC):
626
655
  Zte = 376.73031341259
627
656
  amplitude= np.sqrt(self.power*4*Zte/(self.dims[0]*self.dims[1]))
628
657
  return amplitude
629
-
630
- def port_mode_2d(self, xs: np.ndarray, ys: np.ndarray, k0: float) -> tuple[np.ndarray, float]:
631
- x0 = xs[0]
632
- y0 = ys[0]
633
- x1 = xs[-1]
634
- y1 = ys[-1]
635
- xc = 0.5*(x0+x1)
636
- yc = 0.5*(y0+y1)
637
- a = np.sqrt((x1-x0)**2 + (y1-y0)**2)
638
-
639
- logger.debug(f'Detected port {self.port_number} width = {a*1000:.1f} mm')
640
- ds = np.sqrt((xs-xc)**2 + (ys-yc)**2)
641
- return self.amplitude*np.cos(ds*np.pi/a), np.sqrt(k0**2 - (np.pi/a)**2)
642
-
658
+
643
659
  def get_beta(self, k0: float) -> float:
644
660
  ''' Return the out of plane propagation constant. βz.'''
645
661
  width=self.dims[0]
@@ -647,7 +663,7 @@ class RectangularWaveguide(PortBC):
647
663
  beta = np.sqrt(k0**2 - (np.pi*self.mode[0]/width)**2 - (np.pi*self.mode[1]/height)**2)
648
664
  return beta
649
665
 
650
- def get_gamma(self, k0: float):
666
+ def get_gamma(self, k0: float) -> complex:
651
667
  """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
652
668
 
653
669
  Args:
@@ -699,10 +715,10 @@ class LumpedPort(PortBC):
699
715
  def __init__(self,
700
716
  face: FaceSelection | GeoSurface,
701
717
  port_number: int,
702
- width: float = None,
703
- height: float = None,
704
- direction: Axis = None,
705
- Idirection: Axis = None,
718
+ width: float | None = None,
719
+ height: float | None = None,
720
+ direction: Axis | None = None,
721
+ Idirection: Axis | None = None,
706
722
  active: bool = False,
707
723
  power: float = 1,
708
724
  Z0: float = 50):
@@ -733,26 +749,25 @@ class LumpedPort(PortBC):
733
749
  if width is None or height is None or direction is None:
734
750
  raise ValueError(f'The width, height and direction could not be extracted from {face}')
735
751
 
736
- logger.debug(f'Lumped port: width={1000*width:.1f}mm, height={1000*height:.1f}mm, direction={direction}')
752
+ logger.debug(f'Lumped port: width={1000*width:.1f}mm, height={1000*height:.1f}mm, direction={direction}') # type: ignore
737
753
  self.port_number: int= port_number
738
754
  self.active: bool = active
739
755
 
740
756
  self.power: float = power
741
757
  self.Z0: float = Z0
742
758
 
743
- self._field_amplitude: np.ndarray = None
744
759
  self.width: float = width
745
- self.height: float = height
746
- self.Vdirection: Axis = direction
747
- self.Idirection: Axis = Idirection
760
+ self.height: float = height # type: ignore
761
+ self.Vdirection: Axis = direction # type: ignore
762
+ self.Idirection: Axis = Idirection # type: ignore
748
763
  self.type = 'TEM'
749
764
 
750
765
  logger.info('Constructing coordinate system from normal port')
751
- self.cs = Axis(self.selection.normal).construct_cs()
766
+ self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
752
767
 
753
- self.vintline: Line = None
768
+ self.vintline: Line | None = None
754
769
  self.v_integration = True
755
- self.iintline: Line = None
770
+ self.iintline: Line | None = None
756
771
 
757
772
  @property
758
773
  def surfZ(self) -> float:
@@ -774,6 +789,9 @@ class LumpedPort(PortBC):
774
789
 
775
790
  def get_basis(self) -> np.ndarray:
776
791
  return self.cs._basis
792
+
793
+ def get_inv_basis(self) -> np.ndarray:
794
+ return self.cs._basis_inv
777
795
 
778
796
  def get_beta(self, k0: float) -> float:
779
797
  ''' Return the out of plane propagation constant. βz.'''
@@ -845,9 +863,9 @@ class LumpedElement(RobinBC):
845
863
 
846
864
  def __init__(self,
847
865
  face: FaceSelection | GeoSurface,
848
- impedance_function: Callable = None,
849
- width: float = None,
850
- height: float = None,
866
+ impedance_function: Callable | None = None,
867
+ width: float | None = None,
868
+ height: float | None = None,
851
869
  ):
852
870
  """Generates a lumped power boundary condition.
853
871
 
@@ -876,20 +894,18 @@ class LumpedElement(RobinBC):
876
894
  if width is None or height is None or impedance_function is None:
877
895
  raise ValueError(f'The width, height and impedance function could not be extracted from {face}')
878
896
 
879
- logger.debug(f'Lumped port: width={1000*width:.1f}mm, height={1000*height:.1f}mm')
897
+ logger.debug(f'Lumped port: width={1000*width:.1f}mm, height={1000*height:.1f}mm') # type: ignore
880
898
 
881
- self.Z0: Callable = impedance_function
882
-
883
- self._field_amplitude: np.ndarray = None
884
- self.width: float = width
885
- self.height: float = height
899
+ self.Z0: Callable = impedance_function # type: ignore
900
+ self.width: float = width # type: ignore
901
+ self.height: float = height # type: ignore
886
902
 
887
903
  logger.info('Constructing coordinate system from normal port')
888
- self.cs = Axis(self.selection.normal).construct_cs()
904
+ self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
889
905
 
890
- self.vintline: Line = None
906
+ self.vintline: Line | None = None
891
907
  self.v_integration = True
892
- self.iintline: Line = None
908
+ self.iintline: Line | None = None
893
909
 
894
910
  def surfZ(self, k0: float) -> float:
895
911
  """The surface sheet impedance for the lumped port
@@ -903,6 +919,9 @@ class LumpedElement(RobinBC):
903
919
 
904
920
  def get_basis(self) -> np.ndarray:
905
921
  return self.cs._basis
922
+
923
+ def get_inv_basis(self) -> np.ndarray:
924
+ return self.cs._basis_inv
906
925
 
907
926
  def get_beta(self, k0: float) -> float:
908
927
  ''' Return the out of plane propagation constant. βz.'''
@@ -20,7 +20,7 @@ from ...simulation_data import BaseDataset, DataContainer
20
20
  from ...elements.femdata import FEMBasis
21
21
  from dataclasses import dataclass
22
22
  import numpy as np
23
- from typing import Literal, Any
23
+ from typing import Literal
24
24
  from loguru import logger
25
25
  from .adaptive_freq import SparamModel
26
26
  from ...cs import Axis, _parse_axis
@@ -128,7 +128,7 @@ def generate_ndim(
128
128
  outer_data: dict[str, list[float]],
129
129
  inner_data: list[float],
130
130
  outer_labels: tuple[str, ...]
131
- ) -> np.ndarray:
131
+ ) -> tuple[np.ndarray,...]:
132
132
  """
133
133
  Generates an N-dimensional grid of values from flattened data, and returns each axis array plus the grid.
134
134
 
@@ -231,10 +231,10 @@ class Sparam:
231
231
 
232
232
  @dataclass
233
233
  class PortProperties:
234
- port_number: int | None = None
234
+ port_number: int = -1
235
235
  k0: float | None= None
236
236
  beta: float | None = None
237
- Z0: float | None = None
237
+ Z0: float | complex | None = None
238
238
  Pout: float | None = None
239
239
  mode_number: int = 1
240
240
 
@@ -259,7 +259,7 @@ class FarfieldData:
259
259
 
260
260
  def surfplot(self,
261
261
  polarization: Literal['Ex','Ey','Ez','Etheta','Ephi','normE'],
262
- isotropic: bool = True, dB: bool = False, dBfloor: float = -30, rmax: float = None,
262
+ isotropic: bool = True, dB: bool = False, dBfloor: float = -30, rmax: float | None = None,
263
263
  offset: tuple[float, float, float] = (0,0,0)) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
264
264
  """Returns the parameters to be used as positional arguments for the display.add_surf() function.
265
265
 
@@ -345,7 +345,7 @@ class EHField:
345
345
 
346
346
  @property
347
347
  def Dy(self) -> np.ndarray:
348
- return self.Et*self.er
348
+ return self.Ey*self.er
349
349
 
350
350
  @property
351
351
  def Dz(self) -> np.ndarray:
@@ -499,16 +499,16 @@ class EHField:
499
499
  Returns:
500
500
  (X,Y,Z,Field): The coordinates plus field scalar
501
501
  """
502
- field = getattr(self, field)
502
+ field_arry = getattr(self, field)
503
503
  if metric=='abs':
504
- field = np.abs(field)
504
+ field = np.abs(field_arry)
505
505
  elif metric=='real':
506
- field = field.real
506
+ field = field_arry.real
507
507
  elif metric=='imag':
508
- field = field.imag
508
+ field = field_arry.imag
509
509
  elif metric=='complex':
510
- field = field
511
- return self.x, self.y, self.z, field
510
+ field = field_arry
511
+ return self.x, self.y, self.z, field_arry
512
512
 
513
513
  class _EHSign:
514
514
  """A small class to manage the sign of field components when computing the far-field with Stratton-Chu
@@ -588,7 +588,7 @@ class MWField:
588
588
  mode_number: int,
589
589
  k0: float,
590
590
  beta: float,
591
- Z0: float,
591
+ Z0: float | complex | None,
592
592
  Pout: float) -> None:
593
593
  self.port_modes.append(PortProperties(port_number=port_number,
594
594
  mode_number=mode_number,
@@ -609,7 +609,7 @@ class MWField:
609
609
  def _field(self) -> np.ndarray:
610
610
  if self._mode_field is not None:
611
611
  return self._mode_field
612
- return sum([self.excitation[mode.port_number]*self._fields[mode.port_number] for mode in self.port_modes])
612
+ return sum([self.excitation[mode.port_number]*self._fields[mode.port_number] for mode in self.port_modes]) # type: ignore
613
613
 
614
614
  def set_field_vector(self) -> None:
615
615
  """Defines the default excitation coefficients for the current dataset"""
@@ -678,9 +678,9 @@ class MWField:
678
678
 
679
679
  def cutplane(self,
680
680
  ds: float,
681
- x: float=None,
682
- y: float=None,
683
- z: float=None) -> EHField:
681
+ x: float | None = None,
682
+ y: float | None = None,
683
+ z: float | None = None) -> EHField:
684
684
  xb, yb, zb = self.basis.bounds
685
685
  xs = np.linspace(xb[0], xb[1], int((xb[1]-xb[0])/ds))
686
686
  ys = np.linspace(yb[0], yb[1], int((yb[1]-yb[0])/ds))
@@ -765,8 +765,8 @@ class MWField:
765
765
  faces: FaceSelection | GeoSurface,
766
766
  ang_range: tuple[float, float] = (-180, 180),
767
767
  Npoints: int = 201,
768
- origin: tuple[float, float, float] = None,
769
- syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
768
+ origin: tuple[float, float, float] | None = None,
769
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
770
770
  """Compute the farfield electric and magnetic field defined by a circle.
771
771
 
772
772
  Args:
@@ -782,18 +782,18 @@ class MWField:
782
782
  tuple[np.ndarray, np.ndarray, np.ndarray]: _description_
783
783
  """
784
784
  refdir = _parse_axis(ref_direction).np
785
- plane_normal = _parse_axis(plane_normal).np
786
- theta, phi = arc_on_plane(refdir, plane_normal, ang_range, Npoints)
785
+ plane_normal_parsed = _parse_axis(plane_normal).np
786
+ theta, phi = arc_on_plane(refdir, plane_normal_parsed, ang_range, Npoints)
787
787
  E,H = self.farfield(theta, phi, faces, origin, syms = syms)
788
788
  angs = np.linspace(*ang_range, Npoints)*np.pi/180
789
789
  return angs, E ,H
790
790
 
791
791
  def farfield_3d(self,
792
792
  faces: FaceSelection | GeoSurface,
793
- thetas: np.ndarray = None,
794
- phis: np.ndarray = None,
795
- origin: tuple[float, float, float] = None,
796
- syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] = None) -> FarfieldData:
793
+ thetas: np.ndarray | None = None,
794
+ phis: np.ndarray | None = None,
795
+ origin: tuple[float, float, float] | None = None,
796
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> FarfieldData:
797
797
  """Compute the farfield in a 3D angular grid
798
798
 
799
799
  If thetas and phis are not provided, they default to a sample space of 2 degrees.
@@ -823,8 +823,8 @@ class MWField:
823
823
  def farfield(self, theta: np.ndarray,
824
824
  phi: np.ndarray,
825
825
  faces: FaceSelection | GeoSurface,
826
- origin: tuple[float, float, float] = None,
827
- syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] = None) -> tuple[np.ndarray, np.ndarray]:
826
+ origin: tuple[float, float, float] | None = None,
827
+ syms: list[Literal['Ex','Ey','Ez', 'Hx','Hy','Hz']] | None = None) -> tuple[np.ndarray, np.ndarray]:
828
828
  """Compute the farfield at the provided theta/phi coordinates
829
829
 
830
830
  Args:
@@ -874,7 +874,7 @@ class MWField:
874
874
 
875
875
  return Eff, Hff
876
876
 
877
- def optycal(self, faces: FaceSelection | GeoSurface = None) -> tuple:
877
+ def optycal(self, faces: FaceSelection | GeoSurface | None = None) -> tuple:
878
878
  """Export this models exterior to an Optical acceptable dataset
879
879
 
880
880
  Args:
@@ -953,7 +953,7 @@ class MWScalar:
953
953
  mode_number: int,
954
954
  k0: float,
955
955
  beta: float,
956
- Z0: float,
956
+ Z0: float | complex,
957
957
  Pout: float) -> None:
958
958
  i = self._portmap[port_number]
959
959
  self.beta[i] = beta
@@ -1026,9 +1026,9 @@ class MWScalarNdim:
1026
1026
 
1027
1027
  def export_touchstone(self,
1028
1028
  filename: str,
1029
- Z0ref: float = None,
1029
+ Z0ref: float | None = None,
1030
1030
  format: Literal['RI','MA','DB'] = 'RI',
1031
- custom_comments: list[str] = None,
1031
+ custom_comments: list[str] | None = None,
1032
1032
  funit: Literal['HZ','KHZ','MHZ','GHZ'] = 'GHZ'):
1033
1033
  """Export the S-parameter data to a touchstone file
1034
1034
 
@@ -1063,9 +1063,9 @@ class MWScalarNdim:
1063
1063
  filename: str,
1064
1064
  Smatrix: np.ndarray,
1065
1065
  frequencies: np.ndarray,
1066
- Z0ref: float = None,
1066
+ Z0ref: float | None = None,
1067
1067
  format: Literal['RI','MA','DB'] = 'RI',
1068
- custom_comments: list[str] = None,
1068
+ custom_comments: list[str] | None = None,
1069
1069
  funit: Literal['HZ','KHZ','MHZ','GHZ'] = 'GHZ') -> None:
1070
1070
  """Save an S-parameter matrix to a touchstone file.
1071
1071