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.
- emerge/__init__.py +4 -1
- emerge/_emerge/cs.py +2 -2
- emerge/_emerge/elements/ned2_interp.py +21 -26
- emerge/_emerge/elements/nedleg2.py +27 -45
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/modeler.py +2 -2
- emerge/_emerge/geo/pcb.py +4 -4
- emerge/_emerge/geo/shapes.py +37 -14
- emerge/_emerge/geometry.py +27 -1
- emerge/_emerge/howto.py +9 -9
- emerge/_emerge/material.py +1 -0
- emerge/_emerge/mesh3d.py +63 -14
- emerge/_emerge/mesher.py +7 -4
- emerge/_emerge/mth/optimized.py +30 -0
- emerge/_emerge/periodic.py +46 -16
- emerge/_emerge/physics/microwave/assembly/assembler.py +4 -21
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +23 -19
- emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +465 -0
- emerge/_emerge/physics/microwave/assembly/robinbc.py +59 -18
- emerge/_emerge/physics/microwave/microwave_3d.py +38 -186
- emerge/_emerge/physics/microwave/microwave_bc.py +101 -35
- emerge/_emerge/physics/microwave/microwave_data.py +1 -1
- emerge/_emerge/plot/pyvista/display.py +40 -7
- emerge/_emerge/plot/pyvista/display_settings.py +1 -0
- emerge/_emerge/plot/simple_plots.py +159 -27
- emerge/_emerge/projects/_gen_base.txt +2 -2
- emerge/_emerge/projects/_load_base.txt +1 -1
- emerge/_emerge/simmodel.py +22 -7
- emerge/_emerge/solve_interfaces/cudss_interface.py +44 -2
- emerge/_emerge/solve_interfaces/pardiso_interface.py +1 -0
- emerge/_emerge/solver.py +26 -19
- emerge/ext.py +4 -0
- emerge/lib.py +1 -1
- {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/METADATA +6 -4
- {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/RECORD +38 -37
- emerge/_emerge/elements/legrange2.py +0 -172
- {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/WHEEL +0 -0
- {emerge-0.5.5.dist-info → emerge-0.6.0.dist-info}/entry_points.txt +0 -0
- {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(
|
|
246
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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='
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
G =
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
ax.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
32
|
+
m.mw.run_sweep()
|
emerge/_emerge/simmodel.py
CHANGED
|
@@ -58,7 +58,7 @@ class SimulationError(Exception):
|
|
|
58
58
|
# BASE 3D SIMULATION MODEL #
|
|
59
59
|
############################################################
|
|
60
60
|
|
|
61
|
-
class
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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:
|
|
96
|
-
|
|
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),
|
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
|
-
|
|
48
|
-
|
|
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],
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
|
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,
|
|
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
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.”
|