emerge 0.5.6__py3-none-any.whl → 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of emerge might be problematic. Click here for more details.

emerge/__init__.py CHANGED
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "0.5.6"
21
+ __version__ = "0.6.1"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -44,7 +44,7 @@ from loguru import logger
44
44
  LOG_CONTROLLER.set_default()
45
45
  logger.debug('Importing modules')
46
46
 
47
- from ._emerge.simmodel import Simulation3D
47
+ from ._emerge.simmodel import Simulation
48
48
  from ._emerge.material import Material
49
49
  from ._emerge import bc
50
50
  from ._emerge.solver import SolverBicgstab, SolverGMRES, SolveRoutine, ReverseCuthillMckee, Sorter, SolverPardiso, SolverUMFPACK, SolverSuperLU, EMSolver
@@ -28,13 +28,13 @@ from ..const import MU0, C0
28
28
 
29
29
  class FieldFunctionClass:
30
30
  """ This Class serves as a picklable class so that ModalPort boundary conditions
31
- can actually be stored with the Simulation3D class. Functions aren't picklable in
31
+ can actually be stored with the Simulation class. Functions aren't picklable in
32
32
  Python.
33
33
 
34
34
  I am not happy with the existence of this class, it feels too ad-hoc but for now it
35
35
  is the simplest way. It stores all actually required information needed to do a
36
36
  surface field interpolation without needing to store the Mesh3D and SurfaceMesh class
37
- objects plus the NedelecLegrange2 classes with the Simulation3D.
37
+ objects plus the NedelecLegrange2 classes with the Simulation.
38
38
 
39
39
  As it stands currently, only the GMSH mesh is stored plus the geometry objects. The
40
40
  mesh is reconstructed as it is deterministic.
@@ -18,7 +18,7 @@
18
18
  from .pcb import PCB
19
19
  from .pmlbox import pmlbox
20
20
  from .horn import Horn
21
- from .shapes import Cyllinder, CoaxCyllinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment
21
+ from .shapes import Cylinder, CoaxCylinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment
22
22
  from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect
23
23
  from .polybased import XYPolygon, GeoPrism
24
24
  from .step import STEPItems
@@ -18,7 +18,7 @@
18
18
  from __future__ import annotations
19
19
  import numpy as np
20
20
  from typing import Callable, Iterable
21
- from .shapes import Box, Cyllinder, Alignment
21
+ from .shapes import Box, Cylinder, Alignment
22
22
  from ..geometry import GeoVolume, GeoObject
23
23
  from .operations import rotate, mirror, translate, add, subtract, embed
24
24
  from numbers import Number
@@ -400,7 +400,7 @@ class Modeler:
400
400
  if NPoly:
401
401
  cyl = XYPolygon.circle(r, Nsections=NPoly).extrude(h, cs2)
402
402
  else:
403
- cyl = Cyllinder(r,h, cs2)
403
+ cyl = Cylinder(r,h, cs2)
404
404
  cyls.append(cyl)
405
405
 
406
406
  self.ndimcont.set_copies(N)
emerge/_emerge/geo/pcb.py CHANGED
@@ -20,7 +20,7 @@ from __future__ import annotations
20
20
  from ..cs import CoordinateSystem, GCS, Axis
21
21
  from ..geometry import GeoPolygon, GeoVolume, GeoSurface
22
22
  from ..material import Material, AIR, COPPER
23
- from .shapes import Box, Plate, Cyllinder
23
+ from .shapes import Box, Plate, Cylinder
24
24
  from .polybased import XYPolygon
25
25
  from .operations import change_coordinate_system
26
26
  from .pcb_tools.macro import parse_macro
@@ -1079,7 +1079,7 @@ class PCB:
1079
1079
  plane = change_coordinate_system(plane, self.cs) # type: ignore
1080
1080
  return plane # type: ignore
1081
1081
 
1082
- def gen_pcb(self,
1082
+ def generate_pcb(self,
1083
1083
  split_z: bool = True,
1084
1084
  layer_tolerance: float = 1e-6,
1085
1085
  merge: bool = True) -> GeoVolume:
@@ -1119,7 +1119,7 @@ class PCB:
1119
1119
  box = change_coordinate_system(box, self.cs)
1120
1120
  return box # type: ignore
1121
1121
 
1122
- def gen_air(self, height: float) -> GeoVolume:
1122
+ def generate_air(self, height: float) -> GeoVolume:
1123
1123
  """Generate the Air Block object
1124
1124
 
1125
1125
  This requires that the width, depth and origin are deterimed. This
@@ -1247,14 +1247,14 @@ class PCB:
1247
1247
  plate = change_coordinate_system(plate, self.cs)
1248
1248
  return plate # type: ignore
1249
1249
 
1250
- def generate_vias(self, merge=False) -> list[Cyllinder] | Cyllinder:
1250
+ def generate_vias(self, merge=False) -> list[Cylinder] | Cylinder:
1251
1251
  """Generates the via objects.
1252
1252
 
1253
1253
  Args:
1254
1254
  merge (bool, optional): Whether to merge the result into a final object. Defaults to False.
1255
1255
 
1256
1256
  Returns:
1257
- list[Cyllinder] | Cyllinder: Either al ist of cylllinders or a single one (merge=True)
1257
+ list[Cylinder] | Cylinder: Either al ist of cylllinders or a single one (merge=True)
1258
1258
  """
1259
1259
  vias = []
1260
1260
  for via in self.vias:
@@ -1263,7 +1263,7 @@ class PCB:
1263
1263
  z0 = via.z1*self.unit
1264
1264
  xg, yg, zg = self.cs.in_global_cs(x0, y0, z0)
1265
1265
  cs = CoordinateSystem(self.cs.xax, self.cs.yax, self.cs.zax, np.array([xg, yg, zg]))
1266
- cyl = Cyllinder(via.radius*self.unit, (via.z2-via.z1)*self.unit, cs, via.segments)
1266
+ cyl = Cylinder(via.radius*self.unit, (via.z2-via.z1)*self.unit, cs, via.segments)
1267
1267
  cyl.material = COPPER
1268
1268
  vias.append(cyl)
1269
1269
  if merge:
@@ -172,26 +172,26 @@ class Plate(GeoSurface):
172
172
  tags: list[int] = [gmsh.model.occ.addPlaneSurface([tag_wire,]),]
173
173
  super().__init__(tags)
174
174
 
175
- class Cyllinder(GeoVolume):
175
+ class Cylinder(GeoVolume):
176
176
 
177
177
  def __init__(self,
178
178
  radius: float,
179
179
  height: float,
180
180
  cs: CoordinateSystem = None,
181
181
  Nsections: int = None):
