emerge 1.0.2__py3-none-any.whl → 1.0.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 (30) hide show
  1. emerge/__init__.py +7 -3
  2. emerge/_emerge/elements/femdata.py +5 -1
  3. emerge/_emerge/elements/ned2_interp.py +73 -30
  4. emerge/_emerge/elements/nedelec2.py +1 -0
  5. emerge/_emerge/emerge_update.py +63 -0
  6. emerge/_emerge/geo/operations.py +2 -1
  7. emerge/_emerge/geo/polybased.py +26 -5
  8. emerge/_emerge/geometry.py +5 -0
  9. emerge/_emerge/logsettings.py +26 -1
  10. emerge/_emerge/material.py +29 -8
  11. emerge/_emerge/mesh3d.py +16 -13
  12. emerge/_emerge/mesher.py +70 -3
  13. emerge/_emerge/physics/microwave/assembly/assembler.py +5 -4
  14. emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -1
  15. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +1 -2
  16. emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +1 -1
  17. emerge/_emerge/physics/microwave/assembly/robin_abc_order2.py +0 -1
  18. emerge/_emerge/physics/microwave/microwave_3d.py +37 -16
  19. emerge/_emerge/physics/microwave/microwave_bc.py +15 -4
  20. emerge/_emerge/physics/microwave/microwave_data.py +14 -11
  21. emerge/_emerge/plot/pyvista/cmap_maker.py +70 -0
  22. emerge/_emerge/plot/pyvista/display.py +101 -37
  23. emerge/_emerge/simmodel.py +75 -21
  24. emerge/_emerge/simulation_data.py +22 -4
  25. emerge/_emerge/solver.py +78 -51
  26. {emerge-1.0.2.dist-info → emerge-1.0.4.dist-info}/METADATA +2 -3
  27. {emerge-1.0.2.dist-info → emerge-1.0.4.dist-info}/RECORD +30 -28
  28. {emerge-1.0.2.dist-info → emerge-1.0.4.dist-info}/WHEEL +0 -0
  29. {emerge-1.0.2.dist-info → emerge-1.0.4.dist-info}/entry_points.txt +0 -0
  30. {emerge-1.0.2.dist-info → emerge-1.0.4.dist-info}/licenses/LICENSE +0 -0
