emerge 0.5.2__py3-none-any.whl → 0.5.4__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 (35) hide show
  1. emerge/__init__.py +2 -2
  2. emerge/_emerge/__init__.py +1 -26
  3. emerge/_emerge/_cache_check.py +46 -0
  4. emerge/_emerge/bc.py +3 -12
  5. emerge/_emerge/const.py +5 -0
  6. emerge/_emerge/elements/nedleg2.py +2 -2
  7. emerge/_emerge/geo/pcb.py +110 -13
  8. emerge/_emerge/geo/pcb_tools/calculator.py +2 -2
  9. emerge/_emerge/geometry.py +1 -1
  10. emerge/_emerge/logsettings.py +29 -13
  11. emerge/_emerge/material.py +4 -0
  12. emerge/_emerge/mesh3d.py +9 -9
  13. emerge/_emerge/mth/integrals.py +1 -1
  14. emerge/_emerge/mth/pairing.py +1 -2
  15. emerge/_emerge/periodic.py +1 -1
  16. emerge/_emerge/physics/microwave/adaptive_freq.py +1 -5
  17. emerge/_emerge/physics/microwave/assembly/assembler.py +62 -39
  18. emerge/_emerge/physics/microwave/assembly/curlcurl.py +1 -8
  19. emerge/_emerge/physics/microwave/microwave_3d.py +33 -26
  20. emerge/_emerge/physics/microwave/microwave_bc.py +97 -27
  21. emerge/_emerge/physics/microwave/microwave_data.py +3 -5
  22. emerge/_emerge/physics/microwave/sc.py +26 -26
  23. emerge/_emerge/physics/microwave/simjob.py +8 -3
  24. emerge/_emerge/selection.py +1 -1
  25. emerge/_emerge/simmodel.py +12 -9
  26. emerge/_emerge/simulation_data.py +5 -1
  27. emerge/_emerge/solve_interfaces/cudss_interface.py +238 -0
  28. emerge/_emerge/solver.py +285 -107
  29. emerge/cli.py +1 -1
  30. emerge/lib.py +54 -40
  31. {emerge-0.5.2.dist-info → emerge-0.5.4.dist-info}/METADATA +15 -8
  32. {emerge-0.5.2.dist-info → emerge-0.5.4.dist-info}/RECORD +35 -32
  33. {emerge-0.5.2.dist-info → emerge-0.5.4.dist-info}/licenses/LICENSE +39 -0
  34. {emerge-0.5.2.dist-info → emerge-0.5.4.dist-info}/WHEEL +0 -0
  35. {emerge-0.5.2.dist-info → emerge-0.5.4.dist-info}/entry_points.txt +0 -0
@@ -27,6 +27,8 @@ from dataclasses import dataclass
27
27
  from collections import defaultdict
28
28
  from ...bc import BoundaryCondition, BoundaryConditionSet, Periodic
29
29
  from ...periodic import PeriodicCell, HexCell, RectCell
30
+ from ...material import Material
31
+ from ...const import Z0, C0, PI, EPS0, MU0
30
32
 
31
33
  class MWBoundaryConditionSet(BoundaryConditionSet):
32
34
 
@@ -39,13 +41,28 @@ class MWBoundaryConditionSet(BoundaryConditionSet):
39
41
  self.ModalPort: type[ModalPort] = self._construct_bc(ModalPort)
40
42
  self.LumpedPort: type[LumpedPort] = self._construct_bc(LumpedPort)
41
43
  self.LumpedElement: type[LumpedElement] = self._construct_bc(LumpedElement)
44
+ self.SurfaceImpedance: type[SurfaceImpedance] = self._construct_bc(SurfaceImpedance)
42
45
  self.RectangularWaveguide: type[RectangularWaveguide] = self._construct_bc(RectangularWaveguide)
43
46
  self.Periodic: type[Periodic] = self._construct_bc(Periodic)
44
47
  self.FloquetPort: type[FloquetPort] = self._construct_bc(FloquetPort)
45
48
 
46
49
  self._cell: PeriodicCell | None = None
47
50
 
