emerge 1.0.6__py3-none-any.whl → 1.1.0__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.

@@ -26,6 +26,9 @@ from ...solver import DEFAULT_ROUTINE, SolveRoutine
26
26
  from ...system import called_from_main_function
27
27
  from ...selection import FaceSelection
28
28
  from ...settings import Settings
29
+ from ...simstate import SimState
30
+ from ...logsettings import DEBUG_COLLECTOR
31
+
29
32
  from .microwave_bc import MWBoundaryConditionSet, PEC, ModalPort, LumpedPort, PortBC
30
33
  from .microwave_data import MWData
31
34
  from .assembly.assembler import Assembler
@@ -34,10 +37,10 @@ from .simjob import SimJob
34
37
 
35
38
  from concurrent.futures import ThreadPoolExecutor
36
39
  from loguru import logger
37
- from typing import Callable, Literal
40
+ from typing import Callable, Literal, Any
38
41
  import multiprocessing as mp
39
42
  from cmath import sqrt as csqrt
40
-
43
+ from itertools import product
41
44
  import numpy as np
42
45
  import threading
43
46
  import time
@@ -54,16 +57,14 @@ def run_job_multi(job: SimJob) -> SimJob:
54
57
  Returns:
55
58
  SimJob: The solved SimJob
56
59
  """
57
- routine = DEFAULT_ROUTINE._configure_routine('MP')
60
+ nr = int(mp.current_process().name.split('-')[1])
61
+ routine = DEFAULT_ROUTINE._configure_routine('MP', proc_nr=nr)
58
62
  for A, b, ids, reuse, aux in job.iter_Ab():
59
63
  solution, report = routine.solve(A, b, ids, reuse, id=job.id)
60
64
  report.add(**aux)
61
65
  job.submit_solution(solution, report)
62
66
  return job
63
67
 
64
- def _init_worker():
65
- nr = int(mp.current_process().name.split('-')[1])
66
- DEFAULT_ROUTINE._configure_routine(proc_nr=nr)
67
68
 
68
69
  def _dimstring(data: list[float] | np.ndarray) -> str:
69
70
  """A String formatter for dimensions in millimeters
@@ -111,14 +112,17 @@ class Microwave3D:
111
112
  formulation.
112
113
 