emerge/__init__.py CHANGED
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "1.0.2"
21
+ __version__ = "1.0.4"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -30,8 +30,11 @@ os.environ["EMERGE_FILE_LOGLEVEL"] = os.getenv("EMERGE_FILE_LOGLEVEL", default="
30
30
  os.environ["OMP_NUM_THREADS"] = os.getenv("OMP_NUM_THREADS", default="1")
31
31
  os.environ["MKL_NUM_THREADS"] = os.getenv("MKL_NUM_THREADS", default="4")
32
32
  os.environ["OPENBLAS_NUM_THREADS"] = NTHREADS
33
+ os.environ["VECLIB_NUM_THREADS"] = NTHREADS
33
34
  os.environ["VECLIB_MAXIMUM_THREADS"] = NTHREADS
34
35
  os.environ["NUMEXPR_NUM_THREADS"] = NTHREADS
36
+ os.environ["NUMBA_NUM_THREADS"] = "4"
37
+ os.environ.setdefault("NUMBA_THREADING_LAYER", "workqueue")
35
38
 
36
39
  ############################################################
37
40
  # IMPORT MODULES #
@@ -44,6 +47,7 @@ LOG_CONTROLLER.set_default()
44
47
  logger.debug('Importing modules')
45
48
  LOG_CONTROLLER._set_log_buffer()
46
49
 
50
+ import gmsh
47
51
  from ._emerge.simmodel import Simulation
48
52
  from ._emerge.material import Material, FreqCoordDependent, FreqDependent, CoordDependent
49
53
  from ._emerge import bc
@@ -53,12 +57,12 @@ from ._emerge.coord import Line
53
57
  from ._emerge import geo
54
58
  from ._emerge.selection import Selection, FaceSelection, DomainSelection, EdgeSelection
55
59
  from ._emerge.geometry import select
56
- #from ._emerge.mth.common_functions import norm, coax_rout, coax_rin
60
+ from ._emerge.mth.common_functions import norm, coax_rout, coax_rin
57
61
  from ._emerge.periodic import RectCell, HexCell
58
62
  from ._emerge.mesher import Algorithm2D, Algorithm3D
59
63
  from . import lib
60
64
  from ._emerge.howto import _HowtoClass
61
-
65
+ from ._emerge.emerge_update import update_emerge
62
66
  howto = _HowtoClass()
63
67
 
64
68
  logger.debug('Importing complete!')
@@ -61,7 +61,7 @@ class FEMBasis:
61
61
  return matmul(ibasis, np.array(self.interpolate(field, xyzg[0,:], xyzg[1,:], xyzg[2,:], tetids)))
62
62
  return func
63
63
 
64
- def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, tetids: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
64
+ def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, tetids: np.ndarray | None = None, usenan: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
65
65
  raise NotImplementedError()
66
66
 
67
67
  def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, constants: np.ndarray, tetids: np.ndarray | None = None, usenan: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
@@ -70,6 +70,10 @@ class FEMBasis:
70
70
  """
71
71
  raise NotImplementedError()
72
72
 
73
+ def interpolate_error(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, tetids: np.ndarray | None = None, cs: tuple[float, float] = (1.0, 1.0)) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
74
+ raise NotImplementedError()
75
+
76
+
73
77
  def empty_tet_matrix(self) -> np.ndarray:
74
78
  nnz = self.n_tets*self.n_tet_dofs**2
75
79
  matrix = np.empty((nnz,), dtype=np.complex128)
@@ -192,6 +192,7 @@ def ned2_tet_interp(coords: np.ndarray,
192
192
  Exl = np.zeros(x.shape, dtype=np.complex128)
193
193
  Eyl = np.zeros(x.shape, dtype=np.complex128)
194
194
  Ezl = np.zeros(x.shape, dtype=np.complex128)
195
+ V1 = (216*V**3)
195
196
  for ie in range(6):
196
197
  Em1, Em2 = Em1s[ie], Em2s[ie]
197
198
  edgeids = l_edge_ids[:, ie]
@@ -202,11 +203,12 @@ def ned2_tet_interp(coords: np.ndarray,
202
203
  x1, x2 = xvs[edgeids]
203
204
  y1, y2 = yvs[edgeids]
204
205
  z1, z2 = zvs[edgeids]
205
-
206
+ F1 = (a1 + b1*x + c1*y + d1*z)
207
+ F2 = (a2 + b2*x + c2*y + d2*z)
206
208
  L = np.sqrt((x1 - x2)**2 + (y1 - y2)**2 + (z1 - z2)**2)
207
- ex = L*(Em1*(a1 + b1*x + c1*y + d1*z) + Em2*(a2 + b2*x + c2*y + d2*z))*(b1*(a2 + b2*x + c2*y + d2*z) - b2*(a1 + b1*x + c1*y + d1*z))/(216*V**3)
208
- ey = L*(Em1*(a1 + b1*x + c1*y + d1*z) + Em2*(a2 + b2*x + c2*y + d2*z))*(c1*(a2 + b2*x + c2*y + d2*z) - c2*(a1 + b1*x + c1*y + d1*z))/(216*V**3)
209
- ez = L*(Em1*(a1 + b1*x + c1*y + d1*z) + Em2*(a2 + b2*x + c2*y + d2*z))*(d1*(a2 + b2*x + c2*y + d2*z) - d2*(a1 + b1*x + c1*y + d1*z))/(216*V**3)
209
+ ex = L*(Em1*F1 + Em2*F2)*(b1*F2 - b2*F1)/V1
210
+ ey = L*(Em1*F1 + Em2*F2)*(c1*F2 - c2*F1)/V1
211
+ ez = L*(Em1*F1 + Em2*F2)*(d1*F2 - d2*F1)/V1
210
212
 
211
213
  Exl += ex
212
214
  Eyl += ey
@@ -226,10 +228,16 @@ def ned2_tet_interp(coords: np.ndarray,
226
228
 
227
229
  L1 = np.sqrt((x1-x3)**2 + (y1-y3)**2 + (z1-z3)**2)
228
230
  L2 = np.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)
229
-
230
- ex = (-Em1*L1*(b1*(a3 + b3*x + c3*y + d3*z) - b3*(a1 + b1*x + c1*y + d1*z))*(a2 + b2*x + c2*y + d2*z) + Em2*L2*(b1*(a2 + b2*x + c2*y + d2*z) - b2*(a1 + b1*x + c1*y + d1*z))*(a3 + b3*x + c3*y + d3*z))/(216*V**3)
231
- ey = (-Em1*L1*(c1*(a3 + b3*x + c3*y + d3*z) - c3*(a1 + b1*x + c1*y + d1*z))*(a2 + b2*x + c2*y + d2*z) + Em2*L2*(c1*(a2 + b2*x + c2*y + d2*z) - c2*(a1 + b1*x + c1*y + d1*z))*(a3 + b3*x + c3*y + d3*z))/(216*V**3)
232
- ez = (-Em1*L1*(d1*(a3 + b3*x + c3*y + d3*z) - d3*(a1 + b1*x + c1*y + d1*z))*(a2 + b2*x + c2*y + d2*z) + Em2*L2*(d1*(a2 + b2*x + c2*y + d2*z) - d2*(a1 + b1*x + c1*y + d1*z))*(a3 + b3*x + c3*y + d3*z))/(216*V**3)
231
+
232
+ F1 = (a1 + b1*x + c1*y + d1*z)
233
+ F2 = (a2 + b2*x + c2*y + d2*z)
234
+ F3 = (a3 + b3*x + c3*y + d3*z)
235
+
236
+ Q1 = Em1*L1*F2
237
+ Q2 = Em2*L2*F3
238
+ ex = (-Q1*(b1*F3 - b3*F1) + Q2*(b1*F2 - b2*F1))/V1
239
+ ey = (-Q1*(c1*F3 - c3*F1) + Q2*(c1*F2 - c2*F1))/V1
240
+ ez = (-Q1*(d1*F3 - d3*F1) + Q2*(d1*F2 - d2*F1))/V1
233
241
 
234
242
  Exl += ex
235
243
  Eyl += ey
@@ -322,6 +330,10 @@ def ned2_tet_interp_curl(coords: np.ndarray,
322
330
  Exl = np.zeros(x.shape, dtype=np.complex128)
323
331
  Eyl = np.zeros(x.shape, dtype=np.complex128)
324
332
  Ezl = np.zeros(x.shape, dtype=np.complex128)
333
+
334
+ V1 = (216*V**3)
335
+ V2 = (72*V**3)
336
+
325
337
  for ie in range(6):
326
338
  Em1, Em2 = Em1s[ie], Em2s[ie]
327
339
  edgeids = l_edge_ids[:, ie]
@@ -334,9 +346,35 @@ def ned2_tet_interp_curl(coords: np.ndarray,
334
346
  z1, z2 = zvs[edgeids]
335
347
 
336
348
  L = np.sqrt((x1 - x2)**2 + (y1 - y2)**2 + (z1 - z2)**2)
337
- ex = L*(-Em1*a1*c1*d2 + Em1*a1*c2*d1 - Em1*b1*c1*d2*x + Em1*b1*c2*d1*x - Em1*c1**2*d2*y + Em1*c1*c2*d1*y - Em1*c1*d1*d2*z + Em1*c2*d1**2*z - Em2*a2*c1*d2 + Em2*a2*c2*d1 - Em2*b2*c1*d2*x + Em2*b2*c2*d1*x - Em2*c1*c2*d2*y - Em2*c1*d2**2*z + Em2*c2**2*d1*y + Em2*c2*d1*d2*z)/(72*V**3)
338
- ey = L*(Em1*a1*b1*d2 - Em1*a1*b2*d1 + Em1*b1**2*d2*x - Em1*b1*b2*d1*x + Em1*b1*c1*d2*y + Em1*b1*d1*d2*z - Em1*b2*c1*d1*y - Em1*b2*d1**2*z + Em2*a2*b1*d2 - Em2*a2*b2*d1 + Em2*b1*b2*d2*x + Em2*b1*c2*d2*y + Em2*b1*d2**2*z - Em2*b2**2*d1*x - Em2*b2*c2*d1*y - Em2*b2*d1*d2*z)/(72*V**3)
339
- ez = L*(-Em1*a1*b1*c2 + Em1*a1*b2*c1 - Em1*b1**2*c2*x + Em1*b1*b2*c1*x - Em1*b1*c1*c2*y - Em1*b1*c2*d1*z + Em1*b2*c1**2*y + Em1*b2*c1*d1*z - Em2*a2*b1*c2 + Em2*a2*b2*c1 - Em2*b1*b2*c2*x - Em2*b1*c2**2*y - Em2*b1*c2*d2*z + Em2*b2**2*c1*x + Em2*b2*c1*c2*y + Em2*b2*c1*d2*z)/(72*V**3)
349
+ C1 = Em1*a1
350
+ C2 = Em1*b1
351
+ C3 = Em1*c1
352
+ C4 = Em1*c2
353
+ C5 = Em2*a2
354
+ C6 = Em2*b2
355
+ C7 = Em2*c1
356
+ C8 = Em2*c2
357
+ C9 = Em1*b2
358
+ C10 = Em2*b1
359
+ D1 = c1*d2
360
+ D2 = c2*d1
361
+ D3 = d1*d2
362
+ D4 = d1*d1
363
+ D5 = c2*d2
364
+ D6 = d2*d2
365
+ D7 = b1*d2
366
+ D8 = b2*d1
367
+ D9 = c1*d1
368
+ D10 = b2*d2
369
+ D11 = b1*c2
370
+ D12 = b2*c1
371
+ D13 = c1*c2
372
+ D14 = c1*c1
373
+ D15 = b2*c2
374
+
375
+ ex = L*(-C1*D1 + C1*D2 - C2*D1*x + C2*D2*x - C3*D1*y + C3*D2*y - C3*D3*z + C4*D4*z - C5*D1 + C5*D2 - C6*D1*x + C6*D2*x - C7*D5*y - C7*D6*z + C8*D2*y + C8*D3*z)/V2
376
+ ey = L*(C1*D7 - C1*D8 + C2*D7*x - C2*D8*x + C2*D1*y + C2*D3*z - C9*D9*y - C9*D4*z + C5*D7 - C5*D8 + C10*D10*x + C10*D5*y + C10*D6*z - C6*D8*x - C6*D2*y - C6*D3*z)/V2
377
+ ez = L*(-C1*D11 + C1*D12 - C2*D11*x + C2*D12*x - C2*D13*y - C2*D2*z + C9*D14*y + C9*D9*z - C5*D11 + C5*D12 - C10*D15*x - C10*c2*c2*y - C10*D5*z + C6*D12*x + C6*D13*y + C6*D1*z)/V2
340
378
  Exl += ex
341
379
  Eyl += ey
342
380
  Ezl += ez
@@ -355,10 +393,28 @@ def ned2_tet_interp_curl(coords: np.ndarray,
355
393
 
356
394
  L1 = np.sqrt((x1-x3)**2 + (y1-y3)**2 + (z1-z3)**2)
357
395
  L2 = np.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)
358
-
359
- ex = (Em1*L1*(-c2*(d1*(a3 + b3*x + c3*y + d3*z) - d3*(a1 + b1*x + c1*y + d1*z)) + d2*(c1*(a3 + b3*x + c3*y + d3*z) - c3*(a1 + b1*x + c1*y + d1*z)) + 2*(c1*d3 - c3*d1)*(a2 + b2*x + c2*y + d2*z)) - Em2*L2*(-c3*(d1*(a2 + b2*x + c2*y + d2*z) - d2*(a1 + b1*x + c1*y + d1*z)) + d3*(c1*(a2 + b2*x + c2*y + d2*z) - c2*(a1 + b1*x + c1*y + d1*z)) + 2*(c1*d2 - c2*d1)*(a3 + b3*x + c3*y + d3*z)))/(216*V**3)
360
- ey = (-Em1*L1*(-b2*(d1*(a3 + b3*x + c3*y + d3*z) - d3*(a1 + b1*x + c1*y + d1*z)) + d2*(b1*(a3 + b3*x + c3*y + d3*z) - b3*(a1 + b1*x + c1*y + d1*z)) + 2*(b1*d3 - b3*d1)*(a2 + b2*x + c2*y + d2*z)) + Em2*L2*(-b3*(d1*(a2 + b2*x + c2*y + d2*z) - d2*(a1 + b1*x + c1*y + d1*z)) + d3*(b1*(a2 + b2*x + c2*y + d2*z) - b2*(a1 + b1*x + c1*y + d1*z)) + 2*(b1*d2 - b2*d1)*(a3 + b3*x + c3*y + d3*z)))/(216*V**3)
361
- ez = (Em1*L1*(-b2*(c1*(a3 + b3*x + c3*y + d3*z) - c3*(a1 + b1*x + c1*y + d1*z)) + c2*(b1*(a3 + b3*x + c3*y + d3*z) - b3*(a1 + b1*x + c1*y + d1*z)) + 2*(b1*c3 - b3*c1)*(a2 + b2*x + c2*y + d2*z)) - Em2*L2*(-b3*(c1*(a2 + b2*x + c2*y + d2*z) - c2*(a1 + b1*x + c1*y + d1*z)) + c3*(b1*(a2 + b2*x + c2*y + d2*z) - b2*(a1 + b1*x + c1*y + d1*z)) + 2*(b1*c2 - b2*c1)*(a3 + b3*x + c3*y + d3*z)))/(216*V**3)
396
+ F1 = (a3 + b3*x + c3*y + d3*z)
397
+ F2 = (a1 + b1*x + c1*y + d1*z)
398
+ F3 = (a2 + b2*x + c2*y + d2*z)
399
+ N1 = (d1*F1 - d3*F2)
400
+ N2 = (c1*F1 - c3*F2)
401
+ N3 = (c1*d3 - c3*d1)
402
+ N4 = (d1*F3 - d2*F2)
403
+ N5 = (c1*F3 - c2*F2)
404
+ D1 = c1*d2
405
+ D2 = c2*d1
406
+ N6 = (D1 - D2)
407
+ N7 = (b1*F1 - b3*F2)
408
+ N8 = (b1*d3 - b3*d1)
409
+ N9 = (b1*F3 - b2*F2)
410
+ D7 = b1*d2
411
+ D8 = b2*d1
412
+ N10 = (D7 - D8)
413
+ D11 = b1*c2
414
+ D12 = b2*c1
415
+ ex = (Em1*L1*(-c2*N1 + d2*N2 + 2*N3*F3) - Em2*L2*(-c3*N4 + d3*N5 + 2*N6*F1))/V1
416
+ ey = (-Em1*L1*(-b2*N1 + d2*N7 + 2*N8*F3) + Em2*L2*(-b3*N4 + d3*N9 + 2*N10*F1))/V1
417
+ ez = (Em1*L1*(-b2*N2 + c2*N7 + 2*(b1*c3 - b3*c1)*F3) - Em2*L2*(-b3*N5 + c3*N9 + 2*(D11 - D12)*F1))/V1
362
418
 
363
419
  Exl += ex
364
420
  Eyl += ey
@@ -445,8 +501,7 @@ def ned2_tri_interp(coords: np.ndarray,
445
501
 
446
502
  Exl = np.zeros(x.shape, dtype=np.complex128)
447
503
  Eyl = np.zeros(x.shape, dtype=np.complex128)
448
-
449
-
504
+
450
505
  for ie in range(3):
451
506
  Em1, Em2 = Em1s[ie], Em2s[ie]
452
507
  edgeids = l_edge_ids[:, ie]
@@ -534,17 +589,11 @@ def ned2_tri_interp_full(coords: np.ndarray,
534
589
  yvs = nodes[1, tris[:,itri]]
535
590
 
536
591
  a_s, b_s, c_s, A = tri_coefficients(xvs, yvs)
537
-
538
592
  e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 = Etri
539
593
 
540
594
  a1, a2, a3 = a_s
541
595
  b1, b2, b3 = b_s
542
596
  c1, c2, c3 = c_s
543
-
544
- # original Nedelec-1 order 2 formulation
545
- # ex = (e1*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) + e2*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a2 + b2*x + c2*y) + e3*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) - e4*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e5*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e6*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a3 + b3*x + c3*y) + e7*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + e8*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y))/(8*A**3)
546
- # ey = (e1*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) + e2*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a2 + b2*x + c2*y) + e3*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) - e4*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e5*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e6*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a3 + b3*x + c3*y) + e7*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + e8*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y))/(8*A**3)
547
- # ez = (a1 + b1*x + c1*y)*(e10*(-A + a1 + b1*x + c1*y) + e11*(-A + a1 + b1*x + c1*y) + e12*(a2 + b2*x + c2*y) + e13*(a2 + b2*x + c2*y) + e14*(a2 + b2*x + c2*y) + e9*(-A + a1 + b1*x + c1*y))/(2*A**2)
548
597
 
549
598
  # New Nedelec-1 order 2 formulation
550
599
  ex = (-2*A*(e1*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y)) + e2*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y)) + e3*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))) - e4*((b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + (b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a1 + b1*x + c1*y)) - e5*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a1 - a2 + b1*x - b2*x + c1*y - c2*y) - e6*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a2 - a3 + b2*x - b3*x + c2*y - c3*y) - e7*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a1 - a3 + b1*x - b3*x + c1*y - c3*y) + e8*((b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + (b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y)))/(8*A**3)
@@ -617,18 +666,12 @@ def ned2_tri_interp_curl(coords: np.ndarray,
617
666
  yvs = nodes[1, tris[:,itri]]
618
667
 
619
668
  a_s, b_s, c_s, A = tri_coefficients(xvs, yvs)
620
-
621
669
  e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 = Etri
622
670
 
623
671
  a1, a2, a3 = a_s
624
672
  b1, b2, b3 = b_s
625
673
  c1, c2, c3 = c_s
626
674
 
627
- # original Nedelec-1 order 2 formulation
628
- #hx = (4*A*(c1*(e10*(-A + a1 + b1*x + c1*y) + e11*(-A + a1 + b1*x + c1*y) + e12*(a2 + b2*x + c2*y) + e13*(a2 + b2*x + c2*y) + e14*(a2 + b2*x + c2*y) + e9*(-A + a1 + b1*x + c1*y)) + (a1 + b1*x + c1*y)*(c1*e10 + c1*e11 + c1*e9 + c2*e12 + c2*e13 + c2*e14)) + jB*(e1*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) + e2*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a2 + b2*x + c2*y) + e3*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) - e4*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e5*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e6*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a3 + b3*x + c3*y) + e7*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + e8*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y)))/(8*A**3)
629
- # hy = (4*A*(b1*(e10*(-A + a1 + b1*x + c1*y) + e11*(-A + a1 + b1*x + c1*y) + e12*(a2 + b2*x + c2*y) + e13*(a2 + b2*x + c2*y) + e14*(a2 + b2*x + c2*y) + e9*(-A + a1 + b1*x + c1*y)) + (a1 + b1*x + c1*y)*(b1*e10 + b1*e11 + b1*e9 + b2*e12 + b2*e13 + b2*e14)) - jB*(e1*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) + e2*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a2 + b2*x + c2*y) + e3*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a1 + b1*x + c1*y) - e4*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e5*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + e6*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a3 + b3*x + c3*y) + e7*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + e8*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y)))/(8*A**3)
630
- # hz = (-3*a1*b1*c2*e1 - 3*a1*b1*c3*e3 + 3*a1*b2*c1*e1 + a1*b2*c3*e4 + a1*b2*c3*e8 + 3*a1*b3*c1*e3 - a1*b3*c2*e4 - a1*b3*c2*e8 - 3*a2*b1*c2*e5 + 2*a2*b1*c3*e4 - a2*b1*c3*e8 + 3*a2*b2*c1*e5 - 3*a2*b2*c3*e2 - 2*a2*b3*c1*e4 + a2*b3*c1*e8 + 3*a2*b3*c2*e2 + a3*b1*c2*e4 - 2*a3*b1*c2*e8 - 3*a3*b1*c3*e7 - a3*b2*c1*e4 + 2*a3*b2*c1*e8 - 3*a3*b2*c3*e6 + 3*a3*b3*c1*e7 + 3*a3*b3*c2*e6 - 3*b1**2*c2*e1*x - 3*b1**2*c3*e3*x + 3*b1*b2*c1*e1*x - 3*b1*b2*c2*e5*x + 3*b1*b2*c3*e4*x + 3*b1*b3*c1*e3*x - 3*b1*b3*c2*e8*x - 3*b1*b3*c3*e7*x - 3*b1*c1*c2*e1*y - 3*b1*c1*c3*e3*y - 3*b1*c2**2*e5*y + 3*b1*c2*c3*e4*y - 3*b1*c2*c3*e8*y - 3*b1*c3**2*e7*y + 3*b2**2*c1*e5*x - 3*b2**2*c3*e2*x - 3*b2*b3*c1*e4*x + 3*b2*b3*c1*e8*x + 3*b2*b3*c2*e2*x - 3*b2*b3*c3*e6*x + 3*b2*c1**2*e1*y + 3*b2*c1*c2*e5*y + 3*b2*c1*c3*e8*y - 3*b2*c2*c3*e2*y - 3*b2*c3**2*e6*y + 3*b3**2*c1*e7*x + 3*b3**2*c2*e6*x + 3*b3*c1**2*e3*y - 3*b3*c1*c2*e4*y + 3*b3*c1*c3*e7*y + 3*b3*c2**2*e2*y + 3*b3*c2*c3*e6*y)/(8*A**3)
631
-
632
675
  # New Nedelec-1 order 2 formulation
633
676
  hx = (4*A*(2*c1*e12*(a2 + b2*x + c2*y) + 2*c1*e14*(a3 + b3*x + c3*y) + c1*e9*(a1 + b1*x + c1*y) - c1*e9*(A - a1 - b1*x - c1*y) + c2*e10*(a2 + b2*x + c2*y) - c2*e10*(A - a2 - b2*x - c2*y) + 2*c2*e12*(a1 + b1*x + c1*y) + 2*c2*e13*(a3 + b3*x + c3*y) + c3*e11*(a3 + b3*x + c3*y) - c3*e11*(A - a3 - b3*x - c3*y) + 2*c3*e13*(a2 + b2*x + c2*y) + 2*c3*e14*(a1 + b1*x + c1*y)) + jB*(2*A*(e1*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y)) + e2*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y)) + e3*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))) + e4*((c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + (c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a1 + b1*x + c1*y)) + e5*(c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a1 - a2 + b1*x - b2*x + c1*y - c2*y) + e6*(c2*(a3 + b3*x + c3*y) - c3*(a2 + b2*x + c2*y))*(a2 - a3 + b2*x - b3*x + c2*y - c3*y) + e7*(c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a1 - a3 + b1*x - b3*x + c1*y - c3*y) - e8*((c1*(a2 + b2*x + c2*y) - c2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + (c1*(a3 + b3*x + c3*y) - c3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y))))/(8*A**3)
634
677
  hy = (4*A*(-2*b1*e12*(a2 + b2*x + c2*y) - 2*b1*e14*(a3 + b3*x + c3*y) - b1*e9*(a1 + b1*x + c1*y) + b1*e9*(A - a1 - b1*x - c1*y) - b2*e10*(a2 + b2*x + c2*y) + b2*e10*(A - a2 - b2*x - c2*y) - 2*b2*e12*(a1 + b1*x + c1*y) - 2*b2*e13*(a3 + b3*x + c3*y) - b3*e11*(a3 + b3*x + c3*y) + b3*e11*(A - a3 - b3*x - c3*y) - 2*b3*e13*(a2 + b2*x + c2*y) - 2*b3*e14*(a1 + b1*x + c1*y)) - jB*(2*A*(e1*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y)) + e2*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y)) + e3*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))) + e4*((b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y) + (b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a1 + b1*x + c1*y)) + e5*(b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a1 - a2 + b1*x - b2*x + c1*y - c2*y) + e6*(b2*(a3 + b3*x + c3*y) - b3*(a2 + b2*x + c2*y))*(a2 - a3 + b2*x - b3*x + c2*y - c3*y) + e7*(b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a1 - a3 + b1*x - b3*x + c1*y - c3*y) - e8*((b1*(a2 + b2*x + c2*y) - b2*(a1 + b1*x + c1*y))*(a3 + b3*x + c3*y) + (b1*(a3 + b3*x + c3*y) - b3*(a1 + b1*x + c1*y))*(a2 + b2*x + c2*y))))/(8*A**3)
@@ -637,4 +680,4 @@ def ned2_tri_interp_curl(coords: np.ndarray,
637
680
  Ex[inside] = hx*dc[0,0]
638
681
  Ey[inside] = hy*dc[1,1]
639
682
  Ez[inside] = hz*dc[2,2]
640
- return Ex, Ey, Ez
683
+ return Ex, Ey, Ez
@@ -91,6 +91,7 @@ class Nedelec2(FEMBasis):
91
91
  vals = np.nan_to_num(vals)
92
92
  return vals
93
93
 
94
+
94
95
  def interpolate_index(self, xs: np.ndarray,
95
96
  ys: np.ndarray,
96
97
  zs: np.ndarray,
@@ -0,0 +1,63 @@
1
+ import subprocess
2
+ import sys
3
+ from importlib import import_module
4
+
5
+ try:
6
+ from loguru import logger
7
+ except ImportError:
8
+ # Fallback if loguru isn't installed
9
+ class _Logger:
10
+ def info(self, msg): print(f"[INFO] {msg}")
11
+ def warning(self, msg): print(f"[WARN] {msg}")
12
+ def error(self, msg): print(f"[ERROR] {msg}")
13
+ logger = _Logger()
14
+
15
+
16
+ def update_emerge(branch: str = "main", confirm: bool = True, _dryrun: bool = False) -> str | None:
17
+ """
18
+ Update the EMerge library directly from GitHub.
19
+
20
+ Parameters
21
+ ----------
22
+ branch : str, optional
23
+ The git branch or tag to install from. Default is "main".
24
+ confirm : bool, optional
25
+ If True, asks the user for confirmation before updating.
26
+
27
+ Returns
28
+ -------
29
+ str | None
30
+ The updated version string if successful, otherwise None.
31
+ """
32
+ logger.warning(
33
+ "You are about to update EMerge from GitHub. "
34
+ "This is an experimental feature and may overwrite your installed version."
35
+ )
36
+
37
+ if confirm:
38
+ ans = input("Do you wish to proceed? [y/N] ").strip().lower()
39
+ if ans != "y":
40
+ logger.info("Update aborted by user.")
41
+ return None
42
+
43
+ url = f"git+https://github.com/FennisRobert/EMerge.git@{branch}"
44
+ logger.info(f"Updating EMerge from branch/tag '{branch}'...")
45
+
46
+ if not _dryrun:
47
+ try:
48
+ subprocess.check_call(
49
+ [sys.executable, "-m", "pip", "install", "--upgrade", "--force-reinstall", url]
50
+ )
51
+ except subprocess.CalledProcessError as e:
52
+ logger.error(f"Update failed: {e}")
53
+ return None
54
+ else:
55
+ logger.info('Dry run... No update executed!')
56
+ try:
57
+ emerge = import_module("emerge")
58
+ version = getattr(emerge, "__version__", "unknown")
59
+ logger.info(f"Successfully updated to EMerge version {version}.")
60
+ return version
61
+ except Exception as e:
62
+ logger.error(f"Update installed but could not retrieve version: {e}")
63
+ return None
@@ -300,14 +300,15 @@ def unite(*objects: GeoObject) -> GeoObject:
300
300
  GeoObject: The resultant object
301
301
  """
302
302
  main, *rest = objects
303
+
303
304
  if not rest:
304
305
  return main
306
+
305
307
  main._exists = False
306
308
  dts = []
307
309
  for other in rest:
308
310
  dts.extend(other.dimtags)
309
311
  other._exists = False
310
-
311
312
  new_dimtags, mapping = gmsh.model.occ.fuse(main.dimtags, dts)
312
313
 
313
314
  new_obj = GeoObject.from_dimtags(new_dimtags)._take_tools(*objects)
@@ -238,7 +238,8 @@ class XYPolygon:
238
238
  def __init__(self,
239
239
  xs: np.ndarray | list | tuple | None = None,
240
240
  ys: np.ndarray | list | tuple | None = None,
241
- cs: CoordinateSystem | None = None):
241
+ cs: CoordinateSystem | None = None,
242
+ resolution: float = 1e-6):
242
243
  """Constructs an XY-plane placed polygon.
243
244
 
244
245
  Args:
@@ -256,6 +257,7 @@ class XYPolygon:
256
257
  self.fillets: list[tuple[float, int]] = []
257
258
 
258
259
  self._cs: CoordinateSystem = cs
260
+ self.resolution: float = resolution
259
261
 
260
262
  @property
261
263
  def center(self) -> tuple[float, float]:
@@ -279,7 +281,7 @@ class XYPolygon:
279
281
  The XYPolygon does not store redundant points p[0]==p[N] so if these are
280
282
  the same, this function will remove the last point.
281
283
  """