182
- """Generates a Cyllinder object in 3D space.
182
+ """Generates a Cylinder object in 3D space.
183
183
  The cyllinder will always be placed in the origin of the provided CoordinateSystem.
184
184
  The bottom cyllinder plane is always placed in the XY-plane. The lenth of the cyllinder is
185
185
  oriented along the Z-axis.
186
186
 
187
187
  By default the cyllinder uses the Open Cascade modeling for a cyllinder. In this representation
188
188
  the surface of the cyllinder is approximated with a tolerance thay may be irregular.
189
- As an alternative, the argument Nsections may be provided in which case the Cyllinder is replaced
189
+ As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
190
190
  by an extrusion of a regular N-sided polygon.
191
191
 
192
192
  Args:
193
- radius (float): The radius of the Cyllinder
194
- height (float): The height of the Cyllinder
193
+ radius (float): The radius of the Cylinder
194
+ height (float): The height of the Cylinder
195
195
  cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
196
196
  Nsections (int, optional): The number of sections. Defaults to None.
197
197
  """
@@ -233,7 +233,7 @@ class Cyllinder(GeoVolume):
233
233
  xo, yo, zo = self.cs.in_global_cs(x.flatten(), y.flatten(), z.flatten())
234
234
  return xo, yo, zo
235
235
 
236
- class CoaxCyllinder(GeoVolume):
236
+ class CoaxCylinder(GeoVolume):
237
237
  """A coaxial cylinder with an inner and outer radius."""
238
238
 
239
239
  def __init__(self,
@@ -249,12 +249,12 @@ class CoaxCyllinder(GeoVolume):
249
249
 
250
250
  By default the coax uses the Open Cascade modeling for a cyllinder. In this representation
251
251
  the surface of the cyllinder is approximated with a tolerance thay may be irregular.
252
- As an alternative, the argument Nsections may be provided in which case the Cyllinder is replaced
252
+ As an alternative, the argument Nsections may be provided in which case the Cylinder is replaced
253
253
  by an extrusion of a regular N-sided polygon.
254
254
 
255
255
  Args:
256
- radius (float): The radius of the Cyllinder
257
- height (float): The height of the Cyllinder
256
+ radius (float): The radius of the Cylinder
257
+ height (float): The height of the Cylinder
258
258
  cs (CoordinateSystem, optional): The coordinate system. Defaults to None.
259
259
  Nsections (int, optional): The number of sections. Defaults to None.
260
260
  """
@@ -267,8 +267,8 @@ class CoaxCyllinder(GeoVolume):
267
267
  self.rin = rin
268
268
  self.height = height
269
269
 
270
- self.cyl_out = Cyllinder(rout, height, cs, Nsections=Nsections)
271
- self.cyl_in = Cyllinder(rin, height, cs, Nsections=Nsections)
270
+ self.cyl_out = Cylinder(rout, height, cs, Nsections=Nsections)
271
+ self.cyl_in = Cylinder(rin, height, cs, Nsections=Nsections)
272
272
  self.cyl_in._exists = False
273
273
  self.cyl_out._exists = False
274
274
  cyltags, _ = gmsh.model.occ.cut(self.cyl_out.dimtags, self.cyl_in.dimtags)
emerge/_emerge/howto.py CHANGED
@@ -27,11 +27,11 @@ class _HowtoClass:
27
27
  """
28
28
  To start a simulation simply create a model object through:
29
29
 
30
- >>> model = emerge.Simulation3D('MyProjectName')
30
+ >>> model = emerge.Simulation('MyProjectName')
31
31
 
32
32
  Optionally, you can use a context manager for a more explicit handling of exiting the GMSH api and storing data after simulations.
33
33
 
34
- >>> with emerge.Simulation3D('MyProjectName') as model:
34
+ >>> with emerge.Simulation('MyProjectName') as model:
35
35
 
36
36
  """
37
37
  pass
@@ -45,7 +45,7 @@ class _HowtoClass:
45
45
  >>> sphere = emerge.geo.Sphere(...)
46
46
  >>> pcb_layouter = emerge.geo.PCBLayout(...)
47
47
  >>> plate = emerge.geo.Plate(...)
48
- >>> cyl = emerge.geo.Cyllinder(...)
48
+ >>> cyl = emerge.geo.Cylinder(...)
49
49
 
50
50
  After making geometries, you should pass all of them to
51
51
  the simulation object
@@ -77,7 +77,7 @@ class _HowtoClass:
77
77
  >>> face = cutout.face('front', tool=box)
78
78
 
79
79
  Exclusions or specific isolations can be added with optional arguments.
80
- There is also a select object in your Simulation3D class that has various convenient selection options
80
+ There is also a select object in your Simulation class that has various convenient selection options
81
81
  >>> faces = model.select.face.inlayer()
82
82
  >>> faces = model.select.inplane()
83
83
  >>> faces = model.select.face.near(x,y,z)
@@ -99,15 +99,15 @@ class _HowtoClass:
99
99
 
100
100
  """
101
101
 
102
- def run_frequency_domain(self):
102
+ def run_sweep(self):
103
103
  """
104
104
  You can run a frequency domain study by simply calling:\
105
105
 
106
- >>> results = model.mw.frequency_domain(...)
106
+ >>> results = model.mw.run_sweep(...)
107
107
 
108
108
  You can distribute your frequency sweep across multiple threads using
109
109
 
110
- >>> results = model.mw.frequency_domain(parallel=True, njobs=3)
110
+ >>> results = model.mw.run_sweep(parallel=True, njobs=3)
111
111
 
112
112
  The frequency domain study will return an MWSimData object that contains all data.
113
113
  """
@@ -154,7 +154,7 @@ class _HowtoClass:
154
154
  def save_and_load(self):
155
155
  """
156
156
  You can save your project data by setting save_file to True:
157
- >>> model = emerge.Simulation3D(..., save_file=True)
157
+ >>> model = emerge.Simulation(..., save_file=True)
158
158
 
159
159
  Whenever you want, you can save all data by calling the .save() method
160
160
 
@@ -166,7 +166,7 @@ class _HowtoClass:
166
166
 
167
167
  You can load the data from a simulation using:
168
168
 
169
- >>> model = emerge.Simulation3D(..., load_file=True)
169
+ >>> model = emerge.Simulation(..., load_file=True)
170
170
 
171
171
  The data from a simulation can be found in:
172
172
 
@@ -577,14 +577,14 @@ class Microwave3D:
577
577
  logger.info(f'Elapsed time = {(T2-T0):.2f} seconds.')
578
578
  return None
579
579
 