113
114
  """
114
- def __init__(self, mesher: Mesher, settings: Settings, mwdata: MWData, order: int = 2):
115
+ def __init__(self, state: SimState, mesher: Mesher, settings: Settings, order: int = 2):
116
+
117
+ self._settings: Settings = settings
118
+
115
119
  self.frequencies: list[float] = []
116
120
  self.current_frequency = 0
117
121
  self.order: int = order
118
- self.resolution: float = 1
119
- self._settings: Settings = settings
122
+ self.resolution: float = 0.33
123
+
120
124
  self.mesher: Mesher = mesher
121
- self.mesh: Mesh3D = Mesh3D(self.mesher)
125
+ self._state: SimState = state
122
126
 
123
127
  self.assembler: Assembler = Assembler(self._settings)
124
128
  self.bc: MWBoundaryConditionSet = MWBoundaryConditionSet(None)
@@ -128,38 +132,33 @@ class Microwave3D:
128
132
 
129
133
  ## States
130
134
  self._bc_initialized: bool = False
131
- self.data: MWData = mwdata
132
-
133
- ## Data
134
- self._params: dict[str, float] = dict()
135
135
  self._simstart: float = 0.0
136
136
  self._simend: float = 0.0
137
+
138
+ self._container: dict[str, Any] = dict()
137
139
 
138
- self.set_order(order)
139
-
140
- def reset_data(self):
141
- self.data = MWData()
142
-
143
- def reset(self):
144
- self.bc.reset()
140
+ @property
141
+ def _params(self) -> dict[str, float]:
142
+ return self._state.params
143
+
144
+ @property
145
+ def mesh(self) -> Mesh3D:
146
+ return self._state.mesh
147
+
148
+ @property
149
+ def data(self) -> MWData:
150
+ return self._state.data.mw
151
+
152
+ def reset(self, _reset_bc: bool = True):
153
+ if _reset_bc:
154
+ self.bc = MWBoundaryConditionSet(None)
155
+ else:
156
+ for bc in self.bc.oftype(ModalPort):
157
+ bc.reset()
158
+
145
159
  self.basis: FEMBasis = None
146
- self.bc = MWBoundaryConditionSet(None)
147
160
  self.solveroutine.reset()
148
-
149
- def set_order(self, order: int) -> None:
150
- """Sets the order of the basis functions used. Currently only supports second order.
151
-
152
- Args:
153
- order (int): The order to use.
154
-
155
- Raises:
156
- ValueError: An error if a wrong order is used.
157
- """
158
- if order not in (2,):
159
- raise ValueError(f'Order {order} not supported. Only order-2 allowed.')
160
-
161
- self.order = order
162
- self.resolution = {1: 0.15, 2: 0.3}[order]
161
+ self.assembler.cached_matrices = None
163
162
 
164
163
  @property
165
164
  def nports(self) -> int:
@@ -217,7 +216,15 @@ class Microwave3D:
217
216
  self.frequencies = list(frequency)
218
217
  else:
219
218
  self.frequencies = [frequency]
220
-
219
+
220
+ # Safety tests
221
+ if len(self.frequencies) > 200:
222
+ DEBUG_COLLECTOR.add_report(f'More than 200 frequency points are detected ({len(frequency)}). This may cause slow simulations. Consider using Vector Fitting to subsample S-parameters.')
223
+ if min(self.frequencies) < 1e6:
224
+ DEBUG_COLLECTOR.add_report(f'A frequency smaller than 1MHz has been detected ({min(frequency)} Hz). Perhaps you forgot to include usints like 1e6 for MHz etc.')
225
+ if max(self.frequencies) > 1e12:
226
+ DEBUG_COLLECTOR.add_report(f'A frequency greater than THz has been detected ({min(frequency)} Hz). Perhaps you double counted frequency units like twice 1e6 for MHz etc.')
227
+
221
228
  self.mesher.max_size = self.resolution * 299792458 / max(self.frequencies)
222
229
  self.mesher.min_size = 0.1 * self.mesher.max_size
223
230
 
@@ -359,29 +366,6 @@ class Microwave3D:
359
366
  logger.trace(f' - Port[{port.port_number}] integration line {start} -> {end}.')
360
367
 
361
368
  port.v_integration = True
362
-
363
- def _compute_integration_line(self, group1: list[int], group2: list[int]) -> tuple[np.ndarray, np.ndarray]:
364
- """Computes an integration line for two node island groups by finding the closest two nodes.
365
-
366
- This method is used for the modal TEM analysis to find an appropriate voltage integration path
367
- by looking for the two closest points for the two conductor islands that where discovered.
368
-
369
- Currently it defaults to 11 integration line points.
370
-
371
- Args:
372
- group1 (list[int]): The first island node group
373
- group2 (list[int]): The second island node group
374
-
375
- Returns:
376
- centers (np.ndarray): The center points of the line segments
377
- dls (np.ndarray): The delta-path vectors for each line segment.
378
- """
379
- nodes1 = self.mesh.nodes[:,group1]
380
- nodes2 = self.mesh.nodes[:,group2]
381
- path = shortest_path(nodes1, nodes2, 21)
382
- centres = (path[:,1:] + path[:,:-1])/2
383
- dls = path[:,1:] - path[:,:-1]
384
- return centres, dls
385
369
 
386
370
  def _find_tem_conductors(self, port: ModalPort, sigtri: np.ndarray) -> tuple[list[int], list[int]]:
387
371
  ''' Returns two lists of global node indices corresponding to the TEM port conductors.
@@ -401,8 +385,10 @@ class Microwave3D:
401
385
  raise ValueError('The field basis is not yet defined.')
402
386
 
403
387
  logger.debug(' - Finding PEC TEM conductors')
404
- pecs: list[PEC] = self.bc.get_conductors() # type: ignore
405
388
  mesh = self.mesh
389
+
390
+ # Find all BC conductors
391
+ pecs: list[PEC] = self.bc.get_conductors() # type: ignore
406
392
 
407
393
  # Process all PEC Boundary Conditions
408
394
  pec_edges = []
@@ -418,19 +404,38 @@ class Microwave3D:
418
404
  edge_ids = list(mesh.tri_to_edge[:,itri].flatten())
419
405
  pec_edges.extend(edge_ids)
420
406
 
407
+ # All PEC edges
421
408
  pec_edges = list(set(pec_edges))
422
409
 
410
+ # Port mesh data
423
411
  tri_ids = mesh.get_triangles(port.tags)
424
412
  edge_ids = list(mesh.tri_to_edge[:,tri_ids].flatten())
425
413
 
426
- pec_port = np.array([i for i in pec_edges if i in set(edge_ids)])
414
+ port_pec_edges = np.array([i for i in pec_edges if i in set(edge_ids)])
427
415
 
428
- pec_islands = mesh.find_edge_groups(pec_port)
416
+ pec_islands = mesh.find_edge_groups(port_pec_edges)
429
417
 
418
+
430
419
  logger.debug(f' - Found {len(pec_islands)} PEC islands.')
431
420
 
432
421
  if len(pec_islands) != 2:
433
- raise ValueError(f' - Found {len(pec_islands)} PEC islands. Expected 2.')
422
+ pec_island_tags = {i: self.mesh._get_dimtags(edges=pec_edge_group) for i,pec_edge_group in enumerate(pec_islands)}
423
+ plus_term = None
424
+ min_term = None
425
+
426
+ for i, dimtags in pec_island_tags.items():
427
+ if not set(dimtags).isdisjoint(port.plus_terminal):
428
+ plus_term = i
429
+
430
+ if not set(dimtags).isdisjoint(port.minus_terminal):
431
+ min_term = i
432
+
433
+ if plus_term is None or min_term is None:
434
+ logger.error(f' - Found {len(pec_islands)} PEC islands without a terminal definition. Please use .set_terminals() to define which conductors are which polarity, or define the integration line manually.')
435
+ return None, None
436
+ logger.debug(f'Positive island = {pec_island_tags[plus_term]}')
437
+ logger.debug(f'Negative island = {pec_island_tags[min_term]}')
438
+ pec_islands = [pec_islands[plus_term], pec_islands[min_term]]
434
439
 
435
440
  groups = []
436
441
  for island in pec_islands:
@@ -457,7 +462,11 @@ class Microwave3D:
457
462
  if not bc.mixed_materials and bc.initialized:
458
463
  continue
459
464
 
460
- self.modal_analysis(bc, 1, False, bc.TEM, freq=freq)
465
+ if bc.forced_modetype=='TEM':
466
+ TEM = True
467
+ else:
468
+ TEM = False
469
+ self.modal_analysis(bc, 1, direct=False, freq=freq, TEM=TEM)
461
470
 
462
471
  def modal_analysis(self,
463
472
  port: ModalPort,
@@ -551,7 +560,7 @@ class Microwave3D:
551
560
  target_kz = k0*target_neff
552
561
 
553
562
  if target_kz is None:
554
- if TEM:
563
+ if TEM or port.forced_modetype=='TEM':
555
564
  target_kz = ermean*urmean*1.1*k0
556
565
  else:
557
566
 
@@ -573,19 +582,28 @@ class Microwave3D:
573
582
  Emode = np.zeros((nlf.n_field,), dtype=np.complex128)
574
583
  eigenmode = eigen_modes[:,i]
575
584
  Emode[solve_ids] = np.squeeze(eigenmode)
576
- Emode = Emode * np.exp(-1j*np.angle(np.max(Emode)))
585
+ Emode = Emode * np.exp(-1j*np.angle(Emode[np.argmax(np.abs(Emode))]))
577
586
 
578
587
  beta_base = np.emath.sqrt(-eigen_values[i])
579
588
  beta = min(k0*np.sqrt(ermax*urmax), beta_base)
580
589
 
581
590
  residuals = -1
582
591
 
592
+ if port._get_alignment_vector(i) is not None:
593
+ vec = port._get_alignment_vector(i)
594
+ xyz_centers = self.mesh.tri_centers[:,self.mesh.get_triangles(port.tags)]
595
+ E_centers = np.mean(nlf.interpolate_Ef(Emode)(xyz_centers[0,:], xyz_centers[1,:], xyz_centers[2,:]), axis=1)
596
+ EdotVec = vec[0]*E_centers[0] + vec[1]*E_centers[1] + vec[2]*E_centers[2]
597
+ if EdotVec.real < 0:
598
+ logger.debug(f'Mode polarization along alignment axis {vec} = {EdotVec.real:.3f}, inverting.')
599
+ Emode = -Emode
600
+
583
601
  portfE = nlf.interpolate_Ef(Emode)
584
602
  portfH = nlf.interpolate_Hf(Emode, k0, ur, beta)
585
-
586
603
  P = compute_avg_power_flux(nlf, Emode, k0, ur, beta)
587
604
 
588
- mode = port.add_mode(Emode, portfE, portfH, beta, k0, residuals, TEM=TEM, freq=freq)
605
+ mode = port.add_mode(Emode, portfE, portfH, beta, k0, residuals, number=i, freq=freq)
606
+
589
607
  if mode is None:
590
608
  continue
591
609
 
@@ -594,20 +612,40 @@ class Microwave3D:
594
612
  Ez = np.max(np.abs(Efz))
595
613
  Exy = np.max(np.abs(Efxy))
596
614
 
597
- if Ez/Exy < 1e-1 and not TEM:
615
+ if port.forced_modetype == 'TEM' or TEM:
616
+ mode.modetype = 'TEM'
617
+
618
+ if len(port.vintline)>0:
619
+ line = port.vintline[0]
620
+ else:
621
+ G1, G2 = self._find_tem_conductors(port, sigtri=cond)
622
+ if G1 is None or G2 is None:
623
+ logger.warning('Skipping characteristic impedance calculation.')
624
+ continue
625
+
626
+ nodes1 = self.mesh.nodes[:,G1]
627
+ nodes2 = self.mesh.nodes[:,G2]
628
+ path = shortest_path(nodes1, nodes2, 2)
629
+ line = Line.from_points(path[:,0], path[:,1], 21)
630
+ port.vintline.append(line)
631
+
632
+ cs = np.array(line.cmid)
633
+
634
+ logger.debug(f'Integrating portmode from {cs[:,0]} to {cs[:,-1]}')
635
+ voltage = line.line_integral(portfE)
636
+ # Align mode polarity to positive voltage
637
+ if voltage < 0:
638
+ mode.polarity = mode.polarity * -1
639
+
640
+ mode.Z0 = abs(voltage**2/(2*P))
641
+ logger.debug(f'Port Z0 = {mode.Z0}')
642
+ elif Ez/Exy < 1e-1 or port.forced_modetype=='TE':
598
643
  logger.debug('Low Ez/Et ratio detected, assuming TE mode')
599
644
  mode.modetype = 'TE'
600
- elif Ez/Exy > 1e-1 and not TEM:
645
+ elif Ez/Exy > 1e-1 or port.forced_modetype=='TM':
601
646
  logger.debug('High Ez/Et ratio detected, assuming TM mode')
602
647
  mode.modetype = 'TM'
603
- elif TEM:
604
- G1, G2 = self._find_tem_conductors(port, sigtri=cond)
605
- cs, dls = self._compute_integration_line(G1,G2)
606
- mode.modetype = 'TEM'
607
- Ex, Ey, Ez = portfE(cs[0,:], cs[1,:], cs[2,:])
608
- voltage = np.sum(Ex*dls[0,:] + Ey*dls[1,:] + Ez*dls[2,:])
609
- mode.Z0 = abs(voltage**2/(2*P))
610
- logger.debug(f'Port Z0 = {mode.Z0}')
648
+
611
649
 
612
650
  mode.set_power(P*port._qmode(k0)**2)
613
651
 
@@ -787,7 +825,7 @@ class Microwave3D:
787
825
  "if __name__ == '__main__' guard in the top-level script."
788
826
  )
789
827
  # Start parallel pool
790
- with mp.Pool(processes=n_workers, initializer=_init_worker) as pool:
828
+ with mp.Pool(processes=n_workers) as pool:
791
829
  for i_group, fgroup in enumerate(freq_groups):
792
830
  logger.debug(f'Precomputing group {i_group}.')
793
831
  jobs = []
@@ -835,6 +873,100 @@ class Microwave3D:
835
873
  self._post_process(results, matset)
836
874
  return self.data
837
875
 
876
+ def _run_adaptive_mesh(self,
877
+ iteration: int,
878
+ frequency: float,
879
+ automatic_modal_analysis: bool = True) -> MWData:
880
+ """Executes a frequency domain study
881
+
882
+ The study is distributed over "n_workers" workers.
883
+ As optional parameter you may set a harddisc_threshold as integer. This determines the maximum
884
+ number of degrees of freedom before which the jobs will be cahced to the harddisk. The
885
+ path that will be used to cache the sparse matrices can be specified.
886
+ Additionally the term frequency_groups may be specified. This number will define in how
887
+ many groups the matrices will be pre-computed before they are send to workers. This can minimize
888
+ the total amound of RAM memory used. For example with 11 frequencies in gruops of 4, the following
889
+ frequency indices will be precomputed and then solved: [[1,2,3,4],[5,6,7,8],[9,10,11]]
890
+
891
+ Args:
892
+ iteration (int): The iteration number
893
+ frequency (float): The simulation frequency
894
+
895
+ Raises:
896
+ SimulationError: An error associated witha a problem during the simulation.
897
+
898
+ Returns:
899
+ MWSimData: The dataset.
900
+ """
901
+
902
+ self._simstart = time.time()
903
+ if self.bc._initialized is False:
904
+ raise SimulationError('Cannot run a modal analysis because no boundary conditions have been assigned.')
905
+
906
+ self._initialize_field()
907
+ self._initialize_bc_data()
908
+ self._check_physics()
909
+
910
+ if self.basis is None:
911
+ raise SimulationError('Cannot proceed, the simulation basis class is undefined.')
912
+
913
+ materials = self.mesh._get_material_assignment(self.mesher.volumes)
914
+
915
+ ### Does this move
916
+ logger.debug('Initializing single frequency settings.')
917
+
918
+ #### Port settings
919
+ all_ports = self.bc.oftype(PortBC)
920
+
921
+ ##### FOR PORT SWEEP SET ALL ACTIVE TO FALSE. THIS SHOULD BE FIXED LATER
922
+ ### COMPUTE WHICH TETS ARE CONNECTED TO PORT INDICES
923
+
924
+ for port in all_ports:
925
+ port.active=False
926
+
927
+
928
+ def run_job_single(job: SimJob):
929
+ for A, b, ids, reuse, aux in job.iter_Ab():
930
+ solution, report = self.solveroutine.solve(A, b, ids, reuse, id=job.id)
931
+ report.add(**aux)
932
+ job.submit_solution(solution, report)
933
+ return job
934
+
935
+
936
+ matset: list[tuple[np.ndarray, np.ndarray, np.ndarray]] = []
937
+
938
+ self._compute_modes(frequency)
939
+
940
+ logger.debug(f'Simulation frequency = {frequency/1e9:.3f} GHz')
941
+
942
+ if automatic_modal_analysis:
943
+ self._compute_modes(frequency)
944
+
945
+ job, mats = self.assembler.assemble_freq_matrix(self.basis, materials,
946
+ self.bc.boundary_conditions,
947
+ frequency,
948
+ cache_matrices=self.cache_matrices)
949
+
950
+ job.id = 0
951
+
952
+ logger.info('Starting solve')
953
+ job = run_job_single(job)
954
+
955
+
956
+ logger.info('Solving complete')
957
+
958
+ self.data.setreport(job.reports, freq=frequency, **self._params)
959
+
960
+ for variables, data in self.data.sim.iterate():
961
+ logger.trace(f'Sim variable: {variables}')
962
+ for item in data['report']:
963
+ item.logprint(logger.trace)
964
+
965
+ self.solveroutine.reset()
966
+ ### Compute S-parameters and return
967
+ self._post_process([job,], [mats,])
968
+ return self.data
969
+
838
970
  def eigenmode(self, search_frequency: float,
839
971
  nmodes: int = 6,
840
972
  k0_limit: float = 1,
@@ -868,10 +1000,6 @@ class Microwave3D:
868
1000
 
869
1001
  materials = self.mesh._get_material_assignment(self.mesher.volumes)
870
1002
 
871
- # er = self.mesh.retreive(lambda mat,x,y,z: mat.fer3d_mat(x,y,z), self.mesher.volumes)
872
- # ur = self.mesh.retreive(lambda mat,x,y,z: mat.fur3d_mat(x,y,z), self.mesher.volumes)
873
- # cond = self.mesh.retreive(lambda mat,x,y,z: mat.cond, self.mesher.volumes)[0,0,:]
874
-
875
1003
  ### Does this move
876
1004
  logger.debug('Initializing frequency domain sweep.')
877
1005
 
@@ -932,14 +1060,21 @@ class Microwave3D:
932
1060
  """