282
- if np.sqrt((self.x[-1]-self.x[0])**2 + (self.y[-1]-self.y[0])**2) < 1e-6:
284
+ if np.sqrt((self.x[-1]-self.x[0])**2 + (self.y[-1]-self.y[0])**2) < 1e-9:
283
285
  self.x = self.x[:-1]
284
286
  self.y = self.y[:-1]
285
287
 
@@ -329,6 +331,23 @@ class XYPolygon:
329
331
  self.fillets.append((radius, i))
330
332
  return self
331
333
 
334
+ def _cleanup(self, resolution: float | None = None) -> None:
335
+ # Compute differences between consecutive points
336
+ if resolution is None:
337
+ resolution = self.resolution
338
+ dx = np.diff(self.x)
339
+ dy = np.diff(self.y)
340
+
341
+ # Distances between consecutive points
342
+ dist = np.sqrt(dx**2 + dy**2)
343
+
344
+ # Keep the first point, then points where distance >= threshold
345
+ keep = np.insert(dist >= resolution, 1, True)
346
+
347
+ # Apply mask
348
+ self.x = self.x[keep]
349
+ self.y = self.y[keep]
350
+
332
351
  def _make_wire(self, cs: CoordinateSystem) -> tuple[list[int], list[int], int]:
333
352
  """Turns the XYPolygon object into a GeoPolygon that is embedded in 3D space.
334
353
 
@@ -345,8 +364,11 @@ class XYPolygon:
345
364
  ptags = []
346
365
  xg, yg, zg = cs.in_global_cs(self.x, self.y, 0*self.x)
347
366
 
367
+ points = dict()
348
368
  for x,y,z in zip(xg, yg, zg):
349
- ptags.append(gmsh.model.occ.add_point(x,y,z))
369
+ ptag = gmsh.model.occ.add_point(x,y,z)
370
+ points[ptag] = (x,y,z)
371
+ ptags.append(ptag)
350
372
 
351
373
  lines = []
352
374
  for i1, p1 in enumerate(ptags):
@@ -375,6 +397,7 @@ class XYPolygon:
375
397
  Returns:
376
398
  GeoPolygon: The resultant 3D GeoPolygon object.
377
399
  """