580
- def frequency_domain(self,
581
- parallel: bool = False,
582
- njobs: int = 2,
583
- harddisc_threshold: int | None = None,
584
- harddisc_path: str = 'EMergeSparse',
585
- frequency_groups: int = -1,
586
- multi_processing: bool = False,
587
- automatic_modal_analysis: bool = True) -> MWData:
580
+ def run_sweep(self,
581
+ parallel: bool = False,
582
+ njobs: int = 2,
583
+ harddisc_threshold: int | None = None,
584
+ harddisc_path: str = 'EMergeSparse',
585
+ frequency_groups: int = -1,
586
+ multi_processing: bool = False,
587
+ automatic_modal_analysis: bool = True) -> MWData:
588
588
  """Executes a frequency domain study
589
589
 
590
590
  The study is distributed over "njobs" workers.
@@ -1017,179 +1017,13 @@ class Microwave3D:
1017
1017
  mode_p = sparam_mode_power(self.mesh.nodes, tri_vertices, bc, k0, const, 5)
1018
1018
  return field_p, mode_p
1019
1019
 
1020
- # def frequency_domain_single(self, automatic_modal_analysis: bool = False) -> MWData:
1021
- # """Execute a frequency domain study without distributed frequency sweep.
1022
1020
 
1023
- # Args:
1024
- # automatic_modal_analysis (bool, optional): Automatically compute port modes. Defaults to False.
1021
+ ############################################################
1022
+ # DEPRICATED FUNCTIONS #
1023
+ ############################################################
1025
1024
 