933
1061
  if self.basis is None:
934
1062
  raise SimulationError('Cannot post-process. Simulation basis function is undefined.')
1063
+
935
1064
  mesh = self.mesh
936
1065
  all_ports = self.bc.oftype(PortBC)
937
1066
  port_numbers = [port.port_number for port in all_ports]
938
1067
 
939
1068
  logger.info('Computing S-parameters')
940
1069
 
941
-
942
- for freq, job, mats in zip(self.frequencies, results, materials):
1070
+ not_conserved = False
1071
+
1072
+ single_corr = self._settings.mw_cap_sp_single
1073
+ col_corr = self._settings.mw_cap_sp_col
1074
+ recip_corr = self._settings.mw_recip_sp
1075
+
1076
+ for job, mats in zip(results, materials):
1077
+ freq = job.freq
943
1078
  er, ur, cond = mats
944
1079
  ertri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
945
1080
  urtri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
@@ -965,6 +1100,7 @@ class Microwave3D:
965
1100
 
966
1101
  logger.info(f'Post Processing simulation frequency = {freq/1e9:.3f} GHz')
967
1102
 
1103
+
968
1104
  # Recording port information
969
1105
  for active_port in all_ports:
970
1106
  port_tets = self.mesh.get_face_tets(active_port.tags)