400
+ self._cleanup()
378
401
  ptags, lines, wiretag = self._make_wire(cs)
379
402
  surftag = gmsh.model.occ.add_plane_surface([wiretag,])
380
403
  poly = GeoPolygon([surftag,], name=name)
@@ -635,8 +658,6 @@ class Curve(GeoEdge):
635
658
  self.dstart: tuple[float, float, float] = (p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2])
636
659
 
637
660
 
638
-
639
-
640
661
  @property
641
662
  def p0(self) -> tuple[float, float, float]:
642
663
  """The start coordinate
@@ -60,6 +60,11 @@ class _GeometryManager:
60
60
  model = self.active
61
61
  return [geo for geo in self.geometry_list[model].values() if geo._exists]
62
62
 
63
+ def set_geometries(self, geos: list[GeoObject], model: str | None = None):
64
+ if model is None:
65
+ model = self.active
66
+ self.geometry_list[model] = geos
67
+
63
68
  def all_names(self, model: str | None = None) -> set[str]:
64
69
  if model is None:
65
70
  model = self.active
@@ -21,11 +21,25 @@ from typing import Literal
21
21
  from pathlib import Path
22
22
  import os
23
23
  from collections import deque
24
-
24
+ import platform
25
+ import importlib
26
+ import multiprocessing
27
+
28
+ packages = ["numba", "numpy", "scipy", "gmsh", "joblib","matplotlib","pyvista","mkl","cloudpickle","scikit-umfpack","nvidia-cudss-cu12","nvmath-python[cu12]","cupy-cuda12x","ezdxf"]
29
+
30
+ def get_version(pkg_name):
31
+ try:
32
+ module = importlib.import_module(pkg_name)
33
+ return getattr(module, "__version__", "unknown")
34
+ except ImportError:
35
+ return "not installed"
36
+
25
37
  _LOG_BUFFER = deque()
26
38
 
27
39
  def _log_sink(message):
28
40
  _LOG_BUFFER.append(message)
41
+
42
+
29
43
  ############################################################
30
44
  # FORMATS #
31
45
  ############################################################
@@ -93,6 +107,17 @@ class LogController:
93
107
  logger.opt(depth=6).log(msg.record["level"].name, msg.record["message"])
94
108
  _LOG_BUFFER.clear()
95
109
 
110
+ def _sys_info(self) -> None:
111
+ for pkg in packages:
112
+ logger.trace(f" (!) {pkg} version: {get_version(pkg)}")
113
+
114
+ logger.trace(f" (!) OS: {platform.system()} {platform.release()} ({platform.version()})")
115
+ logger.trace(f" (!) Architecture: {platform.machine()} ({platform.architecture()[0]})")
116
+ logger.trace(f" (!) Processor: {platform.processor()}")
117
+ logger.trace(f" (!) Python build: {platform.python_build()}")
118
+ logger.trace(f" (!) Python version: {platform.python_version()} [{sys.version}]")
119
+ logger.trace(f" (!) CPU cores: {multiprocessing.cpu_count()}")
120
+
96
121
  def set_std_loglevel(self, loglevel: str):
97
122
  handler = {"sink": sys.stdout,
98
123
  "level": loglevel,
@@ -45,9 +45,10 @@ def _to_mat(value: float | complex | int | np.ndarray) -> np.ndarray:
45
45
  class MatProperty:
46
46
  _freq_dependent: bool = False
47
47
  _coord_dependent: bool = False
48
+ _pickle_exclude = {"_func","_fmax"}
48
49
  """The MatProperty class is an interface for EMerge to deal with frequency and coordinate dependent material properties