48
- def get_type(self, bctype: Literal['PEC','ModalPort','LumpedPort','PMC','LumpedElement','RectangularWaveguide','Periodic','FloquetPort']) -> FaceSelection:
51
+ def get_conductors(self) -> list[BoundaryCondition]:
52
+ """Returns a list of all boundary conditions that ought to be considered as a "conductor"
53
+ for the purpose of modal analyses.
54
+
55
+ Returns:
56
+ list[BoundaryCondition]: All conductor like boundary conditions
57
+ """
58
+ bcs = self.oftype(PEC)
59
+ for bc in self.oftype(SurfaceImpedance):
60
+ if bc.material.cond > 1e3:
61
+ bcs.append(bc)
62
+
63
+ return bcs
64
+
65
+ def get_type(self, bctype: Literal['PEC','ModalPort','LumpedPort','PMC','LumpedElement','RectangularWaveguide','Periodic','FloquetPort','SurfaceImpedance']) -> FaceSelection:
49
66
  tags = []
50
67
  for bc in self.boundary_conditions:
51
68
  if bctype in str(bc.__class__):
@@ -119,7 +136,7 @@ class RobinBC(BoundaryCondition):
119
136
  raise NotImplementedError('get_Uinc not implemented for Port class')
120
137
 
121
138
  class PortBC(RobinBC):
122
- Zvac: float = 376.730313412
139
+ Zvac: float = Z0
123
140
  def __init__(self, face: FaceSelection | GeoSurface):
124
141
  """(DO NOT USE) A generalization of the Port boundary condition.
125
142
 
@@ -165,9 +182,9 @@ class PortBC(RobinBC):
165
182
  if self.modetype(k0)=='TEM':
166
183
  return self.Zvac
167
184
  elif self.modetype(k0)=='TE':
168
- return k0*299792458/self.get_beta(k0) * 4*np.pi*1e-7
185
+ return k0*299792458/self.get_beta(k0) * MU0
169
186
  elif self.modetype(k0)=='TM':
170
- return self.get_beta(k0)/(k0*299792458*8.854187818814*1e-12)
187
+ return self.get_beta(k0)/(k0*299792458*EPS0)
171
188
  else:
172
189
  raise ValueError(f'Port mode type should be TEM, TE or TM but instead is {self.modetype(k0)}')
173
190
 
@@ -183,7 +200,7 @@ class PortBC(RobinBC):
183
200
  Returns:
184
201
  float: The mode amplitude correction factor.
185
202
  """
186
- return np.sqrt(self.Zmode(k0)/376.73031341259)
203
+ return np.sqrt(self.Zmode(k0)/Z0)
187
204
 
188
205
  @property
189
206
  def mode_number(self) -> int:
@@ -300,7 +317,7 @@ class PortMode:
300
317
 
301
318
  def set_power(self, power: complex) -> None:
302
319
  self.norm_factor = np.sqrt(1/np.abs(power))
303
- logger.info(f'Setting port mode amplitude to: {self.norm_factor} ')
320
+ logger.info(f'Setting port mode amplitude to: {self.norm_factor:.2f} ')
304
321
 
305
322
  class FloquetPort(PortBC):
306
323
  _include_stiff: bool = True
@@ -334,7 +351,7 @@ class FloquetPort(PortBC):
334
351
  self.cs = GCS
335
352
 
336
353
  def portZ0(self, k0: float | None = None) -> complex | float | None:
337
- return 376.73031341259
354
+ return Z0
338
355
 
339
356
  def get_amplitude(self, k0: float) -> float:
340
357
  return 1.0
@@ -372,7 +389,7 @@ class FloquetPort(PortBC):
372
389
  P = self.pol_p
373
390
  S = self.pol_s
374
391
 
375
- E0 = self.get_amplitude(k0)*np.sqrt(2*376.73031341259/(self.area))
392
+ E0 = self.get_amplitude(k0)*np.sqrt(2*Z0/(self.area))
376
393
  Ex = E0*(-S*np.sin(self.scan_phi) - P*np.cos(self.scan_theta)*np.cos(self.scan_phi))*phi
377
394
  Ey = E0*(S*np.cos(self.scan_phi) - P*np.cos(self.scan_theta)*np.sin(self.scan_phi))*phi
378
395
  Ez = E0*(-P*E0*np.sin(self.scan_theta))*phi
@@ -646,13 +663,13 @@ class RectangularWaveguide(PortBC):
646
663
  return self.cs._basis_inv
647
664
 
648
665
  def portZ0(self, k0: float) -> complex:
649
- return k0*299792458 * 4*np.pi*1e-7/self.get_beta(k0)
666
+ return k0*299792458 * MU0/self.get_beta(k0)
650
667
 
651
668
  def modetype(self, k0):
652
669
  return self.type
653
670
 
654
671
  def get_amplitude(self, k0: float) -> float:
655
- Zte = 376.73031341259
672
+ Zte = Z0
656
673
  amplitude= np.sqrt(self.power*4*Zte/(self.dims[0]*self.dims[1]))
657
674
  return amplitude
658
675
 
@@ -807,10 +824,10 @@ class LumpedPort(PortBC):
807
824
  Returns:
808
825
  complex: The γ-constant
809
826
  """
810
- return 1j*k0*376.730313412/self.surfZ
827
+ return 1j*k0*Z0/self.surfZ
811
828
 
812
829
  def get_Uinc(self, x_local, y_local, k0) -> np.ndarray:
813
- Emag = -1j*2*k0 * self.voltage/self.height * (376.730313412/self.surfZ)
830
+ Emag = -1j*2*k0 * self.voltage/self.height * (Z0/self.surfZ)
814
831
  return Emag*self.port_mode_3d(x_local, y_local, k0)
815
832
 
816
833
  def port_mode_3d(self,
@@ -899,16 +916,9 @@ class LumpedElement(RobinBC):
899
916
  self.Z0: Callable = impedance_function # type: ignore
900
917
  self.width: float = width # type: ignore
901
918
  self.height: float = height # type: ignore
902
-
903
- logger.info('Constructing coordinate system from normal port')
904
- self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
905
-
906
- self.vintline: Line | None = None
907
- self.v_integration = True
908
- self.iintline: Line | None = None
909
919
 
910
920
  def surfZ(self, k0: float) -> float:
911
- """The surface sheet impedance for the lumped port
921
+ """The surface sheet impedance for the lumped Element
912
922
 
913
923
  Returns:
914
924
  float: The surface sheet impedance
@@ -916,18 +926,17 @@ class LumpedElement(RobinBC):
916
926
  Z0 = self.Z0(k0*299792458/(2*np.pi))*self.width/self.height
917
927
  return Z0
918
928
 
919
-
920
- def get_basis(self) -> np.ndarray:
921
- return self.cs._basis
929
+ def get_basis(self) -> np.ndarray | None:
930
+ return None
922
931
 
923
- def get_inv_basis(self) -> np.ndarray:
924
- return self.cs._basis_inv
932
+ def get_inv_basis(self) -> np.ndarray | None:
933
+ return None
925
934
 
926
935
  def get_beta(self, k0: float) -> float:
927
936
  ''' Return the out of plane propagation constant. βz.'''
928
937
 
929
938
  return k0
930
-
939
+
931
940
  def get_gamma(self, k0: float) -> complex:
932
941
  """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
933
942
 
@@ -937,6 +946,67 @@ class LumpedElement(RobinBC):
937
946
  Returns:
938
947
  complex: The γ-constant
939
948
  """
940
- return 1j*k0*376.730313412/self.surfZ(k0)
949
+ return 1j*k0*Z0/self.surfZ(k0)
950
+
941
951
 
942
952
 
953
+ class SurfaceImpedance(RobinBC):
954
+
955
+ _include_stiff: bool = True
956
+ _include_mass: bool = False
957
+ _include_force: bool = False
958
+
959
+ def __init__(self,
960
+ face: FaceSelection | GeoSurface,
961
+ material: Material | None = None,
962
+ ):
963
+ """Generates a lumped power boundary condition.
964
+
965
+ The lumped port boundary condition assumes a uniform E-field along the "direction" axis.
966
+ The port with and height must be provided manually in meters. The height is the size
967
+ in the "direction" axis along which the potential is imposed. The width dimension
968
+ is orthogonal to that. For a rectangular face its the width and for a cyllindrical face
969
+ its the circumpherance.
970
+
971
+ Args:
972
+ face (FaceSelection, GeoSurface): The port surface
973
+ port_number (int): The port number
974
+ width (float): The port width (meters).
975
+ height (float): The port height (meters).
976
+ direction (Axis): The port direction as an Axis object (em.Axis(..) or em.ZAX)
977
+ active (bool, optional): Whether the port is active. Defaults to False.
978
+ power (float, optional): The port output power. Defaults to 1.
979
+ Z0 (float, optional): The port impedance. Defaults to 50.
980
+ """
981
+ super().__init__(face)
982
+
983
+ self.material: Material = material
984
+
985
+
986
+ def get_basis(self) -> np.ndarray | None:
987
+ return None
988
+
989
+ def get_inv_basis(self) -> np.ndarray | None:
990
+ return None
991
+
992
+ def get_beta(self, k0: float) -> float:
993
+ ''' Return the out of plane propagation constant. βz.'''
994
+
995
+ return k0
996
+
997
+ def get_gamma(self, k0: float) -> complex:
998
+ """Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
999
+
1000
+ Args:
1001
+ k0 (float): The free space propagation constant.
1002
+
1003
+ Returns:
1004
+ complex: The γ-constant
1005
+ """
1006
+ w0 = k0*C0
1007
+ sigma = self.material.cond
1008
+ rho = 1/sigma
1009
+ d_skin = (2*rho/(w0*MU0) * ((1+(w0*EPS0*rho)**2)**0.5 + rho*w0*EPS0))**0.5
1010
+ d_skin = (2*rho/(w0*MU0))**0.5
1011
+ R = rho/d_skin
1012
+ return 1j*k0*Z0/R
@@ -27,6 +27,7 @@ from ...cs import Axis, _parse_axis
27
27
  from ...selection import FaceSelection
28
28
  from ...geometry import GeoSurface
29
29
  from ...mesh3d import Mesh3D
30
+ from ...const import Z0, MU0, EPS0
30
31
 
31
32
  EMField = Literal[
32
33
  "er", "ur", "freq", "k0",
@@ -36,9 +37,6 @@ EMField = Literal[
36
37
  "mode", "beta",
37
38
  ]
38
39
 
39
- EPS0 = 8.854187818814e-12
40
- MU0 = 1.2566370612720e-6
41
-
42
40
  def arc_on_plane(ref_dir, normal, angle_range_deg, num_points=100):
43
41
  """