@@ -1010,10 +1146,36 @@ class Microwave3D:
1010
1146
  pfield, pmode = self._compute_s_data(bc, fieldf,tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
1011
1147
  logger.debug(f'[{bc.port_number}] Passive amplitude = {np.abs(pfield):.3f}')
1012
1148
  scalardata.write_S(bc.port_number, active_port.port_number, pfield/Pout)
1149
+ if abs(pfield/Pout) > 1.0:
1150
+ logger.warning(f'S-parameter > 1.0 detected: {np.abs(pfield/Pout)}')
1151
+ not_conserved = True
1013
1152
  active_port.active=False
1014
1153
 
1154
+
1015
1155
  fielddata.set_field_vector()
1156
+
1157
+ N = scalardata.Sp.shape[1]
1158
+
1159
+ # Enforce reciprocity
1160
+ if recip_corr:
1161
+ scalardata.Sp = (scalardata.Sp + scalardata.Sp.T)/2
1162
+
1163
+ # Enforce energy conservation
1164
+ if col_corr:
1165
+ for j in range(N):
1166
+ scalardata.Sp[:,j] = scalardata.Sp[:,j] / max(1.0, np.sum(np.abs(scalardata.Sp[:,j])**2))
1167
+
1168
+ # Enforce S-parameter limit to 1.0
1169
+ if single_corr:
1170
+ for i,j in product(range(N), range(N)):
1171
+ scalardata.Sp[i,j] = scalardata.Sp[i,j] / max(1.0, np.abs(scalardata.Sp[i,j]))
1172
+
1173
+
1016
1174
 
1175
+ if not_conserved:
1176
+ DEBUG_COLLECTOR.add_report('S-parameters with an amplitude greater than 1.0 detected. This could be due to a ModalPort with the wrong mode type.\n' +
1177
+ 'Specify the type of mode (TE/TM/TEM) in the constructor using ModalPort(..., modetype=\'TE\') for example.\n' +
1178
+ f'Values slightly greater than 1 are possible due to numerical accuracy. Automatic normalization = {single_corr or col_corr}')
1017
1179
  logger.info('Simulation Complete!')
1018
1180
  self._simend = time.time()
1019
1181
  logger.info(f'Elapsed time = {(self._simend-self._simstart):.2f} seconds.')
@@ -1039,6 +1201,7 @@ class Microwave3D:
1039
1201
  tuple[complex, complex]: _description_
1040
1202
  """
1041
1203
  from .sparam import sparam_field_power, sparam_mode_power
1204
+
1042
1205
  if bc.v_integration:
1043
1206
  if bc.vintline is None:
1044
1207
  raise SimulationError('Trying to compute characteristic impedance but no integration line is defined.')
@@ -1064,7 +1227,7 @@ class Microwave3D:
1064
1227
  else:
1065
1228
  if bc.modetype(k0) == 'TEM':
1066
1229
  const = 1/(np.sqrt((urp[0,0,:] + urp[1,1,:] + urp[2,2,:])/(erp[0,0,:] + erp[1,1,:] + erp[2,2,:])))
1067
- if bc.modetype(k0) == 'TE':
1230
+ elif bc.modetype(k0) == 'TE':
1068
1231
  const = 1/((urp[0,0,:] + urp[1,1,:] + urp[2,2,:])/3)
1069
1232
  elif bc.modetype(k0) == 'TM':
1070
1233
  const = 1/((erp[0,0,:] + erp[1,1,:] + erp[2,2,:])/3)