49
50
  """
50
-
51
+
51
52
  def __init__(self, value: float | complex | int | np.ndarray):
52
53
  self.value: np.ndarray = _to_mat(value)
53
54
 
@@ -76,7 +77,19 @@ class MatProperty:
76
77
  self._x: np.ndarray = np.array([], dtype=np.float64)
77
78
  self._y: np.ndarray = np.array([], dtype=np.float64)
78
79
  self._z: np.ndarray = np.array([], dtype=np.float64)
80
+
81
+ def __getstate__(self):
82
+ state = self.__dict__.copy()
83
+ for k in self._pickle_exclude:
84
+ state.pop(k, None)
79
85
 
86
+ return state
87
+
88
+ def __setstate__(self, state):
89
+ self.__dict__.update(state)
90
+ for k in self._pickle_exclude:
91
+ setattr(self, k, None)
92
+
80
93
  class FreqDependent(MatProperty):
81
94
  _freq_dependent: bool = True
82
95
  _coord_dependent: bool = False
@@ -165,6 +178,7 @@ class CoordDependent(MatProperty):
165
178
  if scalar is not None:
166
179
  def _func(x, y, z) -> np.ndarray:
167
180
  return np.eye(3)[:, :, None] * scalar(x,y,z)[None, None, :]
181
+
168
182
  if vector is not None:
169
183
  def _func(x, y, z) -> np.ndarray:
170
184
  N = x.shape[0]
@@ -261,13 +275,6 @@ class FreqCoordDependent(MatProperty):
261
275
  def scalar(self, f: float):
262
276
  return self._func(f, 0,0,0)[0,0]
263
277
 
264
- # To be finished once its clear how to deal with default values for functions
265
-
266
- # def parse_material_property(value: complex | float | Callable):
267
- # if not isinstance(value, Callable):
268
- # return MatProperty(value)
269
- # pass
270
-
271
278
  class Material:
272
279
  """The Material class generalizes a material in the EMerge FEM environment.
