emerge 0.5.5__py3-none-any.whl → 0.6.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.

Files changed (39) hide show
  1. emerge/__init__.py +4 -1
  2. emerge/_emerge/cs.py +2 -2
  3. emerge/_emerge/elements/ned2_interp.py +21 -26
  4. emerge/_emerge/elements/nedleg2.py +27 -45
  5. emerge/_emerge/geo/__init__.py +1 -1
  6. emerge/_emerge/geo/modeler.py +2 -2
  7. emerge/_emerge/geo/pcb.py +4 -4
  8. emerge/_emerge/geo/shapes.py +37 -14
  9. emerge/_emerge/geometry.py +27 -1
  10. emerge/_emerge/howto.py +9 -9
  11. emerge/_emerge/material.py +1 -0
  12. emerge/_emerge/mesh3d.py +63 -14
  13. emerge/_emerge/mesher.py +7 -4
  14. emerge/_emerge/mth/optimized.py +30 -0
  15. emerge/_emerge/periodic.py +46 -16
  16. emerge/_emerge/physics/microwave/assembly/assembler.py +4 -21
  17. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +23 -19
  18. emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +465 -0
  19. emerge/_emerge/physics/microwave/assembly/robinbc.py +59 -18
  20. emerge/_emerge/physics/microwave/microwave_3d.py +38 -186
  21. emerge/_emerge/physics/microwave/microwave_bc.py +101 -35
  22. emerge/_emerge/physics/microwave/microwave_data.py +1 -1
  23. emerge/_emerge/plot/pyvista/display.py +40 -7
  24. emerge/_emerge/plot/pyvista/display_settings.py +1 -0
  25. emerge/_emerge/plot/simple_plots.py +159 -27
  26. emerge/_emerge/projects/_gen_base.txt +2 -2
  27. emerge/_emerge/projects/_load_base.txt +1 -1
  28. emerge/_emerge/simmodel.py +22 -7
  29. emerge/_emerge/solve_interfaces/cudss_interface.py +44 -2
  30. emerge/_emerge/solve_interfaces/pardiso_interface.py +1 -0
  31. emerge/_emerge/solver.py +26 -19
  32. emerge/ext.py +4 -0
  33. emerge/lib.py +1 -1
  34. {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/METADATA +6 -4
  35. {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/RECORD +38 -37
  36. emerge/_emerge/elements/legrange2.py +0 -172
  37. {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/WHEEL +0 -0
  38. {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/entry_points.txt +0 -0
  39. {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
 
@@ -313,7 +313,7 @@ class Simulation3D:
313
313
  for geo in _GEOMANAGER.all_geometries():
314
314
  self.display.add_object(geo)
315
315
  if selections:
316
- [self.display.add_object(sel, color='red', opacity=0.7) for sel in selections]
316
+ [self.display.add_object(sel, color='red', opacity=0.3) for sel in selections]
317
317
  self.display.show()
318
318
 
319
319
  return None
@@ -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
  """
@@ -435,7 +435,21 @@ class Simulation3D:
435
435
  else:
436
436
  yield (dim[i_iter] for dim in dims_flat) # type: ignore
437
437
  self.mw.cache_matrices = True
438
+
439
+ def export(self, filename: str):
440
+ """Exports the model or mesh depending on the extension.
441
+
442
+ Exporting is realized by GMSH.
443
+ Supported file formats are:
444
+
445
+ 3D Model: .opt, .geo_unrolled, .brep, .xao ,.step and .iges
446
+ Mesh: .msh, .inp, .key, ._0000.rad, .celum, .cgns, .diff, .unv, .ir3, .mes, .mesh
447
+ .mail, .m, .bdf, .off, .p3d, .stl, .wrl, .vtk, .dat, .ply2, .su2, .neu, .x3d
438
448
 
449
+ Args:
450
+ filename (str): The filename
451
+ """
452
+ gmsh.write(filename)
439
453
  ############################################################
440
454
  # DEPRICATED FUNCTIONS #
441
455
  ############################################################
@@ -444,4 +458,5 @@ class Simulation3D:
444
458
  """DEPRICATED VERSION: Use .commit_geometry()
445
459
  """
446
460
  logger.warning('define_geometry() will be derpicated. Use commit_geometry() instead.')
447
- self.commit_geometry(*args)
461
+ self.commit_geometry(*args)
462
+
@@ -21,6 +21,7 @@ from nvmath import CudaDataType # ty: ignore
21
21
 
22
22
  from scipy.sparse import csr_matrix
23
23
  import numpy as np
24
+ from typing import Literal
24
25
 
25
26
  from loguru import logger
26
27
 
@@ -51,6 +52,11 @@ def _c_pointer(arry) -> int:
51
52
  ############################################################
52
53
 
53
54
  class CuDSSInterface:
55
+ """the CuDSSInterface class implements the nvmath bindings and cupy
56
+ control for EMerge.
57
+ """
58
+ AlgType = cudss.AlgType
59
+
54
60
  def __init__(self):
55
61
  self.A_cu = None
56
62
  self.b_cu = None
@@ -92,10 +98,31 @@ class CuDSSInterface:
92
98
  reorder_alg.nbytes
93
99
  )
94
100
 
95
- def set_algorithm(self, alg_type: cudss.AlgType):
96
- self.RALG = alg_type
101
+ def set_algorithm(self, alg_type: Literal['METIS','COLAMD','COLAM_BT','AMD']):
102
+ """Define fill-in reduction column permuation algorithm. The options are:
103
+
104
+ - "METIS" (Default) = NVidia's own Nested Dissection METIS sorter
105
+ - "COLAMD" = Column approximate minimum degree
106
+ - "COLAM_BT" = Column Approximate Minimum Degree Block Triangular
107
+ - "AMD" = Approximate Minimum Degree
108
+
109
+ Args:
110
+ alg_type (str): The chosen type
111
+ """
112
+ if alg_type=='METIS':
113
+ self.RALG = ALG_NEST_DISS_METIS
114
+ elif alg_type =='COLAMD':
115
+ self.RALG = ALG_COLAMD
116
+ elif alg_type == 'COLAMD_BT':
117
+ self.RALG = ALG_COLAMD_BLOCK_TRI
118
+ elif alg_type == 'AMD':
119
+ self.RALG = ALG_AMD
120
+ else:
121
+ logger.warning(f'Algorithm type {alg_type} is not of the chosen set. Ignoring setting.')
97
122
 
98
123
  def init_type(self):
124
+ """Initializes the value data type of the solver (float vs complex, single vs double).
125
+ """
99
126
  if self._PRES == 1:
100
127
  if self._COMP:
101
128
  self.c_dtype = cp.complex64
@@ -112,6 +139,11 @@ class CuDSSInterface:
112
139
  self.VTYPE = FLOAT64
113
140
 
114
141
  def submit_matrix(self, A: csr_matrix):
142
+ """Sets the given csr_matrix as the matrix to be solved.
143
+
144
+ Args:
145
+ A (csr_matrix): The csr_format matrix for the problem Ax=b
146
+ """
115
147
  self.N = A.shape[0]
116
148
 
117
149
  if np.iscomplexobj(A):
@@ -132,12 +164,21 @@ class CuDSSInterface:
132
164
  self._COL_IDS = self.A_cu.indices.astype(cp.int32)
133
165
 
134
166
  def submit_vector(self, b: np.ndarray):
167
+ """Submits the dense vector b to be solved.
168
+
169
+ Args:
170
+ b (np.ndarray): The dense vector for the problem Ax=b
171
+ """
135
172
  self.b_cu = cp.array(b).astype(self.c_dtype)
136
173
 
137
174
  def create_solvec(self):
175
+ """Initializes a solution vector that the nvmath binding can access.
176
+ """
138
177
  self.x_cu = cp.empty_like(self.b_cu)
139
178
 
140
179
  def _update_dss_data(self):
180
+ """Updates the currently defined matrix data into the existing memory.ALG_AMD
181
+ """
141
182
  cudss.matrix_set_values(self.A_cobj, _c_pointer(self._VAL))
142
183
 
143
184
 
@@ -147,6 +188,7 @@ class CuDSSInterface:
147
188
  int(self.VTYPE), int(cudss.Layout.COL_MAJOR))
148
189
 
149
190
  def _create_dss_data(self):
191
+ """Creates a new memory slot for the CSR matrix of the matrix A"""
150
192
  self.A_cobj = cudss.matrix_create_csr(
151
193
  self.N,self.N,self._NNZ,
152
194
  _c_pointer(self._ROW_START),
@@ -15,6 +15,7 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program; if not, see
17
17
  # <https://www.gnu.org/licenses/>.
18
+
18
19
  from __future__ import annotations
19
20
  import os
20
21
  import sys
emerge/_emerge/solver.py CHANGED
@@ -44,11 +44,8 @@ If so, attempt to import PyPardiso (if its installed)
44
44
  ############################################################
45
45
 
46
46
  if 'arm' not in platform.processor():
47
- try:
48
- from .solve_interfaces.pardiso_interface import PardisoInterface
49
- _PARDISO_AVAILABLE = True
50
- except ModuleNotFoundError:
51
- logger.info('Pardiso not found, defaulting to SuperLU')
47
+ from .solve_interfaces.pardiso_interface import PardisoInterface
48
+ _PARDISO_AVAILABLE = True
52
49
 
53
50
 
54
51
  ############################################################
@@ -62,7 +59,6 @@ try:
62
59
  except ModuleNotFoundError:
63
60
  logger.debug('UMFPACK not found, defaulting to SuperLU')
64
61
 
65
-
66
62
  ############################################################
67
63
  # CUDSS #
68
64
  ############################################################
@@ -72,7 +68,10 @@ try:
72
68
  _CUDSS_AVAILABLE = True
73
69
  except ModuleNotFoundError:
74
70
  pass
75
-
71
+ except ImportError as e:
72
+ logger.error('Error while importing CuDSS dependencies:')
73
+ logger.exception(e)
74
+
76
75
  ############################################################
77
76
  # SOLVE REPORT #
78
77
  ############################################################
@@ -177,7 +176,8 @@ def filter_real_modes(eigvals: np.ndarray, eigvecs: np.ndarray,
177
176
  # EIGENMODE ORTHOGONALITY CHECK #
178
177
  ############################################################
179
178
 
180
- def filter_unique_eigenpairs(eigen_values: list[complex], eigen_vectors: list[np.ndarray], tol=-3) -> tuple[list[complex], list[np.ndarray]]:
179
+ def filter_unique_eigenpairs(eigen_values: list[complex],
180
+ eigen_vectors: list[np.ndarray], tol=-3) -> tuple[list[complex], list[np.ndarray]]:
181
181
  """
182
182
  Filters eigenvectors by orthogonality using dot-product tolerance.
183
183
 
@@ -301,7 +301,8 @@ class Solver:
301
301
  Options may be ignored depending on the type of solver used."""
302
302
  pass
303
303
 
304
- def solve(self, A: csr_matrix, b: np.ndarray, precon: Preconditioner, reuse_factorization: bool = False, id: int = -1) -> tuple[np.ndarray, SolveReport]:
304
+ def solve(self, A: csr_matrix, b: np.ndarray, precon: Preconditioner,
305
+ reuse_factorization: bool = False, id: int = -1) -> tuple[np.ndarray, SolveReport]:
305
306
  raise NotImplementedError("This classes Ax=B solver method is not implemented.")
306
307
 
307
308
  def reset(self) -> None:
@@ -329,7 +330,8 @@ class EigSolver:
329
330
  def duplicate(self) -> Solver:
330
331
  return self.__class__()
331
332
 
332
- def eig(self, A: csr_matrix | csr_matrix, B: csr_matrix | csr_matrix, nmodes: int = 6, target_k0: float = 0.0, which: str = 'LM', sign: float = 1.):
333
+ def eig(self, A: csr_matrix | csr_matrix, B: csr_matrix | csr_matrix, nmodes: int = 6,
334
+ target_k0: float = 0.0, which: str = 'LM', sign: float = 1.):
333
335
  raise NotImplementedError("This classes eigenmdoe solver method is not implemented.")
334
336
 
335
337
  def reset(self) -> None:
@@ -337,7 +339,6 @@ class EigSolver:
337
339
  pass
338
340
 
339
341
 
340
-
341
342
  ############################################################
342
343
  # SORTERS #
343
344
  ############################################################
@@ -379,7 +380,9 @@ class ILUPrecon(Preconditioner):
379
380
 
380
381
  def init(self, A, b):
381
382
  logger.info("Generating ILU Preconditioner")
382
- self.ilu = sparse.linalg.spilu(A, drop_tol=1e-2, fill_factor=self.fill_factor, permc_spec='MMD_AT_PLUS_A', diag_pivot_thresh=0.001, options=self.options) # ty: ignore
383
+ self.ilu = sparse.linalg.spilu(A, drop_tol=1e-2, fill_factor=self.fill_factor, # ty: ignore
384
+ permc_spec='MMD_AT_PLUS_A', diag_pivot_thresh=0.001,
385
+ options=self.options)
383
386
  self.M = sparse.linalg.LinearOperator(A.shape, self.ilu.solve) # ty: ignore
384
387
 
385
388
 
@@ -601,7 +604,7 @@ class CuDSSSolver(Solver):
601
604
  self.fact_numb = False
602
605
 
603
606
  def solve(self, A, b, precon, reuse_factorization: bool = False, id: int = -1):
604
- logger.info(f'[{id}] Calling cuDSS Solver')
607
+ logger.info(f'[ID={id}] Calling cuDSS Solver')
605
608
 
606
609
  if self.fact_symb is False:
607
610
  logger.debug('Executing symbollic factorization')
@@ -911,7 +914,8 @@ class SolveRoutine:
911
914
  return new_routine
912
915
 
913
916
  def set_solver(self, *solvers: EMSolver | EigSolver | Solver) -> None:
914
- """Set a given Solver class instance as the main solver. Solvers will be checked on validity for the given problem.
917
+ """Set a given Solver class instance as the main solver.
918
+ Solvers will be checked on validity for the given problem.
915
919
 
916
920
  Args:
917
921
  solver (EMSolver | Solver): The solver objects
@@ -923,7 +927,8 @@ class SolveRoutine:
923
927
  self.forced_solver.append(solver)
924
928
 
925
929
  def disable(self, *solvers: EMSolver) -> None:
926
- """Disable a given Solver class instance as the main solver. Solvers will be checked on validity for the given problem.
930
+ """Disable a given Solver class instance as the main solver.
931
+ Solvers will be checked on validity for the given problem.
927
932
 
928
933
  Args:
929
934
  solver (EMSolver): The solver objects
@@ -945,7 +950,8 @@ class SolveRoutine:
945
950
  - "SI" = Single threaded
946
951
  - "MT" = Multi threaded
947
952
  - "MP" = Multi-processing,
948
- smart_search (bool, optional): Wether to use smart-search solvers for eigenmode problems. Defaults to False.
953
+ smart_search (bool, optional): Wether to use smart-search solvers
954
+ for eigenmode problems. Defaults to False.
949
955
 
950
956
  Returns:
951
957
  SolveRoutine: The same SolveRoutine object.
@@ -998,7 +1004,6 @@ class SolveRoutine:
998
1004
  return solver
999
1005
  return self.pick_solver(A,b)
1000
1006
 
1001
-
1002
1007
  def pick_solver(self, A: csr_matrix, b: np.ndarray) -> Solver:
1003
1008
  """Returns the relevant Solver object given a certain matrix and source vector
1004
1009
 
@@ -1187,7 +1192,10 @@ class SolveRoutine:
1187
1192
  end = time.time()
1188
1193
 
1189
1194
  simtime = end-start
1190
- return eigen_values, eigen_modes, SolveReport(ndof=A.shape[0], nnz=A.nnz, ndof_solve=Asel.shape[0], nnz_solve=Asel.nnz, simtime=simtime, solver=str(solver), sorter='None', precon='None')
1195
+ return eigen_values, eigen_modes, SolveReport(ndof=A.shape[0], nnz=A.nnz,
1196
+ ndof_solve=Asel.shape[0], nnz_solve=Asel.nnz,
1197
+ simtime=simtime, solver=str(solver),
1198
+ sorter='None', precon='None')
1191
1199
 
1192
1200
  def eig(self,
1193
1201
  A: csr_matrix | csr_matrix,
@@ -1234,7 +1242,6 @@ class SolveRoutine:
1234
1242
 
1235
1243
  return eigen_values, sols, SolveReport(ndof=A.shape[0], nnz=A.nnz, ndof_solve=Asel.shape[0], nnz_solve=Asel.nnz, simtime=simtime, solver=str(solver), sorter='None', precon='None')
1236
1244
 
1237
-
1238
1245
  class AutomaticRoutine(SolveRoutine):
1239
1246
  """ Defines the Automatic Routine for EMerge.
1240
1247
  """
emerge/ext.py ADDED
@@ -0,0 +1,4 @@
1
+
2
+ from ._emerge.solver import Solver, EigSolver, Preconditioner, Sorter
3
+ from ._emerge.geometry import GeoVolume, GeoSurface, GeoEdge, GeoPoint, _FacePointer
4
+ from ._emerge.physics.microwave.microwave_bc import RobinBC, PortBC
emerge/lib.py CHANGED
@@ -29,7 +29,7 @@ GREY = "#bfbfbf"
29
29
  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
- MET_COPPER = Material(cond=5.8e7, color="#62290c", opacity=0.5)
32
+ MET_COPPER = Material(cond=5.8e7, color="#62290c", opacity=1.0)
33
33
  MET_GOLD = Material(cond=4.10e7, color=GREY, 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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 0.5.5
3
+ Version: 0.6.0
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
@@ -42,7 +42,9 @@ pip install emerge
42
42
  On MacOS and Linux you can install it with the very fast UMFPACK through scikit-umfpack
43
43
 
44
44
  ```
45
- pip install emerge[scikit-umfpack]
45
+ brew install swig suite-sparse #MacOS
46
+ sudo apt-get install libsuitesparse-dev #Linux
47
+ pip install emerge[umfpack]
46
48
  ```
47
49
 
48
50
  ### Experimental
@@ -62,7 +64,7 @@ import emerge as em
62
64
  def main():
63
65
  # setup simulation
64
66
 
65
- model.mw.frequency_domain(True, ..., multi_processing=True)
67
+ model.mw.run_sweep(True, ..., multi_processing=True)
66
68
 
67
69
  if __name__ == "__main__":
68
70
  main()
@@ -93,4 +95,4 @@ First time runs will be very slow because Numba needs to generate local C-compil
93
95
 
94
96
  ## Third Party License Notice
95
97
 
96
- “This package depends on Intel® Math Kernel Library (MKL), which is licensed separately under the Intel Simplified Software License (October 2022). Installing with pip will fetch the MKL wheel and prompt you to accept that licence.”
98
+ “This package depends on Intel® Math Kernel Library (MKL), which is licensed separately under the Intel Simplified Software License (October 2022). Installing with pip will fetch the MKL wheel and prompt you to accept that licence.”