1026
- # Raises:
1027
- # SimulationError: _description_
1028
-
1029
- # Returns:
1030
- # MWSimData: The Simulation data.
1031
- # """
1032
- # T0 = time.time()
1033
- # mesh = self.mesh
1034
- # if self.bc._initialized is False:
1035
- # raise SimulationError('Cannot run a modal analysis because no boundary conditions have been assigned.')
1036
-
1037
- # self._initialize_field()
1038
- # self._initialize_bc_data()
1039
-
1040
- # er = self.mesh.retreive(lambda mat,x,y,z: mat.fer3d_mat(x,y,z), self.mesher.volumes)
1041
- # ur = self.mesh.retreive(lambda mat,x,y,z: mat.fur3d_mat(x,y,z), self.mesher.volumes)
1042
- # cond = self.mesh.retreive(lambda mat,x,y,z: mat.cond, self.mesher.volumes)[0,0,:]
1043
-
1044
- # ertri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
1045
- # urtri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
1046
- # condtri = np.zeros((self.mesh.n_tris,), dtype=np.complex128)
1047
-
1048
- # for itri in range(self.mesh.n_tris):
1049
- # itet = self.mesh.tri_to_tet[0,itri]
1050
- # ertri[:,:,itri] = er[:,:,itet]
1051
- # urtri[:,:,itri] = ur[:,:,itet]
1052
- # condtri[itri] = cond[itet]
1053
-
1054
- # #### Port settings
1055
-
1056
- # all_ports = self.bc.oftype(PortBC)
1057
- # port_numbers = [port.port_number for port in all_ports]
1058
-
1059
- # ##### FOR PORT SWEEP SET ALL ACTIVE TO FALSE. THIS SHOULD BE FIXED LATER
1060
- # ### COMPUTE WHICH TETS ARE CONNECTED TO PORT INDICES
1061
-
1062
- # all_port_tets = []
1063
- # for port in all_ports:
1064
- # port.active=False
1065
-
1066
- # all_port_tets = mesh.get_face_tets(*[port.tags for port in all_ports])
1067
-
1068
-
1069
- # logger.debug(f'Starting the simulation of {len(self.frequencies)} frequency points.')
1070
-
1071
- # # ITERATE OVER FREQUENCIES
1072
- # for freq in self.frequencies:
1073
- # logger.info(f'Simulation frequency = {freq/1e9:.3f} GHz')
1074
-
1075
- # # Assembling matrix problem
1076
- # if automatic_modal_analysis:
1077
- # self._compute_modes(freq)
1078
-
1079
- # job = self.assembler.assemble_freq_matrix(self.basis, er, ur, cond, self.bc.boundary_conditions, freq, cache_matrices=self.cache_matrices)
1080
-
1081
- # logger.debug(f'Routine: {self.solveroutine}')
1082
-
1083
- # for A, b, ids, reuse in job.iter_Ab():
1084
- # solution, report = self.solveroutine.solve(A, b, ids, reuse)
1085
- # job.submit_solution(solution, report)
1086
-
1087
- # self.data.setreport(job.reports, freq=freq, **self._params)
1088
-
1089
- # k0 = 2*np.pi*freq/299792458
1090
-
1091
- # scalardata = self.data.scalar.new(freq=freq, **self._params)
1092
- # scalardata.init_sp(port_numbers)
1093
- # scalardata.freq = freq
1094
- # scalardata.k0 = k0
1095
-
1096
- # fielddata = self.data.field.new(freq=freq, **self._params)
1097
- # fielddata.freq = freq
1098
- # fielddata.er = np.squeeze(er[0,0,:])
1099
- # fielddata.ur = np.squeeze(ur[0,0,:])
1100
-
1101
- # # Recording port information
1102
- # for i, port in enumerate(all_ports):
1103
- # fielddata.add_port_properties(port.port_number,
1104
- # mode_number=port.mode_number,
1105
- # k0 = k0,
1106
- # beta = port.get_beta(k0),
1107
- # Z0 = port.portZ0(k0),
1108
- # Pout= port.power)
1109
-
1110
- # for active_port in all_ports:
1111
-
1112
- # active_port.active = True
1113
- # solution = job._fields[active_port.port_number]
1114
-
1115
- # fielddata._fields[active_port.port_number] = solution # TODO: THIS IS VERY FRAIL
1116
- # fielddata.basis = self.basis
1117
-
1118
- # # Compute the S-parameters
1119
- # # Define the field interpolation function
1120
- # fieldf = self.basis.interpolate_Ef(solution, tetids=all_port_tets)
1121
-
1122
- # # Active port power
1123
- # logger.debug('Active ports:')
1124
- # tris = mesh.get_triangles(active_port.tags)
1125
- # tri_vertices = mesh.tris[:,tris]
1126
- # pfield, pmode = self._compute_s_data(active_port, fieldf, tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
1127
- # logger.debug(f'Field Amplitude = {np.abs(pfield):.3f}, Excitation = {np.abs(pmode):.2f}')
1128
- # Pout = pmode
1129
-
1130
- # #Passive ports
1131
- # logger.debug('Passive ports:')
1132
- # for bc in all_ports:
1133
- # tris = mesh.get_triangles(bc.tags)
1134
- # tri_vertices = mesh.tris[:,tris]
1135
- # pfield, pmode = self._compute_s_data(bc, fieldf, tri_vertices, k0, ertri[:,:,tris], urtri[:,:,tris])
1136
- # logger.debug(f'Field amplitude = {np.abs(pfield):.3f}, Excitation= {np.abs(pmode):.2f}')
1137
- # scalardata.write_S(bc.port_number, active_port.port_number, pfield/Pout)
1138
-
1139
- # active_port.active=False
1140
-
1141
- # fielddata.set_field_vector()
1142
-
1143
- # logger.info('Simulation Complete!')
1144
- # T2 = time.time()
1145
- # logger.info(f'Elapsed time = {(T2-T0):.2f} seconds.')
1146
- # return self.data
1147
- ## DEPRICATED
1148
-
1149
-
1150
-
1151
- #
1152
- # def eigenmode(self, mesh: Mesh3D, solver = None, num_sols: int = 6):
1153
- # if solver is None:
1154
- # logger.info('Defaulting to BiCGStab.')
1155
- # solver = sparse.linalg.eigs
1156
-
1157
- # if self.order == 1:
1158
- # logger.info('Detected 1st order elements.')
1159
- # from ...elements.nedelec1.assembly import assemble_eig_matrix
1160
- # ft = FieldType.VEC_LIN
1161
-
1162
- # elif self.order == 2:
1163
- # logger.info('Detected 2nd order elements.')
1164
- # from ...elements.nedelec2.assembly import assemble_eig_matrix_E
1165
- # ft = FieldType.VEC_QUAD
1166
-
1167
- # er = self.mesh.retreive(mesh.centers, lambda mat,x,y,z: mat.fer3d(x,y,z))
1168
- # ur = self.mesh.retreive(mesh.centers, lambda mat,x,y,z: mat.fur3d(x,y,z))
1169
-
1170
- # dataset = Dataset3D(mesh, self.frequencies, 0, ft)
1171
- # dataset.er = er
1172
- # dataset.ur = ur
1173
- # logger.info('Solving eigenmodes.')
1174
-
1175
- # f_target = self.frequencies[0]
1176
- # sigma = (2 * np.pi * f_target / 299792458)**2
1177
-
1178
- # A, B, solvenodes = assemble_eig_matrix(mesh, er, ur, self.boundary_conditions)
1179
-
1180
- # A = A[np.ix_(solvenodes, solvenodes)]
1181
- # B = B[np.ix_(solvenodes, solvenodes)]
1182
- # #A = sparse.csc_matrix(A)
1183
- # #B = sparse.csc_matrix(B)
1184
-
1185
- # w, v = sparse.linalg.eigs(A, k=num_sols, M=B, sigma=sigma, which='LM')
1186
-
1187
- # logger.info(f'Eigenvalues: {np.sqrt(w)*299792458/(2*np.pi) * 1e-9} GHz')
1188
-
1189
- # Esol = np.zeros((num_sols, mesh.nfield), dtype=np.complex128)
1190
-
1191
- # Esol[:, solvenodes] = v.T
1192
-
1193
- # dataset.set_efield(Esol)
1194
-
1195
- # self.basis = dataset
1025
+ def frequency_domain(self, *args, **kwargs):
1026
+ """DEPRICATED VERSION: Use run_sweep() instead.
1027
+ """
1028
+ logger.warning('This function is depricated. Please use run_sweep() instead')
1029
+ self.run_sweep(*args, **kwargs)
@@ -242,38 +242,170 @@ def plot(
242
242
 
243
243
  plt.show()
244
244
 
245
- def smith(f: np.ndarray, S: np.ndarray) -> None:
246
- """ Plot the Smith Chart
245
+ def smith(
246
+ S: np.ndarray | Sequence[np.ndarray],
247
+ f: Optional[np.ndarray | Sequence[np.ndarray]] = None,
248
+ colors: Optional[Union[str, Sequence[Optional[str]]]] = None,
249
+ markers: Optional[Union[str, Sequence[str]]] = None,
250
+ labels: Optional[Union[str, Sequence[str]]] = None,
251
+ title: Optional[str] = None,
252
+ linewidth: Optional[Union[float, Sequence[Optional[float]]]] = None,
253
+ n_flabels: int = 8,
254
+ funit: str = 'GHz'
255
+ ) -> None:
256
+ """Plot S-parameter traces on a Smith chart with optional per-trace styling
257
+ and sparse frequency annotations (e.g., labeled by frequency).
247
258
 
248
259
  Args:
249
- f (np.ndarray): Frequency vector (Not used yet)
250
- S (np.ndarray): S-parameters to plot
251
- """
252
- if not isinstance(S, list):
253
- Ss = [S]
260
+ S (np.ndarray | Sequence[np.ndarray]): One or more 1D complex arrays of
261
+ reflection coefficients (Γ) to plot (each shaped like (N,)).
262
+ f (Optional[np.ndarray | Sequence[np.ndarray]], optional): Frequency
263
+ vector(s) aligned with `S` for sparse on-curve labels; provide a
264
+ single array for all traces or one array per trace. Defaults to None.
265
+ colors (Optional[Union[str, Sequence[Optional[str]]]], optional): Color
266
+ for all traces or a sequence of per-trace colors. Defaults to None
267
+ (uses Matplotlib’s color cycle).
268
+ markers (Optional[Union[str, Sequence[str]]], optional): Marker style
269
+ for all traces or per-trace markers. Defaults to None (treated as 'none').
270
+ labels (Optional[Union[str, Sequence[str]]], optional): Legend label for
271
+ all traces or a sequence of per-trace labels. If omitted, no legend
272
+ is shown. Defaults to None.
273
+ title (Optional[str], optional): Axes title. Defaults to None.
274
+ linewidth (Optional[Union[float, Sequence[Optional[float]]]], optional):
275
+ Line width for all traces or per-trace widths. Defaults to None
276
+ (Matplotlib default).
277
+ n_flabels (int, optional): Approximate number of frequency labels to
278
+ place per trace (set 0 to disable, even if `f` is provided).
279
+ Defaults to 8.
280
+ funit (str, optional): Frequency unit used to scale/format labels.
281
+ One of {'Hz','kHz','MHz','GHz','THz'} (case-insensitive).
282
+ Defaults to 'GHz'.
283
+
284
+ Raises:
285
+ ValueError: If a style argument (`colors`, `markers`, `linewidth`, or
286
+ `labels`) is a sequence whose length does not match the number of traces.
287
+ ValueError: If `f` is a sequence whose length does not match the number
288
+ of traces.
289
+ ValueError: If `funit` is not one of {'Hz','kHz','MHz','GHz','THz'}.
290
+
291
+ Returns:
292
+ None: Draws the Smith chart on a new figure/axes and displays it with `plt.show()`.
293
+ """
294
+ # --- normalize S into a list of 1D complex arrays ---
295
+ if isinstance(S, (list, tuple)):
296
+ Ss: List[np.ndarray] = [np.asarray(s).ravel() for s in S]
254
297
  else:
255
- Ss = S
256
-
257
- fig, ax = plt.subplots()
298
+ Ss = [np.asarray(S).ravel()]
299
+
300
+ n_traces = len(Ss)
301
+
302
+ # --- helper: broadcast a scalar or single value to n_traces, or validate a sequence ---
303
+ def _broadcast(value, default, name: str) -> List:
304
+ if value is None:
305
+ return [default for _ in range(n_traces)]
306
+ # treat bare strings specially (they’re Sequences but should broadcast)
307
+ if isinstance(value, str):
308
+ return [value for _ in range(n_traces)]
309
+ if not isinstance(value, (list, tuple)):
310
+ return [value for _ in range(n_traces)]
311
+ if len(value) != n_traces:
312
+ raise ValueError(f"`{name}` must have length {n_traces}, got {len(value)}.")
313
+ return list(value)
314
+
315
+ # --- style parameters (broadcast as needed) ---
316
+ markers_list = _broadcast(markers, 'none', 'markers')
317
+ colors_list = _broadcast(colors, None, 'colors')
318
+ lw_list = _broadcast(linewidth, None, 'linewidth')
319
+ labels_list: Optional[List[Optional[str]]]
320
+ if labels is None:
321
+ labels_list = None
322
+ else:
323
+ labels_list = _broadcast(labels, None, 'labels')
324
+
325
+ # --- frequencies (broadcast as needed) ---
326
+ if f is None:
327
+ fs_list: List[Optional[np.ndarray]] = [None for _ in range(n_traces)]
328
+ else:
329
+ if isinstance(f, (list, tuple)):
330
+ if len(f) != n_traces:
331
+ raise ValueError(f"`f` must have length {n_traces}, got {len(f)}.")
332
+ fs_list = [np.asarray(fi).ravel() for fi in f]
333
+ else:
334
+ fi = np.asarray(f).ravel()
335
+ fs_list = [fi for _ in range(n_traces)]
336
+
337
+ # --- unit scaling ---
338
+ units = {'hz':1.0, 'khz':1e3, 'mhz':1e6, 'ghz':1e9, 'thz':1e12}
339
+ key = funit.lower()
340
+ if key not in units:
341
+ raise ValueError(f"Unknown funit '{funit}'. Choose from {list(units.keys())}.")
342
+ fdiv = units[key]
343
+
344
+ # --- figure/axes ---
345
+ fig: Figure
346
+ ax: Axes
347
+ fig, ax = plt.subplots(figsize=(6, 6))
348
+
349
+ # --- smith grid (kept out of legend) ---
258
350
  for line in _smith_transform(_generate_grids()):
259
- ax.plot(line[0], line[1], color='grey', alpha=0.3, linewidth=0.7)
260
- p = np.linspace(0,2*np.pi,101)
261
- ax.plot(np.cos(p), np.sin(p), color='black', alpha=0.5)
262
- # Add important numbers for the Impedance Axes
263
- # X and Y values (0, 0.5, 1, 2, 10, 50)
264
- for i in [0, 0.2, 0.5, 1, 2, 10]:
265
- z = i + 1j*0
266
- G = (z-1)/(z+1)
267
- ax.annotate(f"{i}", (G.real, G.imag), color='black', fontsize=8)
268
- for i in [0, 0.2, 0.5, 1, 2, 10]:
269
- z = 0 + 1j*i
270
- G = (z-1)/(z+1)
271
- ax.annotate(f"{i}", (G.real, G.imag), color='black', fontsize=8)
272
- ax.annotate(f"{-i}", (G.real, -G.imag), color='black', fontsize=8)
273
- for s in Ss:
274
- ax.plot(s.real, s.imag, color='blue')
351
+ ax.plot(line[0], line[1], color='0.6', alpha=0.3, linewidth=0.7, label='_nolegend_')
352
+
353
+ # unit circle
354
+ p = np.linspace(0, 2*np.pi, 361)
355
+ ax.plot(np.cos(p), np.sin(p), color='black', alpha=0.5, linewidth=0.8, label='_nolegend_')
356
+
357
+ # --- annotate a few impedance reference ticks (kept out of legend) ---
358
+ ref_vals = [0, 0.2, 0.5, 1, 2, 10]
359
+ for r in ref_vals:
360
+ z = r + 1j*0
361
+ G = (z - 1) / (z + 1)
362
+ ax.annotate(f"{r}", (G.real, G.imag), color='black', fontsize=8)
363
+ for x in ref_vals:
364
+ z = 0 + 1j*x
365
+ G = (z - 1) / (z + 1)
366
+ ax.annotate(f"{x}", (G.real, G.imag), color='black', fontsize=8)
367
+ ax.annotate(f"{-x}", (G.real, -G.imag), color='black', fontsize=8)
368
+
369
+ # --- plot traces ---
370
+ for i, s in enumerate(Ss):
371
+ lbl = labels_list[i] if labels_list is not None else None
372
+ line, = ax.plot(
373
+ s.real, s.imag,
374
+ color=colors_list[i],
375
+ marker=markers_list[i],
376
+ linewidth=lw_list[i],
377
+ label=lbl
378
+ )
379
+
380
+ # frequency labels (sparse)
381
+ fi = fs_list[i]
382
+ if fi[0] is not None and n_flabels > 0 and len(s) > 0 and len(fi) > 0:
383
+ n = min(len(s), len(fi))
384
+ step = max(1, int(round(n / n_flabels))) if n_flabels > 0 else n # avoid step=0
385
+ idx = np.arange(0, n, step)
386
+ # small offset so labels don't sit right on the curve
387
+ dx = 0.03
388
+ for k in idx:
389
+ fk = fi[k] / fdiv
390
+ # choose a compact format (3 significant digits)
391
+ idigit = 3
392
+ if np.log10(fk)>3:
393
+ idigit = 1
394
+ ftxt = f"{fk:.{idigit}f}{funit}"
395
+ ax.annotate(ftxt, (s[k].real + dx, s[k].imag), fontsize=8, color=line.get_color())
396
+
397
+ # legend only if labels were given
398
+ if labels_list is not None:
399
+ ax.legend(loc='best')
400
+
401
+ if title:
402
+ ax.set_title(title)
403
+
404
+ ax.set_aspect('equal', adjustable='box')
405
+ ax.set_xlim(-1.1, 1.1)
406
+ ax.set_ylim(-1.1, 1.1)
275
407
  ax.grid(False)
276
- ax.axis('equal')
408
+ plt.tight_layout()
277
409
  plt.show()
278
410
 
279
411
  def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
@@ -13,7 +13,7 @@ wga = 22.86*mm
13
13
  wgb = 10.16*mm
14
14
  wgl = 50*mm
15
15
 
16
- with em.Simulation3D("myfile", save_file=True) as m:
16
+ with em.Simulation("myfile", save_file=True) as m:
17
17
  m['box'] = em.geo.Box(wga,wgl,wgb,(0,0,0))
18
18
 
19
19
  m.commit_geometry()
@@ -29,4 +29,4 @@ with em.Simulation3D("myfile", save_file=True) as m:
29
29
  port2 = m.mw.bc.RectangularWaveguide(m['box'].face('back'), 2)
30
30
 
31
31
  # Run simulation steps
32
- m.mw.frequency_domain()
32
+ m.mw.run_sweep()
@@ -9,7 +9,7 @@ mil = 0.0254
9
9
  um = 0.000001
10
10
  PI = np.pi
11
11
 
12
- with em.Simulation3D("myfile", load_file=True) as m:
12
+ with em.Simulation("myfile", load_file=True) as m:
13
13
 
14
14
  data = m.data.mw.scalar.grid
15
15
 
@@ -58,7 +58,7 @@ class SimulationError(Exception):
58
58
  # BASE 3D SIMULATION MODEL #
59
59
  ############################################################
60
60
 
61
- class Simulation3D:
61
+ class Simulation:
62
62
 
63
63
  def __init__(self,
64
64
  modelname: str,
@@ -67,7 +67,7 @@ class Simulation3D:
67
67
  save_file: bool = False,
68
68
  logfile: bool = False,
69
69
  path_suffix: str = ".EMResults"):
70
- """Generate a Simulation3D class object.
70
+ """Generate a Simulation class object.
71
71
 
72
72
  As a minimum a file name should be provided. Additionally you may provide it with any
73
73
  class that inherits from BaseDisplay. This will then be used for geometry displaying.
@@ -128,11 +128,11 @@ class Simulation3D:
128
128
  """Get the data from the current data container"""
129
129
  return self.data.sim[name]
130
130
 
131
- def __enter__(self) -> Simulation3D:
131
+ def __enter__(self) -> Simulation:
132
132
  """This method is depricated with the new atexit system. It still exists for backwards compatibility.
133
133
 
134
134
  Returns:
135
- Simulation3D: the Simulation3D object
135
+ Simulation: the Simulation object
136
136
  """
137
137
  return self
138
138
 
@@ -408,7 +408,7 @@ class Simulation3D:
408
408
  Example:
409
409
  >>> for W, H in model.parameter_sweep(True, width=widths, height=heights):
410
410
  >>> // build simulation
411
- >>> data = model.frequency_domain()
411
+ >>> data = model.run_sweep()
412
412
  >>> // Extract the data
413
413
  >>> widths, heights, frequencies, S21 = data.ax('width','height','freq').S(2,1)
414
414
  """
@@ -458,4 +458,5 @@ class Simulation3D:
458
458
  """DEPRICATED VERSION: Use .commit_geometry()
459
459
  """
460
460
  logger.warning('define_geometry() will be derpicated. Use commit_geometry() instead.')
461
- self.commit_geometry(*args)
461
+ self.commit_geometry(*args)
462
+
emerge/lib.py CHANGED
@@ -30,10 +30,10 @@ MET_ALUMINUM = Material(cond=3.77e7, color=GREY, opacity=0.5)
30
30
  MET_CARBON = Material(cond=3.33e4, color=GREY, opacity=0.5)
31
31
  MET_CHROMIUM = Material(cond=5.56e6, color=GREY, opacity=0.5)
32
32
  MET_COPPER = Material(cond=5.8e7, color="#62290c", opacity=1.0)
33
- MET_GOLD = Material(cond=4.10e7, color=GREY, opacity=0.5)
33
+ MET_GOLD = Material(cond=4.10e7, color="#d4af37", opacity=0.5)
34
34
  MET_INDIUM = Material(cond=6.44e6, color=GREY, opacity=0.5)
35
35
  MET_IRIDIUM = Material(cond=2.13e7, color=GREY, opacity=0.5)
36
- MET_IRON = Material(cond=1.04e7, color=GREY, opacity=0.5)
36
+ MET_IRON = Material(cond=1.04e7, color="#aaaaaa", opacity=0.5)
37
37
  MET_LEAD = Material(cond=4.84e6, color=GREY, opacity=0.5)
38
38
  MET_MAGNESIUM = Material(cond=2.38e7, color=GREY, opacity=0.5)
39
39
  MET_NICKEL = Material(cond=1.14e7, color=GREY, opacity=0.5)
@@ -274,6 +274,7 @@ DIEL_ULTRALAM_3850 = Material(er=2.9, tand=0.0025, color="#21912b", opacity=0.3)
274
274
  DIEL_XT_Duroid_80000_002IN_Thick = Material(er=3.23, tand=0.0035, color="#21912b", opacity=0.3)
275
275
  DIEL_XT_Duroid_8100 = Material(er=3.54, tand=0.0049, color="#21912b", opacity=0.3)
276
276
  DIEL_XT_Duroid_81000_004IN_Thick = Material(er=3.32, tand=0.0038, color="#21912b", opacity=0.3)
277
+ DIEL_TEFLON = Material(er=2.1, tand=0.0003, color='#eeeeee', opacity=0.3)
277
278
 
278
279
  # Legacy FR Materials
279
280
  DIEL_FR1 = Material(er=4.8, tand=0.025, color="#3c9747", opacity=0.3) # Paper + phenolic resin
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 0.5.6
3
+ Version: 0.6.1
4
4
  Summary: An open source EM FEM simulator in Python
5
5
  Project-URL: Homepage, https://github.com/FennisRobert/EMerge
6
6
  Project-URL: Issues, https://github.com/FennisRobert/EMerge/issues
@@ -64,7 +64,7 @@ import emerge as em
64
64
  def main():
65
65
  # setup simulation
66
66
 
67
- model.mw.frequency_domain(True, ..., multi_processing=True)
67
+ model.mw.run_sweep(True, ..., multi_processing=True)
68
68
 
69
69
  if __name__ == "__main__":
70
70
  main()
@@ -1,8 +1,8 @@
1
- emerge/__init__.py,sha256=fTLCl-XBjUuatkqGewgn3IJX3kG3nrinr3C7Ioc3ghE,2607
1
+ emerge/__init__.py,sha256=3x0JR1LLC_oNbxA1oBKyGPUPf0oH82MxfwMYvIre5uo,2605
2
2
  emerge/__main__.py,sha256=WVf16sfrOI910QWohrQDaChZdRifMNoS6VKzCT6f3ZA,92
3
3
  emerge/cli.py,sha256=NU1uhwuZ6i50680v3_I4kDZPTHqz74gOYK71UBhb8oE,666
4
4
  emerge/ext.py,sha256=IBoHH5PQFj5pYMfp6r-uMpNNgbSe8c0g9x8qjBzzVmU,223
5
- emerge/lib.py,sha256=pq8YtX9G-hkUm-CyQl1RvRUyEqOaWJXi9-IadHCqhJg,22316
5
+ emerge/lib.py,sha256=rvUuawdX7-qiKH2p1RwrenN1KBdDeU-MHsAgH7_bYA8,22400
6
6
  emerge/plot.py,sha256=AH2D9rKeWUXlSOlh-pUUfLt0oxVLcqF_piki-BmPEg0,83
7
7
  emerge/pyvista.py,sha256=-Ht2YcZYsh8-dici5ZPNAWwsis6uz5wNj8n8mxv5fog,42
8
8
  emerge/_emerge/__init__.py,sha256=aidfiILy33dt3VyiZ2mgtA87mq-WQ5pXItZUE5wR5ws,703
@@ -14,7 +14,7 @@ emerge/_emerge/cs.py,sha256=efabOj7hK-eG2IYVzhGG08vyvJm2bB9PT04qt5B-BY8,17960
14
14
  emerge/_emerge/dataset.py,sha256=UcSAJ_siLrOjNBBWRWsS3GUZUpayp63EM6pP6ClwKDI,1534
15
15
  emerge/_emerge/geo2d.py,sha256=e_HkX1GQ2iYrdO0zeEgzVOzfGyU1WGJyjeGBAobOttE,3323
16
16
  emerge/_emerge/geometry.py,sha256=xZqgtqWoQpHBd5gSThhWAO24jaFL28aQ1_44NZyPNmQ,18072
17
- emerge/_emerge/howto.py,sha256=qY3v6DynB9ZBVeYa3sPFq1cDuvf2zyjnLShknqYyvKQ,8208
17
+ emerge/_emerge/howto.py,sha256=vrymIkMcrZsf8UB8rQEvAsHv2rC4nLUgup9nW-BfkcA,8172
18
18
  emerge/_emerge/logsettings.py,sha256=DcUWIUUhdLe9ev5XC1bd5ZUrJz00MjABkY8rnekFrPY,3373
19
19
  emerge/_emerge/material.py,sha256=HTjQ8wDzkBwYA1sLPRuqR8lPeoXsW-4m5K3yXoKqjXQ,4022
20
20
  emerge/_emerge/mesh3d.py,sha256=_MJa_kCZNKCq196UcshA4_GdYIs2FCF76zzXFTSLw3I,34333
@@ -22,7 +22,7 @@ emerge/_emerge/mesher.py,sha256=fKgPb6oZe_bqp0XYfZ6UNgBfRaAS3-tjUtZX8NalJe8,1319
22
22
  emerge/_emerge/periodic.py,sha256=xfdKKq3qX7iBBestnRizOzJNfXlpr9lCPkiYhfrRIR8,12013
23
23
  emerge/_emerge/plot.py,sha256=cf1I9mj7EIUJcq8vmANlUkqoV6QqVaJaP-zlC-T9E18,8041
24
24
  emerge/_emerge/selection.py,sha256=6UizuWC01sUGjFodrGTy8N1J1832vfOWA4u5paNupw4,21297
25
- emerge/_emerge/simmodel.py,sha256=8EqIavO4GUoJapOChUCXlapsQ4C9ofhRkoFSzDCVlD0,18206
25
+ emerge/_emerge/simmodel.py,sha256=3tCfxaE2tl1PoiNqjB7p69s15wtUD32xRdSQML3IQks,18198
26
26
  emerge/_emerge/simulation_data.py,sha256=r9-9lpLeA1Z5HU3jDVOXV1H80GVawnXL5K81_dvmlE4,14506
27
27
  emerge/_emerge/solver.py,sha256=PGPI2LuF4XhfyS8KfC-49X9M1mtYUadgHyqopYfPj58,48542
28
28
  emerge/_emerge/system.py,sha256=p4HNz7d_LMRNE9Gk75vVdFecDH2iN_groAM9u-yQTpk,1618
@@ -31,15 +31,15 @@ emerge/_emerge/elements/femdata.py,sha256=3rFVz1-PLtqxnDFvTH1Q9dA98lO5VyjcFKecCJ
31
31
  emerge/_emerge/elements/index_interp.py,sha256=DlDy2KrhM5QsF6jYQIl4BJndr9F9wnjFMXxlX3ewb4s,2070
32
32
  emerge/_emerge/elements/ned2_interp.py,sha256=kMhbjS1fACa1fmhSkQTU35jS3iF63dJcFe5DHl4Xo78,32541
33
33
  emerge/_emerge/elements/nedelec2.py,sha256=chU3Ewz7grKZtpyglj2qFJYHvBzCXFQZa1skzjfHoPo,6138
34
- emerge/_emerge/elements/nedleg2.py,sha256=CehidddpNpeQLSoafvkODm1qotdfb3cu3ENWGivQ7pg,8472
35
- emerge/_emerge/geo/__init__.py,sha256=ERmbdr8NUnDQWK-E-Bbc4Kl8AQczjpIP5BnwtZmcurE,1102
34
+ emerge/_emerge/elements/nedleg2.py,sha256=qVPKtJpT7UCA5dcI_mXNX7Co4tzVCvlxQv5VtsLuKN8,8468
35
+ emerge/_emerge/geo/__init__.py,sha256=9zITlCRbdzYZ65mA0i-gxEEMV24vWOG9Crb9WGyQOYE,1100
36
36
  emerge/_emerge/geo/horn.py,sha256=h4GzGzEkXgWaQgoBMvhFgGsqOrNRB3CJg3RBKXigCz0,4186
37
- emerge/_emerge/geo/modeler.py,sha256=N8yRtkK0nb3_jjDexpszmSdeElz8g0kuJpqm3vKl1mI,15575
37
+ emerge/_emerge/geo/modeler.py,sha256=gbxmwXAdR7kKW_EFbkd1QPVqJWKJpnTufI7lxaqPyjU,15573
38
38
  emerge/_emerge/geo/operations.py,sha256=LKA7_BU4iw6-i6KOUyuL6l10QjXSwnXsixOaTw-M6Vk,9697
39
- emerge/_emerge/geo/pcb.py,sha256=yhPsU_BgAi1w3FGFtoY8BjXklu2CcKvcbcLMi582x8Q,52209
39
+ emerge/_emerge/geo/pcb.py,sha256=b4FZAwZaO8X2NMnRkYYDUTa8w9ZdN2XfZdmyHhZZaGY,52213
40
40
  emerge/_emerge/geo/pmlbox.py,sha256=TNjuyPtuqrAU5Yl8GrLlievuwYf3hUKJaCEz0cXKSzo,7922
41
41
  emerge/_emerge/geo/polybased.py,sha256=4J5ExvL-yw4NtA1ZZfIYdj3J5g0vpzq_nlCsIjFQFBU,19473
42
- emerge/_emerge/geo/shapes.py,sha256=CSXzJPVlTHqtbXeGoZNRm7AjQIShBNU6gCWKDy6mY9Q,18389
42
+ emerge/_emerge/geo/shapes.py,sha256=tPWEzit4sJ-aFWc_FP0mHodx9ohPDbB7IUprFmf1S3k,18378
43
43
  emerge/_emerge/geo/step.py,sha256=XcAiEN8W4umNmZdYmrGHX_aJUuiMgc6vgT-UIk8Gbqc,2689
44
44
  emerge/_emerge/geo/pcb_tools/calculator.py,sha256=eGYUXdXmHUJCPlfJyY96S87wjeAVFG-e4sPOwbYj0eA,832
45
45
  emerge/_emerge/geo/pcb_tools/macro.py,sha256=0g-0anOFyxrEkFobiSu0cwWFRQ32xB8Az24mmwo0z6M,2992
@@ -50,7 +50,7 @@ emerge/_emerge/mth/pairing.py,sha256=i8bBvTeMmzgF0JdiDNJiTXxx913x4f10777pzD6FJo0
50
50
  emerge/_emerge/physics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  emerge/_emerge/physics/microwave/__init__.py,sha256=QHeILGYWmvbfLl1o9wrTiWLm0evfXDgS0JiikUoMTts,28
52
52
  emerge/_emerge/physics/microwave/adaptive_freq.py,sha256=aWhijhCVAbnuwkru-I1AaRdY20uyozf6OWRIh9r2ijg,9786
53
- emerge/_emerge/physics/microwave/microwave_3d.py,sha256=VrEvBFoXZctuq1sxO-BV5xlLlakbVG81SpIupR8KPQ8,48969
53
+ emerge/_emerge/physics/microwave/microwave_3d.py,sha256=zKIvFa9WDfDSP6XkYQ-kc_0umZnOIfe0mpBku0piI-4,42000
54
54
  emerge/_emerge/physics/microwave/microwave_bc.py,sha256=-fxeZREL42snL4XvXuUqo4QJh24o8lHJ_IcWxkt5yAU,42484
55
55
  emerge/_emerge/physics/microwave/microwave_data.py,sha256=6xQL-XaFr1j1OZfCNltODXxXlXAZ9ghEaNKW7nHKCis,43889
56
56
  emerge/_emerge/physics/microwave/periodic.py,sha256=wYSUgLFVtCLqSG3EDKoCDRU93iPUzBdXzVRdHTRmbpI,3000
@@ -67,19 +67,19 @@ emerge/_emerge/physics/microwave/assembly/periodicbc.py,sha256=Zg1kgQMccDQA2oVEr
67
67
  emerge/_emerge/physics/microwave/assembly/robinbc.py,sha256=syJ-NuHHA0WDQECuaPdeW-OfzIGHmxxqalKiokSyJFI,17742
68
68
  emerge/_emerge/plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  emerge/_emerge/plot/display.py,sha256=TQLlKb-LkaG5ZOSLfxp9KXPlZPRFTxNj1LhVQ-Lp1-s,18476
70
- emerge/_emerge/plot/simple_plots.py,sha256=95wkxkK1x-fL7Cb8ioeMe3QAUAnTy9wNIkZCVOhSrwY,18751
70
+ emerge/_emerge/plot/simple_plots.py,sha256=qopwJviG36JC48Flioq2whS_ktvPtG_8h3X9noUbo9I,24462
71
71
  emerge/_emerge/plot/matplotlib/mpldisplay.py,sha256=szKafDrgdAW5Nyc5UOHuJC87n0WGkXYackOVv182TDQ,8671
72
72
  emerge/_emerge/plot/pyvista/__init__.py,sha256=CPclatEu6mFnJZzCQk09g6T6Fh20WTbiLAJGSwAnPXU,30
73
73
  emerge/_emerge/plot/pyvista/display.py,sha256=L7_cy7C6EnIIj_-V5E_-j5ORJtIXscvYCmBE03-eCK4,34692
74
74
  emerge/_emerge/plot/pyvista/display_settings.py,sha256=K2OhzKqeFzMXlEfZ5F4CQ9sN3l7nOgVjLplZBuMPjvE,899
75
75
  emerge/_emerge/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
- emerge/_emerge/projects/_gen_base.txt,sha256=_4McYD9Sb1KWRsrGV51mNNAJNYkjm_IemMnCdAsGuLo,607
77
- emerge/_emerge/projects/_load_base.txt,sha256=JTAhWvHXJQYKe-aDD4HDdmE7f3VXxeurhDzwFCegrEg,511
76
+ emerge/_emerge/projects/_gen_base.txt,sha256=DqQz36PZg6v1ovQjHvPjd0t4AIbmikZdb9dmrNYsK3w,598
77
+ emerge/_emerge/projects/_load_base.txt,sha256=94o0eSWoDKlNR336EmhpG_S5syQHIUPHQxna-bVRhXo,509
78
78
  emerge/_emerge/projects/generate_project.py,sha256=TNw-0SpLc82MBq0bd9hB_yqvBZCgmuPonCBsHTp91uk,1450
79
79
  emerge/_emerge/solve_interfaces/cudss_interface.py,sha256=-SjiTNIyE7iJ8Bm14Cva5e2lpJDgfiS2Mvz1Bgy-UL4,9688
80
80
  emerge/_emerge/solve_interfaces/pardiso_interface.py,sha256=iVFxToMmIzhj3hcAP-O_MDHKz82ePFIHY1us11kzUBU,15305
81
- emerge-0.5.6.dist-info/METADATA,sha256=diytWp8I8KQK9QPRZHJBLkINkULanPmXoHJfQX_bl3Y,3340
82
- emerge-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
- emerge-0.5.6.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
84
- emerge-0.5.6.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
85
- emerge-0.5.6.dist-info/RECORD,,
81
+ emerge-0.6.1.dist-info/METADATA,sha256=VbKhuz6FjO80hwmfdU6zFPpOu5_V3fikDkYyfgi0AXE,3333
82
+ emerge-0.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
+ emerge-0.6.1.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
84
+ emerge-0.6.1.dist-info/licenses/LICENSE,sha256=SuHm9Fw32RI6tylP2YCyTPITMwpueazVUZwUMFRE_zk,17856
85
+ emerge-0.6.1.dist-info/RECORD,,
File without changes