emerge 1.0.7__py3-none-any.whl → 1.1.1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (33) hide show
  1. emerge/__init__.py +15 -3
  2. emerge/_emerge/const.py +2 -1
  3. emerge/_emerge/elements/ned2_interp.py +122 -42
  4. emerge/_emerge/geo/__init__.py +1 -1
  5. emerge/_emerge/geo/operations.py +20 -0
  6. emerge/_emerge/geo/pcb.py +162 -71
  7. emerge/_emerge/geo/shapes.py +12 -7
  8. emerge/_emerge/geo/step.py +177 -41
  9. emerge/_emerge/geometry.py +189 -27
  10. emerge/_emerge/logsettings.py +26 -2
  11. emerge/_emerge/material.py +2 -0
  12. emerge/_emerge/mesh3d.py +6 -8
  13. emerge/_emerge/mesher.py +67 -11
  14. emerge/_emerge/mth/common_functions.py +1 -1
  15. emerge/_emerge/mth/optimized.py +2 -2
  16. emerge/_emerge/physics/microwave/adaptive_mesh.py +549 -116
  17. emerge/_emerge/physics/microwave/assembly/assembler.py +9 -1
  18. emerge/_emerge/physics/microwave/microwave_3d.py +133 -83
  19. emerge/_emerge/physics/microwave/microwave_bc.py +158 -8
  20. emerge/_emerge/physics/microwave/microwave_data.py +94 -5
  21. emerge/_emerge/plot/pyvista/display.py +36 -23
  22. emerge/_emerge/selection.py +17 -2
  23. emerge/_emerge/settings.py +124 -6
  24. emerge/_emerge/simmodel.py +273 -150
  25. emerge/_emerge/simstate.py +106 -0
  26. emerge/_emerge/simulation_data.py +11 -23
  27. emerge/_emerge/solve_interfaces/cudss_interface.py +20 -1
  28. emerge/_emerge/solver.py +4 -4
  29. {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/METADATA +7 -3
  30. {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/RECORD +33 -32
  31. {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/WHEEL +0 -0
  32. {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/entry_points.txt +0 -0
  33. {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/licenses/LICENSE +0 -0
emerge/_emerge/mesh3d.py CHANGED
@@ -25,6 +25,7 @@ from .geometry import GeoVolume
25
25
  from loguru import logger
26
26
  from .bc import Periodic
27
27
  from .material import Material
28
+ from .logsettings import DEBUG_COLLECTOR
28
29
 
29
30
  _MISSING_ID: int = -1234
30
31
 
@@ -78,9 +79,7 @@ class Mesh3D(Mesh):
78
79
  are mapped to mesh elements is managed by the FEMBasis class.
79
80
 
80
81
  """
81
- def __init__(self, mesher: Mesher):
82
-
83
- self.geometry: Mesher = mesher
82
+ def __init__(self):
84
83
 
85
84
  # All spatial objects
86
85
  self.nodes: np.ndarray = np.array([])
@@ -186,6 +185,8 @@ class Mesh3D(Mesh):
186
185
  i11, i21, i31 = tuple(sorted((int(i1), int(i2), int(i3))))
187
186
  output = self.inv_tris.get(tuple(sorted((int(i1), int(i2), int(i3)))), None)
188
187
  if output is None:
188
+ DEBUG_COLLECTOR.add_report(f'Mesh3D: The program is crashed due to a non existing triangle {i11}, {i21}, {i31}. This occurs often if surfaces stick out of the 3D domain.\n' +
189
+ 'Only 3D volumes can be meshed. Parts or entire simulations that are two dimensional will cause this problem.')
189
190
  raise ValueError(f'There is no triangle with indices {i11}, {i21}, {i31}')
190
191
  return output
191
192
 
@@ -419,7 +420,7 @@ class Mesh3D(Mesh):
419
420
  self.tet_to_edge_sign = np.zeros((6, self.tets.shape[1]), dtype=int) + _MISSING_ID
420
421
  self.tet_to_tri = np.zeros((4, self.tets.shape[1]), dtype=int) + _MISSING_ID
421
422
  self.tet_to_tri_sign = np.zeros((4, self.tets.shape[1]), dtype=int) + _MISSING_ID
422
-
423
+
423
424
  tri_to_tet = defaultdict(list)
424
425
  for itet in range(self.tets.shape[1]):
425
426
  edge_ids = [self.get_edge(self.tets[i-1,itet],self.tets[j-1,itet]) for i,j in zip([1, 1, 1, 2, 4, 3], [2, 3, 4, 3, 2, 4])]
@@ -502,7 +503,6 @@ class Mesh3D(Mesh):
502
503
  continue
503
504
  self.etag_to_edge[t] = [int(self.edge_t2i.get(tag,None)) for tag in edge_tags[0] if tag in self.edge_t2i]
504
505
 
505
-
506
506
  ## Tag bindings
507
507
  logger.trace('Constructing geometry to mesh mappings.')
508
508
  face_dimtags = gmsh.model.get_entities(2)
@@ -511,7 +511,6 @@ class Mesh3D(Mesh):
511
511
  node_tags = [self.n_t2i[int(t)] for t in node_tags[0]]
512
512
  self.ftag_to_node[t] = node_tags
513
513
  node_tags = np.squeeze(np.array(node_tags)).reshape(-1,3).T
514
-
515
514
  self.ftag_to_tri[t] = [self.get_tri(node_tags[0,i], node_tags[1,i], node_tags[2,i]) for i in range(node_tags.shape[1])]
516
515
  self.ftag_to_edge[t] = sorted(list(np.unique(self.tri_to_edge[:,self.ftag_to_tri[t]].flatten())))
517
516
 
@@ -652,8 +651,6 @@ class Mesh3D(Mesh):
652
651
  * Only the connectivity of the supplied edges is considered.
653
652
  In particular, vertices that never occur in `edges` do **not** create extra
654
653
  components.
655
- * Runtime is *O*(N + V), with N = number of edges, V = number of
656
- distinct vertices. No external libraries are needed.
657
654
  """
658
655
  edges = self.edges[:,edge_ids]
659
656
  if edges.ndim != 2 or edges.shape[0] != 2:
@@ -692,6 +689,7 @@ class Mesh3D(Mesh):
692
689
  group += list(new_edges)
693
690
  ungrouped.difference_update(new_edges)
694
691
 
692
+ groups = sorted(groups, key = lambda x: sum(self.edge_lengths[np.array(x)]))
695
693
  return groups
696
694
 
697
695
  def boundary_surface(self,
emerge/_emerge/mesher.py CHANGED
@@ -75,7 +75,13 @@ class Mesher:
75
75
  self.objects: list[GeoObject] = []
76
76
  self.size_definitions: list[tuple[int, float]] = []
77
77
  self.mesh_fields: list[int] = []
78
+
78
79
  self._amr_fields: list[int] = []
80
+ self._amr_coords: np.ndarray = None
81
+ self._amr_sizes: np.ndarray = None
82
+ self._amr_ratios: np.ndrray = None
83
+ self._amr_new: np.ndarray = None
84
+
79
85
  self.min_size: float = None
80
86
  self.max_size: float = None
81
87
  self.periodic_cell: PeriodicCell = None
@@ -150,7 +156,6 @@ class Mesher:
150
156
  3: dict()}
151
157
  if len(objects) > 0: # type: ignore
152
158
  dimtags, output_mapping = gmsh.model.occ.fragment(final_dimtags, embedding_dimtags)
153
-
154
159
  for domain, mapping in zip(final_dimtags + embedding_dimtags, output_mapping):
155
160
  tag_mapping[domain[0]][domain[1]] = [o[1] for o in mapping]
156
161
  for dom in objects: # type: ignore
@@ -173,11 +178,11 @@ class Mesher:
173
178
  gmsh.model.mesh.set_periodic(2, face2.tags, face1.tags, translation)
174
179
 
175
180
  def set_algorithm(self,
176
- obj: GeoObject,
177
181
  algorithm: Algorithm3D) -> None:
178
- for tag in obj.tags:
179
- gmsh.model.mesh.setAlgorithm(obj.dim, tag, algorithm.value)
180
-
182
+
183
+ gmsh.option.setNumber("General.NumThreads", 16)
184
+ gmsh.option.setNumber("Mesh.Algorithm3D", algorithm.value)
185
+
181
186
  def set_periodic_cell(self, cell: PeriodicCell, excluded_faces: Selection | None = None):
182
187
  """Sets the periodic cell information based on the PeriodicCell class object"""
183
188
  if excluded_faces is None:
@@ -286,6 +291,57 @@ class Mesher:
286
291
  for dimtag in dimtags:
287
292
  gmsh.model.mesh.setSizeFromBoundary(dimtag[0], dimtag[1], 0)
288
293
 
294
+
295
+ def add_refinement_points(self, coords: np.ndarray, sizes: np.ndarray, ratios: np.ndarray):
296
+ if self._amr_coords is None:
297
+ self._amr_coords = coords
298
+ else:
299
+ self._amr_coords = np.hstack((self._amr_coords, coords))
300
+
301
+ if self._amr_sizes is None:
302
+ self._amr_sizes = sizes
303
+ else:
304
+ self._amr_sizes = np.hstack((self._amr_sizes, sizes))
305
+
306
+ if self._amr_ratios is None:
307
+ self._amr_ratios = ratios
308
+ else:
309
+ self._amr_ratios = np.hstack((self._amr_ratios, ratios))
310
+
311
+ if self._amr_new is None:
312
+ self._amr_new = np.ones_like(sizes)
313
+ else:
314
+ self._amr_new = np.hstack((0.0*self._amr_new, np.ones_like(sizes)))
315
+
316
+
317
+ def set_refinement_function(self,
318
+ gr: float = 1.5,
319
+ _qf: float = 1.0):
320
+ xs = self._amr_coords[0,:]
321
+ ys = self._amr_coords[1,:]
322
+ zs = self._amr_coords[2,:]
323
+ newsize = self._amr_ratios*self._amr_sizes
324
+ A = newsize/gr
325
+ B = (1-gr)/gr
326
+ from numba import njit, i8, f8
327
+
328
+ @njit(f8(i8,i8,f8,f8,f8,f8), nogil=True, fastmath=True, parallel=False)
329
+ def func(dim, tag, x, y, z, lc):
330
+ sizes = np.maximum(newsize, A - B * _qf*np.clip(np.sqrt((x-xs)**2 + (y-ys)**2 + (z-zs)**2) - newsize/3, a_min=0, a_max=None))
331
+ return min(lc, float(np.min(sizes)))
332
+
333
+ gmsh.model.mesh.setSizeCallback(func)
334
+
335
+ def refine_finer(self, factor: float):
336
+ newids = self._amr_new==1
337
+ self._amr_ratios[newids] = self._amr_ratios[newids]*(0.85**factor)
338
+ return self._amr_ratios[newids][0]
339
+
340
+ def refine_coarser(self, factor: float):
341
+ newids = self._amr_new==1
342
+ self._amr_ratios[newids] = self._amr_ratios[newids]/(0.85**factor)
343
+ return self._amr_ratios[newids][0]
344
+
289
345
  def add_refinement_point(self,
290
346
  coordinate: np.ndarray,
291
347
  refinement: float,
@@ -301,7 +357,7 @@ class Mesher:
301
357
  def set_boundary_size(self,
302
358
  boundary: GeoObject | Selection | Iterable,
303
359
  size:float,
304
- growth_rate: float = 1.4,
360
+ growth_rate: float = 3,
305
361
  max_size: float | None = None) -> None:
306
362
  """Refine the mesh size along the boundary of a conducting surface
307
363
 
@@ -323,8 +379,8 @@ class Mesher:
323
379
  self._check_ready()
324
380
  max_size = self.max_size
325
381
 
326
- growth_distance = np.log10(max_size/size)/np.log10(growth_rate)
327
-
382
+ #growth_distance = np.log10(max_size/size)/np.log10(growth_rate)
383
+ growth_distance = (growth_rate*max_size - size)/(growth_rate-1)
328
384
  logger.debug(f'Setting boundary size for region {dimtags} to {size*1000:.3f}mm, GR={growth_rate:.3f}, dist={growth_distance*1000:.2f}mm, Max={max_size*1000:.3f}mm')
329
385
 
330
386
  nodes = gmsh.model.getBoundary(dimtags, combined=False, oriented=False, recursive=False)
@@ -341,7 +397,7 @@ class Mesher:
341
397
  gmsh.model.mesh.field.setNumber(thtag, "SizeMin", size)
342
398
  gmsh.model.mesh.field.setNumber(thtag, "SizeMax", max_size)
343
399
  gmsh.model.mesh.field.setNumber(thtag, "DistMin", size)
344
- gmsh.model.mesh.field.setNumber(thtag, "DistMax", growth_distance*size)
400
+ gmsh.model.mesh.field.setNumber(thtag, "DistMax", growth_distance)
345
401
 
346
402
  self.mesh_fields.append(thtag)
347
403
 
@@ -357,7 +413,7 @@ class Mesher:
357
413
  if obj.dim==2:
358
414
  logger.warning('Forwarding to set_face_size')
359
415
  self.set_face_size(obj, size)
360
- logger.debug(f'Setting size {size*1000:.3f}ff for object {obj}')
416
+ logger.debug(f'Setting size {size*1000:.3f}mm for object {obj}')
361
417
  self._set_size_in_domain(obj.tags, size)
362
418
 
363
419
  def set_face_size(self, obj: GeoSurface | Selection, size: float):
@@ -373,7 +429,7 @@ class Mesher:
373
429
  logger.warning('Forwarding to set_domain_size')
374
430
  self.set_face_size(obj, size)
375
431
 
376
- logger.debug(f'Setting size {size*1000:.3f}ff for face {obj}')
432
+ logger.debug(f'Setting size {size*1000:.3f}mm for face {obj}')
377
433
  self._set_size_on_face(obj.tags, size)
378
434
 
379
435
  def set_size(self, obj: GeoObject, size: float) -> None:
@@ -17,7 +17,7 @@
17
17
 
18
18
  import numpy as np
19
19
 
20
- def norm(Field: np.ndarray) -> np.ndarray:
20
+ def norm(Field: np.ndarray) -> np.ndarray:
21
21
  """ Computes the complex norm of a field (3,N)
22
22
 
23
23
  Args:
@@ -397,7 +397,7 @@ def matinv(M: np.ndarray) -> np.ndarray:
397
397
  out[2,0] = M[1,0]*M[2,1] - M[1,1]*M[2,0]
398
398
  out[2,1] = -M[0,0]*M[2,1] + M[0,1]*M[2,0]
399
399
  out[2,2] = M[0,0]*M[1,1] - M[0,1]*M[1,0]
400
- out = out*det
400
+ out = out/det
401
401
  return out
402
402
 
403
403
  @njit(f8[:,:](f8[:,:]), cache=True, nogil=True)
@@ -427,7 +427,7 @@ def matinv_r(M: np.ndarray) -> np.ndarray:
427
427
  out[2,0] = M[1,0]*M[2,1] - M[1,1]*M[2,0]
428
428
  out[2,1] = -M[0,0]*M[2,1] + M[0,1]*M[2,0]
429
429
  out[2,2] = M[0,0]*M[1,1] - M[0,1]*M[1,0]
430
- out = out*det
430
+ out = out/det
431
431
  return out
432
432
 
433
433
  @njit(cache=True, nogil=True)