emerge 1.1.1__py3-none-any.whl → 1.2.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 CHANGED
@@ -32,7 +32,7 @@ warnings.filterwarnings(
32
32
  ############################################################
33
33
  import os
34
34
 
35
- __version__ = "1.1.1"
35
+ __version__ = "1.2.0"
36
36
 
37
37
  NTHREADS = "1"
38
38
  os.environ["EMERGE_STD_LOGLEVEL"] = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
@@ -17,7 +17,7 @@
17
17
 
18
18
  from .microwave_data import MWField
19
19
  import numpy as np
20
- from ...mth.optimized import matmul, outward_normal, cross_c
20
+ from ...mth.optimized import matmul, outward_normal
21
21
  from numba import njit, f8, c16, i8, types, prange # type: ignore, p
22
22
  from loguru import logger
23
23
  from ...const import C0, MU0, EPS0
@@ -783,32 +783,33 @@ def compute_error_single(nodes, tets, tris, edges, centers,
783
783
  pec_tris,
784
784
  k0,) -> np.ndarray:
785
785
 
786
- is_pec = np.zeros((tris.shape[1],), dtype=np.bool)
787
- is_pec[pec_tris] = True
786
+ tet_is_pec = np.zeros((tris.shape[1],), dtype=np.bool)
787
+ tet_is_pec[pec_tris] = True
788
788
 
789
789
  # CONSTANTS
790
- ntet = tets.shape[1]
791
- nedges = edges.shape[1]
792
- W0 = k0*C0
790
+ N_TETS = tets.shape[1]
791
+ N_EDGES = edges.shape[1]
793
792
  N2D = DPTS_2D.shape[1]
794
- W_VOL = DPTS_3D[0,:]
793
+ WEIGHTS_VOL = DPTS_3D[0,:]
794
+
795
+ W0 = k0*C0
795
796
  Y0 = np.sqrt(1/MU0)
796
797
 
797
798
  # INIT POSTERIORI ERROR ESTIMATE QUANTITIES
798
- alpha_t = np.zeros((ntet,), dtype=np.complex128)
799
- max_elem_size = np.zeros((ntet,), dtype=np.float64)
799
+ alpha_t = np.zeros((N_TETS,), dtype=np.complex128)
800
+ max_elem_size = np.zeros((N_TETS,), dtype=np.float64)
800
801
 
801
- Qf_face1 = np.zeros((4,N2D,ntet), dtype=np.complex128)
802
- Qf_face2 = np.zeros((4,N2D,ntet), dtype=np.complex128)
803
- Jf_face1 = np.zeros((4,3,N2D,ntet), dtype=np.complex128)
804
- Jf_face2 = np.zeros((4,3,N2D,ntet), dtype=np.complex128)
802
+ Qf_face1 = np.zeros((4,N2D,N_TETS), dtype=np.complex128)
803
+ Qf_face2 = np.zeros((4,N2D,N_TETS), dtype=np.complex128)
804
+ Jf_face1 = np.zeros((4,3,N2D,N_TETS), dtype=np.complex128)
805
+ Jf_face2 = np.zeros((4,3,N2D,N_TETS), dtype=np.complex128)
805
806
 
806
- areas_fr = np.zeros((4, N2D, ntet), dtype=np.float64)
807
- Rf_fr = np.zeros((4, ntet), dtype=np.float64)
808
- adj_tets_mat = -np.ones((4,ntet), dtype=np.int32)
807
+ areas_face_residual = np.zeros((4, N2D, N_TETS), dtype=np.float64)
808
+ Rf_face_residual = np.zeros((4, N_TETS), dtype=np.float64)
809
+ adj_tets_mat = -np.ones((4,N_TETS), dtype=np.int32)
809
810
 
810
811
  # Compute Error estimate
811
- for itet in range(ntet):
812
+ for itet in range(N_TETS):
812
813
  uinv = (1/ur[itet])*np.eye(3)
813
814
  ermat = er[itet]*np.eye(3)
814
815
  erc = er[itet]
@@ -834,7 +835,7 @@ def compute_error_single(nodes, tets, tris, edges, centers,
834
835
  # TET TRI NODE COUPLINGS
835
836
  g_node_ids = tets[:, itet]
836
837
  g_edge_ids = edges[:, tet_to_field[:6, itet]]
837
- g_tri_ids = tris[:, tet_to_field[6:10, itet]-nedges]
838
+ g_tri_ids = tris[:, tet_to_field[6:10, itet]-N_EDGES]
838
839
  l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
839
840
  l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
840
841
  triids = tet_to_tri[:,itet]
@@ -842,17 +843,16 @@ def compute_error_single(nodes, tets, tris, edges, centers,
842
843
  size_max = circum_sphere_diam(v1,v2,v3,v4)
843
844
  #size_max = np.max(edge_lengths[tet_to_edge[:,itet]])
844
845
 
845
- Volume = compute_volume(vertices[0,:], vertices[1,:], vertices[2,:])
846
-
846
+ TET_VOLUME = compute_volume(vertices[0,:], vertices[1,:], vertices[2,:])
847
847
  Rt = size_max
848
+
848
849
  # Efield
849
850
  Ef = Efield[tet_to_field[:,itet]]
850
851
 
851
852
  # Qt term
852
- Qt = Volume*EPS0*np.sum(W_VOL*compute_div(intpts, vertices, Ef, l_edge_ids, l_tri_ids, ermat), axis=0)
853
+ Qt = TET_VOLUME*EPS0*np.sum(WEIGHTS_VOL*compute_div(intpts, vertices, Ef, l_edge_ids, l_tri_ids, ermat), axis=0)
853
854
 
854
855
  # Jt term
855
-
856
856
  Rv1 = compute_curl_curl(vertices, Ef, l_edge_ids, l_tri_ids, uinv)
857
857
  Rv2 = -k0**2*(ermat @ compute_field(intpts, vertices, Ef, l_edge_ids, l_tri_ids))
858
858
  Rv = 1*Rv2
@@ -860,11 +860,11 @@ def compute_error_single(nodes, tets, tris, edges, centers,
860
860
  Rv[1,:] += Rv1[1] # Y-component
861
861
  Rv[2,:] += Rv1[2] # Z-component
862
862
 
863
- Rv[0,:] = Rv[0,:]*W_VOL
864
- Rv[1,:] = Rv[1,:]*W_VOL
865
- Rv[2,:] = Rv[2,:]*W_VOL
863
+ Rv[0,:] = Rv[0,:]*WEIGHTS_VOL
864
+ Rv[1,:] = Rv[1,:]*WEIGHTS_VOL
865
+ Rv[2,:] = Rv[2,:]*WEIGHTS_VOL
866
866
 
867
- Jt = -Volume*np.sum(1/(1j*W0*MU0) * Rv, axis=1)
867
+ Jt = -TET_VOLUME*np.sum(1/(1j*W0*MU0) * Rv, axis=1)
868
868
 
869
869
  Gt = (1j*W0*np.exp(-1j*k0*Rt)/(4*np.pi*Rt))
870
870
  alpha_t[itet] = - Gt/(erc*EPS0) * Qt*Qt - Gt*urc*MU0 * np.sum(Jt*Jt)
@@ -892,7 +892,7 @@ def compute_error_single(nodes, tets, tris, edges, centers,
892
892
  for iface in range(4):
893
893
  tri_index = triids[iface]
894
894
 
895
- pec_face = is_pec[tri_index]
895
+ pec_face = tet_is_pec[tri_index]
896
896
 
897
897
  i1, i2, i3 = tris[:, tri_index]
898
898
 
@@ -911,8 +911,8 @@ def compute_error_single(nodes, tets, tris, edges, centers,
911
911
  l3 = np.linalg.norm(n3-n2)
912
912
  Rf = np.max(np.array([l1, l2, l3]))
913
913
  Rf = diam_circum_circle(n1,n2,n3)
914
- Rf_fr[iface,itet] = Rf
915
- areas_fr[iface, :, itet] = area
914
+ Rf_face_residual[iface,itet] = Rf
915
+ areas_face_residual[iface, :, itet] = area
916
916
 
917
917
  adj_tets = [int(tri_to_tet[j,triids[iface]]) for j in range(2)]
918
918
  adj_tets = [num for num in adj_tets if num not in (itet, -1234)]
@@ -943,7 +943,7 @@ def compute_error_single(nodes, tets, tris, edges, centers,
943
943
  adj_tets_mat[iface,itet] = itet_adj
944
944
 
945
945
  # Compute 2D Gauss quadrature weight matrix
946
- fWs = np.empty_like(areas_fr, dtype=np.float64)
946
+ fWs = np.empty_like(areas_face_residual, dtype=np.float64)
947
947
  for i in range(N2D):
948
948
  fWs[:,i,:] = DPTS_2D[0,i]
949
949
 
@@ -952,16 +952,16 @@ def compute_error_single(nodes, tets, tris, edges, centers,
952
952
  Jf_delta = Jf_face1 - Jf_face2
953
953
 
954
954
  # Perform Gauss-Quadrature integration (4, NTET)
955
- Qf_int = np.sum(Qf_delta*areas_fr*fWs, axis=1)
956
- Jf_int_x = np.sum(Jf_delta[:,0,:,:]*areas_fr*fWs, axis=1)
957
- Jf_int_y = np.sum(Jf_delta[:,1,:,:]*areas_fr*fWs, axis=1)
958
- Jf_int_z = np.sum(Jf_delta[:,2,:,:]*areas_fr*fWs, axis=1)
955
+ Qf_int = np.sum(Qf_delta*areas_face_residual*fWs, axis=1)
956
+ Jf_int_x = np.sum(Jf_delta[:,0,:,:]*areas_face_residual*fWs, axis=1)
957
+ Jf_int_y = np.sum(Jf_delta[:,1,:,:]*areas_face_residual*fWs, axis=1)
958
+ Jf_int_z = np.sum(Jf_delta[:,2,:,:]*areas_face_residual*fWs, axis=1)
959
959
 
960
- Gf = (1j*W0*np.exp(-1j*k0*Rf_fr)/(4*np.pi*Rf_fr))
960
+ Gf = (1j*W0*np.exp(-1j*k0*Rf_face_residual)/(4*np.pi*Rf_face_residual))
961
961
  alpha_Df = - Gf/(er*EPS0)*(Qf_int*Qf_int) - Gf*(ur*MU0) * (Jf_int_x*Jf_int_x + Jf_int_y*Jf_int_y + Jf_int_z*Jf_int_z)
962
962
 
963
- alpha_Nf = np.zeros((4, ntet), dtype=np.complex128)
964
- for it in range(ntet):
963
+ alpha_Nf = np.zeros((4, N_TETS), dtype=np.complex128)
964
+ for it in range(N_TETS):
965
965
  for iface in range(4):
966
966
  it2 = adj_tets_mat[iface, it]
967
967
  if it2==-1:
@@ -973,7 +973,16 @@ def compute_error_single(nodes, tets, tris, edges, centers,
973
973
 
974
974
  return error, max_elem_size
975
975
 
976
- def compute_error_estimate(field: MWField, pec_tris: list[int]) -> np.ndarray:
976
+ def compute_error_estimate(field: MWField, pec_tris: list[int]) -> tuple[np.ndarray, np.ndarray]:
977
+ """Top level function to compute the EM error of a field solution.
978
+
979
+ Args:
980
+ field (MWField): The MWField object to analyse
981
+ pec_tris (list[int]): A list of triangles that ought to be considered PEC (non-neighbouring.)
982
+
983
+ Returns:
984
+ np.ndarray, np.ndarray: The error estimate in an (Ntet,) float array and the tetrahedral size value.
985
+ """
977
986
  mesh = field.mesh
978
987
 
979
988
  nodes = mesh.nodes
@@ -792,6 +792,7 @@ class MWField:
792
792
 
793
793
  def boundary(self,
794
794
  selection: FaceSelection) -> EHField:
795
+ """ Interpolate the field on the node coordinates of the surface."""
795
796
  nodes = self.mesh.nodes
796
797
  x = nodes[0,:]
797
798
  y = nodes[1,:]
@@ -14,6 +14,7 @@
14
14
  # You should have received a copy of the GNU General Public License
15
15
  # along with this program; if not, see
16
16
  # <https://www.gnu.org/licenses/>.
17
+
17
18
  from __future__ import annotations
18
19
  from ...mesh3d import Mesh3D
19
20
  from ...simstate import SimState
@@ -43,21 +44,43 @@ cmap_names = Literal['bgy','bgyw','kbc','blues','bmw','bmy','kgy','gray','dimgra
43
44
  'bkr','bky','coolwarm','gwv','bjy','bwy','cwr','colorwheel','isolum','rainbow','fire',
44
45
  'cet_fire','gouldian','kbgyw','cwr','CET_CBL1','CET_CBL3','CET_D1A']
45
46
 
46
- EMERGE_AMP = make_colormap(["#1F0061","#35188e","#1531ab", "#ff007b", "#ff7c51"], (0.0, 0.2, 0.4, 0.7, 0.9))
47
+ EMERGE_AMP = make_colormap(["#1F0061","#4218c0","#2849db", "#ff007b", "#ff7c51"], (0.0, 0.15, 0.3, 0.7, 0.9))
47
48
  EMERGE_WAVE = make_colormap(["#4ab9ff","#0510B2B8","#3A37466E","#CC0954B9","#ff9036"], (0.0, 0.3, 0.5, 0.7, 1.0))
48
49
 
49
- def _gen_c_cycle():
50
- colors = [
50
+
51
+ ## Cycler class
52
+
53
+ class _Cycler:
54
+ """Like itertools.cycle(iterable) but with reset(). Materializes the iterable."""
55
+ def __init__(self, iterable):
56
+ self._data = list(iterable)
57
+ self._n = len(self._data)
58
+ self._i = 0
59
+
60
+ def __iter__(self):
61
+ return self
62
+
63
+ def __next__(self):
64
+ if self._n == 0:
65
+ raise StopIteration
66
+ item = self._data[self._i]
67
+ self._i += 1
68
+ if self._i == self._n:
69
+ self._i = 0
70
+ return item
71
+
72
+ def reset(self):
73
+ self._i = 0
74
+
75
+
76
+ C_CYCLE = _Cycler([
51
77
  "#0000aa",
52
78
  "#aa0000",
53
79
  "#009900",
54
80
  "#990099",
55
81
  "#994400",
56
82
  "#005588"
57
- ]
58
- return cycle(colors)
59
-
60
- C_CYCLE = _gen_c_cycle()
83
+ ])
61
84
 
62
85
  class _RunState:
63
86
 
@@ -336,7 +359,8 @@ class PVDisplay(BaseDisplay):
336
359
  self._stop = False
337
360
  self._objs = []
338
361
  self._animate_next = False
339
- C_CYCLE = _gen_c_cycle()
362
+ self._reset_cbar()
363
+ C_CYCLE.reset()
340
364
 
341
365
  def _close_callback(self, arg):
342
366
  """The private callback function that stops the animation.
@@ -495,11 +495,11 @@ def plot_sp(f: np.ndarray | list[np.ndarray], S: list[np.ndarray] | np.ndarray,
495
495
  if isinstance(levelindicator, (int, float)) and levelindicator is not None:
496
496
  lvl = levelindicator
497
497
  fcross = hintersections(f, SdB, lvl)
498
- for fs in fcross:
498
+ for freqs in fcross:
499
499
  ax_mag.annotate(
500
- f"{str(fs)[:4]}{xunit}",
501
- xy=(fs, lvl),
502
- xytext=(fs + 0.08 * (max(f) - min(f)) / unitdivider[xunit], lvl),
500
+ f"{str(freqs)[:4]}{xunit}",
501
+ xy=(freqs, lvl),
502
+ xytext=(freqs + 0.08 * (max(f) - min(f)) / unitdivider[xunit], lvl),
503
503
  arrowprops=dict(facecolor="black", width=1, headwidth=5),
504
504
  )
505
505
  if fill_areas is not None:
@@ -541,6 +541,118 @@ def plot_sp(f: np.ndarray | list[np.ndarray], S: list[np.ndarray] | np.ndarray,
541
541
 
542
542
  return fig, ax_mag, ax_phase
543
543
 
544
+ def plot_vswr(f: np.ndarray | list[np.ndarray], S: list[np.ndarray] | np.ndarray,
545
+ swrlim=[1, 5],
546
+ xunit="GHz",
547
+ levelindicator: int | float | None = None,
548
+ fill_areas: list[tuple] | None = None,
549
+ spec_area: list[tuple[float,...]] | None = None,
550
+ labels: list[str] | None = None,
551
+ linestyles: list[str] | None = None,
552
+ colorcycle: list[int] | None = None,
553
+ filename: str | None = None,
554
+ show_plot: bool = True,
555
+ figdata: tuple | None = None) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
556
+ """Plot S-parameters in VSWR
557
+
558
+ One may provide:
559
+ - A single frequency with a single S-parameter
560
+ - A single frequency with a list of S-parameters
561
+ - A list of frequencies with a list of S-parameters
562
+
563
+ Args:
564
+ f (np.ndarray | list[np.ndarray]): Frequency vector or list of frequencies
565
+ S (list[np.ndarray] | np.ndarray): S-parameters to plot (list or single array)
566
+ swrlim (list, optional): VSWR y-axis limit. Defaults to [1, 5].
567
+ xunit (str, optional): Frequency unit. Defaults to "GHz".
568
+ levelindicator (int | float, optional): Level at which annotation arrows will be added. Defaults to None.
569
+ fill_areas (list[tuple], optional): Regions to fill (fmin, fmax). Defaults to None.
570
+ spec_area (list[tuple[float]], optional): _description_. Defaults to None.
571
+ labels (list[str], optional): A lists of labels to use. Defaults to None.
572
+ linestyles (list[str], optional): The linestyle to use (list or single string). Defaults to None.
573
+ colorcycle (list[int], optional): A list of colors to use. Defaults to None.
574
+ filename (str, optional): The filename (will automatically save). Defaults to None.
575
+ show_plot (bool, optional): If or not to show the resulting plot. Defaults to True.
576
+
577
+ """
578
+ if not isinstance(S, list):
579
+ Ss = [S]
580
+ else:
581
+ Ss = S
582
+
583
+ if not isinstance(f, list):
584
+ fs = [f for _ in Ss]
585
+ else:
586
+ fs = f
587
+
588
+ if linestyles is None:
589
+ linestyles = ['-' for _ in S]
590
+
591
+ if colorcycle is None:
592
+ colorcycle = [i for i, S in enumerate(S)]
593
+
594
+ unitdivider: dict[str, float] = {"MHz": 1e6, "GHz": 1e9, "kHz": 1e3}
595
+
596
+ fs = [f / unitdivider[xunit] for f in fs]
597
+
598
+ if figdata is None:
599
+ # Create two subplots: one for magnitude and one for phase
600
+ fig, ax_swr = plt.subplots()
601
+ fig.subplots_adjust(hspace=0.3)
602
+ else:
603
+ fig, ax_swr = figdata
604
+ maxy = 5
605
+
606
+
607
+ for f, s, ls, cid in zip(fs, Ss, linestyles, colorcycle):
608
+ # Calculate and plot magnitude in dB
609
+ SWR = np.divide((1 + abs(s)), (1 - abs(s)))
610
+ ax_swr.plot(f, SWR, label="VSWR", linestyle=ls, color=EMERGE_COLORS[cid % len(EMERGE_COLORS)])
611
+ if np.max(SWR) > maxy:
612
+ maxy = np.max(SWR)
613
+
614
+ # Annotate level indicators if specified
615
+ if isinstance(levelindicator, (int, float)) and levelindicator is not None:
616
+ lvl = levelindicator
617
+ fcross = hintersections(f, SWR, lvl)
618
+ for fa in fcross:
619
+ ax_swr.annotate(
620
+ f"{str(fa)[:4]}{xunit}",
621
+ xy=(fa, lvl),
622
+ xytext=(fa + 0.08 * (max(f) - min(f)) / unitdivider[xunit], lvl),
623
+ arrowprops=dict(facecolor="black", width=1, headwidth=5),
624
+ )
625
+
626
+
627
+ if fill_areas is not None:
628
+ for fmin, fmax in fill_areas:
629
+ f1 = fmin / unitdivider[xunit]
630
+ f2 = fmax / unitdivider[xunit]
631
+ ax_swr.fill_between([f1, f2], swrlim[0], swrlim[1], color='grey', alpha= 0.2)
632
+
633
+ if spec_area is not None:
634
+ for fmin, fmax, vmin, vmax in spec_area:
635
+ f1 = fmin / unitdivider[xunit]
636
+ f2 = fmax / unitdivider[xunit]
637
+ ax_swr.fill_between([f1, f2], vmin,vmax, color='red', alpha=0.2)
638
+
639
+ # Configure magnitude plot (ax_swr)
640
+ fmin = min([min(f) for f in fs])
641
+ fmax = max([max(f) for f in fs])
642
+ ax_swr.set_ylabel("VSWR")
643
+ ax_swr.set_xlabel(f"Frequency ({xunit})")
644
+ ax_swr.axis([fmin, fmax, swrlim[0], max(maxy*1.1,swrlim[1])]) # type: ignore
645
+ ax_swr.xaxis.set_minor_locator(tck.AutoMinorLocator(2))
646
+ ax_swr.yaxis.set_minor_locator(tck.AutoMinorLocator(2))
647
+
648
+ if labels is not None:
649
+ ax_swr.legend(labels)
650
+ if show_plot:
651
+ plt.show()
652
+ if filename is not None:
653
+ fig.savefig(filename)
654
+
655
+ return fig, ax_swr
544
656
 
545
657
  def plot_ff(
546
658
  theta: np.ndarray | list[np.ndarray],
@@ -317,12 +317,14 @@ class Simulation:
317
317
 
318
318
  def check_version(self, target_version: str, *, log: bool = False) -> None:
319
319
  """
320
- Ensure the script targets an EMerge version compatible with the current runtime.
320
+ Ensure the script targets an EMerge major.minor compatible with the current runtime.
321
+
322
+ Only major.minor is enforced. Patch differences are ignored.
321
323
 
322
324
  Parameters
323
325
  ----------
324
326
  target_version : str
325
- The EMerge version this script was written for (e.g. "1.4.0").
327
+ The EMerge version this script was written for (e.g. "1.4.0" or "1.4").
326
328
  log : bool, optional
327
329
  If True and a `logger` is available, emit a single WARNING with the same
328
330
  message as the exception. Defaults to False.
@@ -330,40 +332,50 @@ class Simulation:
330
332
  Raises
331
333
  ------
332
334
  VersionError
333
- If the script's target version differs from the running EMerge version.
335
+ If the script's target major.minor differs from the running EMerge major.minor.
334
336
  """
335
337
  try:
336
338
  from packaging.version import Version as _V
337
- v_script = _V(target_version)
338
- v_runtime = _V(__version__)
339
- newer = v_script > v_runtime
340
- older = v_script < v_runtime
339
+ def _mm(v: str):
340
+ pv = _V(v)
341
+ return (pv.major, pv.minor)
342
+ script_mm = _mm(target_version)
343
+ runtime_mm = _mm(__version__)
344
+ newer = script_mm > runtime_mm
345
+ older = script_mm < runtime_mm
341
346
  except Exception:
342
- def _parse(v: str):
347
+ def _parse_mm(v: str):
348
+ parts = v.split(".")
343
349
  try:
344
- return tuple(int(p) for p in v.split("."))
350
+ major = int(parts[0])
351
+ minor = int(parts[1]) if len(parts) > 1 else 0
352
+ return (major, minor)
345
353
  except Exception:
346
- # Last-resort: compare as strings to avoid crashing the check itself
347
- return tuple(v.split("."))
348
- v_script = _parse(target_version)
349
- v_runtime = _parse(__version__)
350
- newer = v_script > v_runtime
351
- older = v_script < v_runtime
354
+ # Fallback: compare as strings to avoid crashing the check itself
355
+ major = parts[0] if parts else "0"
356
+ minor = parts[1] if len(parts) > 1 else "0"
357
+ return (str(major), str(minor))
358
+ script_mm = _parse_mm(target_version)
359
+ runtime_mm = _parse_mm(__version__)
360
+ newer = script_mm > runtime_mm
361
+ older = script_mm < runtime_mm
352
362
 
353
363
  if not newer and not older:
354
- return # exact match
364
+ return # major.minor match
355
365
 
356
366
  if newer:
357
367
  msg = (
358
- f"Script targets EMerge {target_version}, but runtime is {__version__}. "
359
- "The script may rely on features added after your installed version. "
368
+ f"Script targets EMerge {target_version} (major.minor {script_mm[0]}.{script_mm[1]}), "
369
+ f"but runtime is {__version__} (major.minor {runtime_mm[0]}.{runtime_mm[1]}). "
370
+ "The script may rely on features added after your installed major.minor. "
360
371
  "Recommended: upgrade EMerge (`pip install --upgrade emerge`). "
361
372
  "If you know the script is compatible, you may remove this check."
362
373
  )
363
374
  else: # older
364
375
  msg = (
365
- f"Script targets EMerge {target_version}, but runtime is {__version__}. "
366
- "APIs may have changed since the targeted version. "
376
+ f"Script targets EMerge {target_version} (major.minor {script_mm[0]}.{script_mm[1]}), "
377
+ f"but runtime is {__version__} (major.minor {runtime_mm[0]}.{runtime_mm[1]}). "
378
+ "APIs may have changed since the targeted major.minor. "
367
379
  "Recommended: update the script for the current EMerge, or run a matching older release. "
368
380
  "If you know the script is compatible, you may remove this check."
369
381
  )
@@ -700,9 +712,7 @@ class SimulationBeta(Simulation):
700
712
 
701
713
 
702
714
  def _reset_mesh(self):
703
- #gmsh.clear()
704
715
  gmsh.model.mesh.clear()
705
-
706
716
  self.mw.reset(_reset_bc = False)
707
717
  self.state.reset_mesh()
708
718
 
@@ -716,6 +726,7 @@ class SimulationBeta(Simulation):
716
726
  growth_rate: float = 2,
717
727
  minimum_refinement_percentage: float = 20.0,
718
728
  error_field_inclusion_percentage: float = 60.0,
729
+ minimum_steps: int = 1,
719
730
  frequency: float = None,
720
731
  show_mesh: bool = False) -> SimulationDataset:
721
732
  """ A beta-version of adaptive mesh refinement.
@@ -735,6 +746,7 @@ class SimulationBeta(Simulation):
735
746
  growth_rate (float, optional): The mesh size growth rate. Defaults to 3.0.
736
747
  minimum_refinement_percentage (float, optional): The minimum mesh size increase . Defaults to 15.0.
737
748
  error_field_inclusion_percentage (float, optional): A percentage of tet elements to be included for refinement. Defaults to 5.0.
749
+ minimum_steps (int, optional): The minimum number of adaptive steps to execute. Defaults to 1.
738
750
  frequency (float, optional): The refinement frequency. Defaults to None.
739
751
  show_mesh (bool, optional): If the intermediate meshes should be shown (freezes simulation). Defaults to False
740
752
 
@@ -742,7 +754,6 @@ class SimulationBeta(Simulation):
742
754
  SimulationDataset: _description_
743
755
  """
744
756
  from .physics.microwave.adaptive_mesh import select_refinement_indices, reduce_point_set, compute_convergence
745
- from collections import defaultdict
746
757
 
747
758
  max_freq = np.max(self.mw.frequencies)
748
759
 
@@ -758,6 +769,7 @@ class SimulationBeta(Simulation):
758
769
 
759
770
  self.state.stash()
760
771
 
772
+ # Coefficients for the refinement ratio calculation.
761
773
  a0 = 0.26
762
774
  c0 = 0.75
763
775
  x0 = 12
@@ -765,6 +777,7 @@ class SimulationBeta(Simulation):
765
777
  b0 = np.tan((c0-a0)/q0)/x0
766
778
  q0 = (0.8-a0)*2/np.pi
767
779
 
780
+
768
781
  for step in range(1,max_steps+1):
769
782
 
770
783
  self.data.sim.new(iter_step=step)
@@ -776,7 +789,7 @@ class SimulationBeta(Simulation):
776
789
  Smat_new = data.scalar[-1].Sp
777
790
  S_matrices.append(Smat_new)
778
791
 
779
- if step > 1:
792
+ if step > minimum_steps:
780
793
  S0 = S_matrices[-2]
781
794
  S1 = S_matrices[-1]
782
795
  conv_complex, conv_mag, conv_phase = compute_convergence(S0, S1)
@@ -787,7 +800,7 @@ class SimulationBeta(Simulation):
787
800
  else:
788
801
  passed = 0
789
802
 
790
- if passed >= min_refined_passes:
803
+ if passed >= min_refined_passes and step > minimum_steps:
791
804
  logger.info(f'Adaptive mesh refinement successfull with {self.mesh.n_tets} tetrahedra.')
792
805
  break
793
806
 
@@ -800,7 +813,7 @@ class SimulationBeta(Simulation):
800
813
  np_percentage = npts/self.mesh.n_tets * 100
801
814
 
802
815
  refinement_ratio = (a0 + np.arctan(b0*np_percentage)*q0)
803
- #calc_refinement_ratio(refinement_throttle, point_percentage)
816
+
804
817
  logger.info(f'Adding {npts} refinement points with a ratio: {refinement_ratio}')
805
818
 
806
819
  # tet_nodes = defaultdict(lambda: 1000.)
@@ -832,15 +845,13 @@ class SimulationBeta(Simulation):
832
845
  self.mesher._amr_ratios = self.mesher._amr_ratios[new_ids]
833
846
  self.mesher._amr_new = self.mesher._amr_new[new_ids]
834
847
 
835
- def clip(value: float):
836
- return max(1.3, value)
837
-
838
848
  logger.debug(f'Initial refinement ratio: {refinement_ratio}')
839
849
 
840
850
  over = False
841
851
  under = False
842
852
  throttle = 1.0
843
853
 
854
+ # Mesh refinement loop. Only escapes if the mesh refined a certain set percentage.
844
855
  while True:
845
856
 
846
857
  if over and under:
@@ -91,7 +91,7 @@ class CuDSSInterface:
91
91
  self._config = cudss.config_create()
92
92
  self._data = cudss.data_create(self._handle)
93
93
 
94
- self.MTYPE = cudss.MatrixType.SYMMETRIC
94
+ self.MTYPE = cudss.MatrixType.GENERAL
95
95
  self.MVIEW = cudss.MatrixViewType.FULL
96
96
  self.RALG = cudss.AlgType.ALG_DEFAULT
97
97
  self.VTYPE = CudaDataType.CUDA_R_64F
emerge/plot.py CHANGED
@@ -1 +1 @@
1
- from ._emerge.plot.simple_plots import smith, plot_sp, plot, plot_ff, plot_ff_polar
1
+ from ._emerge.plot.simple_plots import smith, plot_sp, plot, plot_ff, plot_ff_polar, plot_vswr
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 1.1.1
3
+ Version: 1.2.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
@@ -1,9 +1,9 @@
1
- emerge/__init__.py,sha256=3Z4-1rWSfV3s94VD3u2rtEq2N0OY7F9UWr4Rxjtv9dY,3495
1
+ emerge/__init__.py,sha256=rKWaHi7SzRqoL7Jdp-vdGWY3OeXc-0drfgAUfJPaS8A,3495
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
5
  emerge/lib.py,sha256=VtlMP9yd1OSU_c3IQD75pZgO25Hf8fG6JyHf6IqRvcY,28642
6
- emerge/plot.py,sha256=AH2D9rKeWUXlSOlh-pUUfLt0oxVLcqF_piki-BmPEg0,83
6
+ emerge/plot.py,sha256=8yQpAgEZoaTR-2_aU2fF2jDM5YOJbRZ2stn9nP-Pcn8,94
7
7
  emerge/pyvista.py,sha256=-Ht2YcZYsh8-dici5ZPNAWwsis6uz5wNj8n8mxv5fog,42
8
8
  emerge/_emerge/__init__.py,sha256=aidfiILy33dt3VyiZ2mgtA87mq-WQ5pXItZUE5wR5ws,703
9
9
  emerge/_emerge/bc.py,sha256=ngKXwIPDfeghpctS6XLeNNtSJ6m1giHP6J25SVQ_w8g,8380
@@ -24,7 +24,7 @@ emerge/_emerge/periodic.py,sha256=dUuWqjlDR8mHtQR3ecINP2FFjJJ0cKg0blOVZ0PCcAo,12
24
24
  emerge/_emerge/plot.py,sha256=cf1I9mj7EIUJcq8vmANlUkqoV6QqVaJaP-zlC-T9E18,8041
25
25
  emerge/_emerge/selection.py,sha256=bE7qHk08RryqHwsdqw2ABQviIsDjL0RmwpxT1NF9pr8,21899
26
26
  emerge/_emerge/settings.py,sha256=4Mlo3T6UVGzhTB838833_dwvXYLGKNIEHQ4fr9hxHd8,5335
27
- emerge/_emerge/simmodel.py,sha256=bKAiL0q6hdiHYqmxlPTS9AiJiTg0RopbnJx1Of3Y8FM,35250
27
+ emerge/_emerge/simmodel.py,sha256=QqAFHRolidk2PZhkJn3NLFvlU3opDNs5UVgnv68A0ZE,36086
28
28
  emerge/_emerge/simstate.py,sha256=n_iOz3L9lZ0oD4CBb8cdHXOoaoyx96kcsFKwSQWVxlk,3630
29
29
  emerge/_emerge/simulation_data.py,sha256=W8wTSTqGNZIwnpBuk08I7e4GEXXAbnfoYEWHnhzHvyM,14723
30
30
  emerge/_emerge/solver.py,sha256=ID387uKRYYZaEHOAwMCwt0esmdhmOn7VhH-2KD8tjtc,52993
@@ -55,10 +55,10 @@ emerge/_emerge/mth/pairing.py,sha256=i8bBvTeMmzgF0JdiDNJiTXxx913x4f10777pzD6FJo0
55
55
  emerge/_emerge/physics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  emerge/_emerge/physics/microwave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  emerge/_emerge/physics/microwave/adaptive_freq.py,sha256=aWhijhCVAbnuwkru-I1AaRdY20uyozf6OWRIh9r2ijg,9786
58
- emerge/_emerge/physics/microwave/adaptive_mesh.py,sha256=_fgXf5h8T3x97fNK5BGWM1vCl0RkKVtE5W_b8s1LRu8,37733
58
+ emerge/_emerge/physics/microwave/adaptive_mesh.py,sha256=kBPLGaTl0ARGoDtEPmmC9oh3dAuoGmIaPNzCcqDGsJo,38312
59
59
  emerge/_emerge/physics/microwave/microwave_3d.py,sha256=Wpf8SSCmlDAxJq_1Xb2u-vm1sugam8qNqFtEW5Z5JKM,51871
60
60
  emerge/_emerge/physics/microwave/microwave_bc.py,sha256=nJvoBHXoZLfrG5YEpku2_rEnW33VD40H54FYrZwcufQ,50701
61
- emerge/_emerge/physics/microwave/microwave_data.py,sha256=ZynDIQH8plfMQCFLxqUKETOLJXV9nfgDo-paRMVR2Jk,53846
61
+ emerge/_emerge/physics/microwave/microwave_data.py,sha256=zG-iAH_Kk_z58Oe-lpSh-GTNv6IYmFEv52qkPMNMN9Y,53923
62
62
  emerge/_emerge/physics/microwave/periodic.py,sha256=wYSUgLFVtCLqSG3EDKoCDRU93iPUzBdXzVRdHTRmbpI,3000
63
63
  emerge/_emerge/physics/microwave/port_functions.py,sha256=d-W1D-7P05MfXdOs7WlhPi_RqlSpC0HkYU6yl3GrxgE,2173
64
64
  emerge/_emerge/physics/microwave/sc.py,sha256=WZvoPhmHkfEv619RhmN09sXDBV0ryTqybwErA8Rc7lU,4735
@@ -74,24 +74,24 @@ emerge/_emerge/physics/microwave/assembly/robin_abc_order2.py,sha256=HRGe6ezRVfc
74
74
  emerge/_emerge/physics/microwave/assembly/robinbc.py,sha256=uaaxlw7Vh-6ni5Gaz07WzpUsRvZ0hAru9E2wnNPp_fY,17614
75
75
  emerge/_emerge/plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  emerge/_emerge/plot/display.py,sha256=TQLlKb-LkaG5ZOSLfxp9KXPlZPRFTxNj1LhVQ-Lp1-s,18476
77
- emerge/_emerge/plot/simple_plots.py,sha256=szIpmQmO8o6ubzB_E3zTJfEx16mJ3-OXrMYdD_wjAcs,25256
77
+ emerge/_emerge/plot/simple_plots.py,sha256=OkZMESJdj9a9L9nmnI4AtnJc-si39422INcerHKXt9k,29784
78
78
  emerge/_emerge/plot/matplotlib/mpldisplay.py,sha256=e8V6EhGdCW7nRkSFvjHCcRO5uR-FcD0yHQ1nxPQCbp4,8674
79
79
  emerge/_emerge/plot/pyvista/__init__.py,sha256=CPclatEu6mFnJZzCQk09g6T6Fh20WTbiLAJGSwAnPXU,30
80
80
  emerge/_emerge/plot/pyvista/cmap_maker.py,sha256=_GVXYdtXpJwAO9O-Iekjzn6YcR0MVT8LNh12nqvF2IA,2498
81
- emerge/_emerge/plot/pyvista/display.py,sha256=e-vqbaEEgf2UnUOWOpxqEjz4V3i1-cIyMOSiBpE0-WI,45911
81
+ emerge/_emerge/plot/pyvista/display.py,sha256=Iz5JnZtcmH4voImUCNRVW6Y3LWKdQ96uIgwLFHun3vo,46411
82
82
  emerge/_emerge/plot/pyvista/display_settings.py,sha256=gV5hjRGEAl3oQeBPobas6b6JzYfMFrXIGtVSaeml4N0,1074
83
83
  emerge/_emerge/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  emerge/_emerge/projects/_gen_base.txt,sha256=DqQz36PZg6v1ovQjHvPjd0t4AIbmikZdb9dmrNYsK3w,598
85
85
  emerge/_emerge/projects/_load_base.txt,sha256=bHsZ4okxa9uu8qP4UOxSAeIQzuwpRtN0i71rg8wuqMA,473
86
86
  emerge/_emerge/projects/generate_project.py,sha256=TNw-0SpLc82MBq0bd9hB_yqvBZCgmuPonCBsHTp91uk,1450
87
- emerge/_emerge/solve_interfaces/cudss_interface.py,sha256=D6GVQLgTzRw4ec2MkgNhsSWrkLYMhvqljCyE6qtNQ3o,10599
87
+ emerge/_emerge/solve_interfaces/cudss_interface.py,sha256=ETvbaFydZ-yMWJAFl7wzQM02fh0BL1qGY9YxQaRpdhc,10597
88
88
  emerge/_emerge/solve_interfaces/pardiso_interface.py,sha256=iVFxToMmIzhj3hcAP-O_MDHKz82ePFIHY1us11kzUBU,15305
89
89
  emerge/beta/dxf.py,sha256=Bw4lVk0TquOgCxTZV23BZN7PrgqxBrMZxbHV1waC5U0,50
90
90
  emerge/materials/__init__.py,sha256=Z9tu3m_nqj6F9I-FwoVoN0vCTYUlFesH3KxJ38wkZck,19
91
91
  emerge/materials/isola.py,sha256=kSDxHJZVn2CcanoUjlwRVKIPvadRbBybURTdIHWx728,18660
92
92
  emerge/materials/rogers.py,sha256=4u6ma_XQdXGKWE3WsFkaMTamCQNo9kTYSTU8S1gCAYU,3388
93
- emerge-1.1.1.dist-info/METADATA,sha256=UazYGroEef4uawRCEKbPa3ZYKj0V81TzYdKf253VLfs,3614
94
- emerge-1.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
95
- emerge-1.1.1.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
96
- emerge-1.1.1.dist-info/licenses/LICENSE,sha256=VOCXWddrjMN5j7TvnSAOh1Dx7jkugdwq9Lqhycf5inc,17852
97
- emerge-1.1.1.dist-info/RECORD,,
93
+ emerge-1.2.0.dist-info/METADATA,sha256=Er2aZJ0A9j8xxNyKnWwhouNSjSUjMJ_Te08kPIxXWaQ,3614
94
+ emerge-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
95
+ emerge-1.2.0.dist-info/entry_points.txt,sha256=8rFvAXticpKg4OTC8JEvAksnduW72KIEskCGG9XnFf8,43
96
+ emerge-1.2.0.dist-info/licenses/LICENSE,sha256=VOCXWddrjMN5j7TvnSAOh1Dx7jkugdwq9Lqhycf5inc,17852
97
+ emerge-1.2.0.dist-info/RECORD,,
File without changes