44
42
  Generate theta/phi coordinates of an arc on a plane.
@@ -297,7 +295,7 @@ class FarfieldData:
297
295
  logger.warning('Defaulting to normE')
298
296
  F = np.sqrt(np.abs(self.E[0,:])**2 + np.abs(self.E[1,:])**2 + np.abs(self.E[2,:])**2)
299
297
  if isotropic:
300
- F = F/np.sqrt(376.730313412/(2*np.pi))
298
+ F = F/np.sqrt(Z0/(2*np.pi))
301
299
  if dB:
302
300
  F = 20*np.log10(np.clip(np.abs(F), a_min=10**(dBfloor/20), a_max = 1e9))-dBfloor
303
301
  if rmax is not None:
@@ -662,7 +660,7 @@ class MWField:
662
660
  self.Ez = Ez.reshape(shp)
663
661
 
664
662
 
665
- constants = 1/ (-1j*2*np.pi*self.freq*(self._dur*4*np.pi*1e-7) )
663
+ constants = 1/ (-1j*2*np.pi*self.freq*(self._dur*MU0) )
666
664
  Hx, Hy, Hz = self.basis.interpolate_curl(self._field, xf, yf, zf, constants)
667
665
  ids = self.basis.interpolate_index(xf, yf, zf)
668
666
  self.er = self._der[ids].reshape(shp)
@@ -19,19 +19,21 @@ from numba_progress import ProgressBar
19
19
  from ...mesh3d import SurfaceMesh
20
20
  import numpy as np
21
21
  from loguru import logger
22
- from numba import c8, c16, f8, i8, njit, prange, typeof, f4
23
- from numba.types import Tuple as TupleType
22
+ from numba import c16, njit, prange, f8
23
+ from numba.types import Tuple as TupleType # ty: ignore
24
24
  from numba_progress.progress import ProgressBarType
25
+ from ...const import Z0
25
26
 
26
27
  LR = 0.001