273
280
 
@@ -282,6 +289,8 @@ class Material:
282
289
  to supply a frequency and coordinate dependent property use: emerge.FreqCoordDependent()
283
290
 
284
291
  """
292
+ _pickle_exclude = {"_neff"}
293
+
285
294
  def __init__(self,
286
295
  er: float | complex | np.ndarray | MatProperty = 1.0,
287
296
  ur: float | complex | np.ndarray | MatProperty = 1.0,
@@ -319,6 +328,18 @@ class Material:
319
328
  hex_str = self.color.lstrip('#')
320
329
  self._color_rgb = tuple(int(hex_str[i:i+2], 16)/255.0 for i in (0, 2, 4))
321
330
  self._metal: bool = _metal
331
+
332
+ def __getstate__(self):
333
+ state = self.__dict__.copy()
334
+ for k in self._pickle_exclude:
335
+ state.pop(k, None)
336
+
337
+ return state
338
+
339
+ def __setstate__(self, state):
340
+ self.__dict__.update(state)
341
+ for k in self._pickle_exclude:
342
+ setattr(self, k, None)
322
343
 
323
344
  def __hash__(self):
324
345
  return self._hash_key
emerge/_emerge/mesh3d.py CHANGED
@@ -26,6 +26,8 @@ from loguru import logger
26
26
  from .bc import Periodic
27
27
  from .material import Material
28
28
 
29
+ _MISSING_ID: int = -1234
30
+
29
31
  def shortest_distance(point_cloud):
30
32
  """
