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.
- emerge/__init__.py +15 -3
- emerge/_emerge/const.py +2 -1
- emerge/_emerge/elements/ned2_interp.py +122 -42
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/operations.py +20 -0
- emerge/_emerge/geo/pcb.py +162 -71
- emerge/_emerge/geo/shapes.py +12 -7
- emerge/_emerge/geo/step.py +177 -41
- emerge/_emerge/geometry.py +189 -27
- emerge/_emerge/logsettings.py +26 -2
- emerge/_emerge/material.py +2 -0
- emerge/_emerge/mesh3d.py +6 -8
- emerge/_emerge/mesher.py +67 -11
- emerge/_emerge/mth/common_functions.py +1 -1
- emerge/_emerge/mth/optimized.py +2 -2
- emerge/_emerge/physics/microwave/adaptive_mesh.py +549 -116
- emerge/_emerge/physics/microwave/assembly/assembler.py +9 -1
- emerge/_emerge/physics/microwave/microwave_3d.py +133 -83
- emerge/_emerge/physics/microwave/microwave_bc.py +158 -8
- emerge/_emerge/physics/microwave/microwave_data.py +94 -5
- emerge/_emerge/plot/pyvista/display.py +36 -23
- emerge/_emerge/selection.py +17 -2
- emerge/_emerge/settings.py +124 -6
- emerge/_emerge/simmodel.py +273 -150
- emerge/_emerge/simstate.py +106 -0
- emerge/_emerge/simulation_data.py +11 -23
- emerge/_emerge/solve_interfaces/cudss_interface.py +20 -1
- emerge/_emerge/solver.py +4 -4
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/METADATA +7 -3
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/RECORD +33 -32
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/WHEEL +0 -0
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
-
|
|
179
|
-
|
|
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 =
|
|
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
|
|
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}
|
|
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}
|
|
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:
|
emerge/_emerge/mth/optimized.py
CHANGED
|
@@ -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
|
|
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
|
|
430
|
+
out = out/det
|
|
431
431
|
return out
|
|
432
432
|
|
|
433
433
|
@njit(cache=True, nogil=True)
|