28
+
27
29
  @njit(
28
- TupleType((c8[:, :], c8[:, :]))(
29
- c8[:, :],
30
- c8[:, :],
31
- f4[:, :],
32
- f4[:, :],
33
- f4[:, :],
34
- f4,
30
+ TupleType((c16[:, :], c16[:, :]))(
31
+ c16[:, :],
32
+ c16[:, :],
33
+ f8[:, :],
34
+ f8[:, :],
35
+ f8[:, :],
36
+ f8,
35
37
  ProgressBarType,
36
38
  ),
37
39
  parallel=True,
@@ -86,17 +88,15 @@ def stratton_chu_ff(Ein, Hin, vis, wns, tpout, k0, pgb):
86
88
 
87
89
  N = tpout.shape[1]
88
90
 
89
- Eout = np.zeros((3, N)).astype(np.complex64)
90
- Hout = np.zeros((3, N)).astype(np.complex64)
91
-
92
- Eoutx = np.zeros((N,)).astype(np.complex64)
93
- Eouty = np.zeros((N,)).astype(np.complex64)
94
- Eoutz = np.zeros((N,)).astype(np.complex64)
91
+ Eout = np.zeros((3, N)).astype(np.complex128)
92
+ Hout = np.zeros((3, N)).astype(np.complex128)
95
93
 
96
- Z0 = np.float32(376.73031366857)
94
+ Eoutx = np.zeros((N,)).astype(np.complex128)
95
+ Eouty = np.zeros((N,)).astype(np.complex128)
96
+ Eoutz = np.zeros((N,)).astype(np.complex128)
97
97
 
98
- Q = np.complex64(-1j * k0 / (4 * np.pi))
99
- ii = np.complex64(1j)
98
+ Q = np.complex128(-1j * k0 / (4 * np.pi))
99
+ ii = np.complex128(1j)
100
100
 
101
101
  NxHx = ny * Hz - nz * Hy
102
102
  NxHy = nz * Hx - nx * Hz
@@ -106,7 +106,7 @@ def stratton_chu_ff(Ein, Hin, vis, wns, tpout, k0, pgb):
106
106
  NxEy = nz * Ex - nx * Ez
107
107
  NxEz = nx * Ey - ny * Ex
108
108
 
109
- for j in prange(Nids):
109
+ for j in prange(Nids): # ty: ignore
110
110
  xi = vx[j]
111
111
  yi = vy[j]
112
112
  zi = vz[j]
@@ -147,7 +147,7 @@ def stratton_chu(Ein, Hin, mesh: SurfaceMesh, theta: np.ndarray, phi: np.ndarray
147
147
  areas = mesh.areas
148
148
  vis = mesh.edge_centers
149
149
 
150
- wns = np.zeros_like(vis).astype(np.float32)
150
+ wns = np.zeros_like(vis).astype(np.float64)
151
151
 
152
152
  tri_normals = mesh.normals
153
153
  tri_ids = mesh.tri_to_edge
@@ -164,12 +164,12 @@ def stratton_chu(Ein, Hin, mesh: SurfaceMesh, theta: np.ndarray, phi: np.ndarray
164
164
  tpout = np.array([theta, phi])
165
165
  with ProgressBar(total=Ntot, ncols=100, dynamic_ncols=False) as pgb:
166
166
  Eout, Hout = stratton_chu_ff(
167
- Ein.astype(np.complex64),
168
- Hin.astype(np.complex64),
169
- vis.astype(np.float32),
170
- wns.astype(np.float32),
171
- tpout.astype(np.float32),
172
- np.float32(k0),
167
+ Ein.astype(np.complex128),
168
+ Hin.astype(np.complex128),
169
+ vis.astype(np.float64),
170
+ wns.astype(np.float64),
171
+ tpout.astype(np.float64),
172
+ np.float64(k0),
173
173
  pgb,
174
174
  )
175
175
  return Eout.astype(np.complex128), Hout.astype(np.complex128)
@@ -27,7 +27,7 @@ class SimJob:
27
27
  b: np.ndarray | None,
28
28
  freq: float,
29
29
  cache_factorization: bool,
30
- B: csr_matrix = None
30
+ B: csr_matrix | None = None
31
31
  ):
32
32
 
33
33
  self.A: csr_matrix = A
@@ -88,15 +88,20 @@ class SimJob:
88
88
 
89
89
  b_active = self.b + mode
90
90
  A = self.load_if_needed('A')
91
-
91
+
92
+ aux = {
93
+ 'Active port': str(key),
94
+ }
92
95
 
93
96
  if self.has_periodic:
94
97
  P = self.load_if_needed('P')
95
98
  Pd = self.load_if_needed('Pd')
96
99
  b_active = Pd @ b_active
97
100
  A = Pd @ A @ P
101
+ aux['Periodic reduction'] = str(P.shape)
98
102
 
99
- yield A, b_active, self.solve_ids, reuse_factorization
103
+
104
+ yield A, b_active, self.solve_ids, reuse_factorization, aux
100
105
 
101
106
  reuse_factorization = True
102
107
 
@@ -536,7 +536,7 @@ class Selector:
536
536
  nx: float,
537
537
  ny: float,
538
538
  nz: float,
539
- tolerance: float = 1e-6) -> FaceSelection:
539
+ tolerance: float = 1e-8) -> FaceSelection:
540
540
  """Returns a FaceSelection for all faces that lie in a provided infinite plane
541
541
  specified by an origin plus a plane normal vector.
542
542
 
@@ -51,11 +51,9 @@ Known problems/solutions:
51
51
  --------------------------
52
52
  """
53
53
 
54
-
55
54
  class SimulationError(Exception):
56
55
  pass
57
56
 
58
-
59
57
  ############################################################
60
58
  # BASE 3D SIMULATION MODEL #
61
59
  ############################################################
@@ -93,7 +91,6 @@ class Simulation3D:
93
91
 
94
92
  self.mesh: Mesh3D = Mesh3D(self.mesher)
95
93
  self.select: Selector = Selector()
96
- self.set_loglevel(loglevel)
97
94
 
98
95
  ## STATES
99
96
  self.__active: bool = False
@@ -102,9 +99,6 @@ class Simulation3D:
102
99
 
103
100
  self.display: PVDisplay = PVDisplay(self.mesh)
104
101
 
105
- if logfile:
106
- self.set_logfile()
107
-
108
102
  self.save_file: bool = save_file
109
103
  self.load_file: bool = load_file
110
104
 
@@ -115,6 +109,10 @@ class Simulation3D:
115
109
 
116
110
  self._initialize_simulation()
117
111
 
112
+ self.set_loglevel(loglevel)
113
+ if logfile:
114
+ self.set_logfile()
115
+
118
116
  self._update_data()
119
117
 
120
118
 
@@ -286,6 +284,8 @@ class Simulation3D:
286
284
  loglevel ('DEBUG','INFO','WARNING','ERROR'): The loglevel
287
285
  """
288
286
  LOG_CONTROLLER.set_std_loglevel(loglevel)
287
+ if loglevel not in ('TRACE','DEBUG'):
288
+ gmsh.option.setNumber("General.Terminal", 0)
289
289
 
290
290
  def set_logfile(self) -> None:
291
291
  """Adds a file output for the logger."""
@@ -318,7 +318,6 @@ class Simulation3D:
318
318
 
319
319
  return None
320
320
 
321
-
322
321
  def set_periodic_cell(self, cell: PeriodicCell, excluded_faces: list[FaceSelection] | None = None):
323
322
  """Set the given periodic cell object as the simulations peridicity.
324
323
 
@@ -332,7 +331,7 @@ class Simulation3D:
332
331
  def commit_geometry(self, *geometries: GeoObject | list[GeoObject]) -> None:
333
332
  """Finalizes and locks the current geometry state of the simulation.
334
333
 
335
- The geometries may be provided (legacy behavior) but are automatically managed underwater.
334
+ The geometries may be provided (legacy behavior) but are automatically managed in the background.
336
335
 
337
336
  """
338
337
  geometries_parsed: Any = None
@@ -375,12 +374,16 @@ class Simulation3D:
375
374
  self.mesher.set_mesh_size(self.mw.get_discretizer(), self.mw.resolution)
376
375
 
377
376
  try:
377
+ gmsh.logger.start()
378
378
  gmsh.model.mesh.generate(3)
379
+ logs = gmsh.logger.get()
380
+ gmsh.logger.stop()
381
+ for log in logs:
382
+ logger.trace('[GMSH] '+log)
379
383
  except Exception:
380
384
  logger.error('GMSH Mesh error detected.')
381
385
  print(_GMSH_ERROR_TEXT)
382
386
  raise
383
-
384
387
  self.mesh.update(self.mesher._get_periodic_bcs())
385
388
  self.mesh.exterior_face_tags = self.mesher.domain_boundary_face_tags
386
389
  gmsh.model.occ.synchronize()
@@ -18,7 +18,7 @@
18
18
  from __future__ import annotations
19
19
  import numpy as np
20
20
  from loguru import logger
21
- from typing import TypeVar, Generic, Any, List, Union, Dict
21
+ from typing import TypeVar, Generic, Any, List, Union, Dict, Generator
22
22
  from collections import defaultdict
23
23
 
24
24
  T = TypeVar("T")
@@ -187,6 +187,10 @@ class DataContainer:
187
187
  self.entries.append(entry)
188
188
  return entry
189
189
 
190
+ def iterate(self) -> Generator[tuple[dict[str, float], dict[str, Any]], None, None]:
191
+ for entry in self.entries:
192
+ yield entry.vars, entry.data
193
+
190
194
  @property
191
195
  def last(self) -> DataEntry:
192
196
  """Returns the last added entry"""