31
33
  Compute the shortest distance between any two points in a 3D point cloud.
@@ -128,7 +130,6 @@ class Mesh3D(Mesh):
128
130
  ## States
129
131
  self.defined: bool = False
130
132
 
131
-
132
133
  ## Memory
133
134
  self.ftag_to_tri: dict[int, list[int]] = dict()
134
135
  self.ftag_to_node: dict[int, list[int]] = dict()
@@ -167,8 +168,8 @@ class Mesh3D(Mesh):
167
168
  if i1==i2:
168
169
  raise ValueError("Edge cannot be formed by the same node.")
169
170
  search = (min(int(i1),int(i2)), max(int(i1),int(i2)))
170
- result = self.inv_edges.get(search, -10)
171
- if result == -10 and not skip:
171
+ result = self.inv_edges.get(search, _MISSING_ID)
172
+ if result == _MISSING_ID and not skip:
172
173
  raise ValueError(f'There is no edge with indices {i1}, {i2}')
173
174
  return result
174
175
 
@@ -182,9 +183,10 @@ class Mesh3D(Mesh):
182
183
 
183
184
  def get_tri(self, i1, i2, i3) -> int:
184
185
  '''Return the triangle index given the three node indices'''
186
+ i11, i21, i31 = tuple(sorted((int(i1), int(i2), int(i3))))
185
187
  output = self.inv_tris.get(tuple(sorted((int(i1), int(i2), int(i3)))), None)
186
188
  if output is None:
187
- raise ValueError(f'There is no triangle with indices {i1}, {i2}, {i3}')
189
+ raise ValueError(f'There is no triangle with indices {i11}, {i21}, {i31}')
188
190
  return output
189
191
 
190
192
  def get_tet(self, i1, i2, i3, i4) -> int:
@@ -302,7 +304,7 @@ class Mesh3D(Mesh):
302
304
  """
303
305
  from .mth.optimized import area
304
306
 
305
- logger.trace('Generating mesh data.')
307
+ logger.trace('Generating internal mesh data.')
306
308
  if periodic_bcs is None:
307
309
  periodic_bcs = []
308
310
 
@@ -383,10 +385,10 @@ class Mesh3D(Mesh):
383
385
  self.inv_tets = {_hash((self.tets[0,i], self.tets[1,i], self.tets[2,i], self.tets[3,i])): i for i in range(self.tets.shape[1])}
384
386
 
385
387
  # Tet links
386
- self.tet_to_edge = np.zeros((6, self.tets.shape[1]), dtype=int)-99999
387
- self.tet_to_edge_sign = np.zeros((6, self.tets.shape[1]), dtype=int)-999999
388
- self.tet_to_tri = np.zeros((4, self.tets.shape[1]), dtype=int)-99999
389
- self.tet_to_tri_sign = np.zeros((4, self.tets.shape[1]), dtype=int)-999999
388
+ self.tet_to_edge = np.zeros((6, self.tets.shape[1]), dtype=int) + _MISSING_ID
389
+ self.tet_to_edge_sign = np.zeros((6, self.tets.shape[1]), dtype=int) + _MISSING_ID
390
+ self.tet_to_tri = np.zeros((4, self.tets.shape[1]), dtype=int) + _MISSING_ID
391
+ self.tet_to_tri_sign = np.zeros((4, self.tets.shape[1]), dtype=int) + _MISSING_ID
390
392
 
391
393
  tri_to_tet = defaultdict(list)
392
394
  for itet in range(self.tets.shape[1]):
@@ -408,7 +410,7 @@ class Mesh3D(Mesh):
408
410
  tri_to_tet[self.tet_to_tri[3, itet]].append(itet)
409
411
 
410
412
  # Tri links
411
- self.tri_to_tet = np.zeros((2, self.tris.shape[1]), dtype=int)-1
413
+ self.tri_to_tet = np.zeros((2, self.tris.shape[1]), dtype=int)+_MISSING_ID
412
414
  for itri in range(self.tris.shape[1]):
413
415
  tets = tri_to_tet[itri]
414
416
  self.tri_to_tet[:len(tets), itri] = tets
@@ -459,7 +461,7 @@ class Mesh3D(Mesh):
459
461
  ent = np.array(edge_node_tags).reshape(-1,2).T
460
462
  nET = ent.shape[1]
461
463
  self.edge_t2i = {int(edge_tags[i]): self.get_edge(self.n_t2i[ent[0,i]], self.n_t2i[ent[1,i]], skip=True) for i in range(nET)}
462
- self.edge_t2i = {key: value for key,value in self.edge_t2i.items() if value!=-10}
464
+ self.edge_t2i = {key: value for key,value in self.edge_t2i.items() if value!=-_MISSING_ID}
463
465
  self.edge_i2t = {i: t for t, i in self.edge_t2i.items()}
464
466
 
465
467
  edge_dimtags = gmsh.model.get_entities(1)
@@ -479,6 +481,7 @@ class Mesh3D(Mesh):
479
481
  node_tags = [self.n_t2i[int(t)] for t in node_tags[0]]
480
482
  self.ftag_to_node[t] = node_tags
481
483
  node_tags = np.squeeze(np.array(node_tags)).reshape(-1,3).T
484
+
482
485
  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])]
483
486
  self.ftag_to_edge[t] = sorted(list(np.unique(self.tri_to_edge[:,self.ftag_to_tri[t]].flatten())))
484
487
 
@@ -502,7 +505,7 @@ class Mesh3D(Mesh):
502
505
  self.dimtag_to_center[dt] = gmsh.model.occ.get_center_of_mass(*dt)
503
506
  self.dimtag_to_edges[dt] = self._domain_edge(dt)
504
507
 
505
- logger.trace('Done analyzing mesh.')
508
+ logger.trace('Finalized mesh data generation!')
506
509
 
507
510
 
508
511
  ## Higher order functions
@@ -548,7 +551,7 @@ class Mesh3D(Mesh):
548
551
  node_ids_2_arry = np.array(node_ids_2)
549
552
  dv = np.array(bc.dv)
550
553
 
551
- nodemap = pair_coordinates(self.nodes, node_ids_1_arry, node_ids_2_arry, dv, dsmin/2)
554
+ nodemap = pair_coordinates(self.nodes, node_ids_1_arry, node_ids_2_arry, dv, dsmin/4)
552
555
  node_ids_2_unsorted = [nodemap[i] for i in sorted(node_ids_1)]
553
556
  node_ids_2_sorted = sorted(node_ids_2_unsorted)
554
557
  conv_map = {i1: i2 for i1, i2 in zip(node_ids_2_unsorted, node_ids_2_sorted)}