emerge 1.0.3__tar.gz → 1.0.5__tar.gz

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 (120) hide show
  1. {emerge-1.0.3 → emerge-1.0.5}/.bumpversion.toml +1 -1
  2. emerge-1.0.5/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
  3. {emerge-1.0.3 → emerge-1.0.5}/.github/ISSUE_TEMPLATE/feature_request.md +3 -0
  4. emerge-1.0.5/.nova/Configuration.json +3 -0
  5. {emerge-1.0.3 → emerge-1.0.5}/PKG-INFO +2 -3
  6. {emerge-1.0.3 → emerge-1.0.5}/README.md +1 -1
  7. {emerge-1.0.3 → emerge-1.0.5}/emerge/__init__.py +7 -3
  8. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/femdata.py +5 -1
  9. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/ned2_interp.py +73 -30
  10. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/nedelec2.py +1 -0
  11. emerge-1.0.5/emerge/_emerge/emerge_update.py +63 -0
  12. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/operations.py +6 -3
  13. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/polybased.py +37 -5
  14. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geometry.py +5 -0
  15. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/logsettings.py +26 -1
  16. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/material.py +29 -8
  17. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mesh3d.py +16 -13
  18. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mesher.py +70 -3
  19. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/assembler.py +5 -4
  20. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -1
  21. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +1 -2
  22. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +1 -1
  23. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/robin_abc_order2.py +0 -1
  24. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/microwave_3d.py +37 -16
  25. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/microwave_bc.py +6 -4
  26. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/microwave_data.py +14 -11
  27. emerge-1.0.5/emerge/_emerge/plot/pyvista/cmap_maker.py +70 -0
  28. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/pyvista/display.py +93 -36
  29. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/simmodel.py +77 -21
  30. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/simulation_data.py +22 -4
  31. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/solver.py +67 -32
  32. {emerge-1.0.3 → emerge-1.0.5}/examples/demo10_sgh.py +3 -5
  33. {emerge-1.0.3 → emerge-1.0.5}/examples/demo11_lumped_element_filter.py +3 -3
  34. {emerge-1.0.3 → emerge-1.0.5}/examples/demo12_mode_alignment.py +2 -2
  35. {emerge-1.0.3 → emerge-1.0.5}/examples/demo13_helix_antenna.py +2 -2
  36. {emerge-1.0.3 → emerge-1.0.5}/examples/demo14_boundary_selection.py +2 -2
  37. {emerge-1.0.3 → emerge-1.0.5}/examples/demo1_stepped_imp_filter.py +3 -3
  38. {emerge-1.0.3 → emerge-1.0.5}/examples/demo2_combline_filter.py +2 -2
  39. {emerge-1.0.3 → emerge-1.0.5}/examples/demo3_coupled_line_filter.py +3 -3
  40. {emerge-1.0.3 → emerge-1.0.5}/examples/demo4_patch_antenna.py +5 -4
  41. {emerge-1.0.3 → emerge-1.0.5}/examples/demo5_revolve.py +1 -2
  42. {emerge-1.0.3 → emerge-1.0.5}/examples/demo6_striplines_with_vias.py +3 -3
  43. {emerge-1.0.3 → emerge-1.0.5}/examples/demo7_periodic_cells.py +3 -3
  44. {emerge-1.0.3 → emerge-1.0.5}/examples/demo8_waveguide_bpf_synthesis.py +4 -4
  45. {emerge-1.0.3 → emerge-1.0.5}/examples/demo9_dielectric_resonator.py +2 -2
  46. {emerge-1.0.3 → emerge-1.0.5}/pyproject.toml +1 -2
  47. {emerge-1.0.3 → emerge-1.0.5}/uv.lock +1 -12
  48. emerge-1.0.3/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  49. {emerge-1.0.3 → emerge-1.0.5}/.gitignore +0 -0
  50. {emerge-1.0.3 → emerge-1.0.5}/.python-version +0 -0
  51. {emerge-1.0.3 → emerge-1.0.5}/LICENSE +0 -0
  52. {emerge-1.0.3 → emerge-1.0.5}/THIRD_PARTY_LICENSES.md +0 -0
  53. {emerge-1.0.3 → emerge-1.0.5}/UMFPACK_Install_windows.md +0 -0
  54. {emerge-1.0.3 → emerge-1.0.5}/UMFPACK_installer_windows.py +0 -0
  55. {emerge-1.0.3 → emerge-1.0.5}/emerge/__main__.py +0 -0
  56. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/__init__.py +0 -0
  57. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/bc.py +0 -0
  58. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/cacherun.py +0 -0
  59. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/const.py +0 -0
  60. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/coord.py +0 -0
  61. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/cs.py +0 -0
  62. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/dataset.py +0 -0
  63. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/__init__.py +0 -0
  64. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/index_interp.py +0 -0
  65. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/elements/nedleg2.py +0 -0
  66. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/__init__.py +0 -0
  67. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/horn.py +0 -0
  68. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/modeler.py +0 -0
  69. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/pcb.py +0 -0
  70. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  71. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/pcb_tools/dxf.py +0 -0
  72. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  73. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/pmlbox.py +0 -0
  74. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/shapes.py +0 -0
  75. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo/step.py +0 -0
  76. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/geo2d.py +0 -0
  77. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/howto.py +0 -0
  78. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mth/_cache_check.py +0 -0
  79. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mth/common_functions.py +0 -0
  80. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mth/integrals.py +0 -0
  81. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mth/optimized.py +0 -0
  82. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/mth/pairing.py +0 -0
  83. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/periodic.py +0 -0
  84. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/__init__.py +0 -0
  85. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/__init__.py +0 -0
  86. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  87. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
  88. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
  89. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  90. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
  91. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/sc.py +0 -0
  92. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  93. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  94. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  95. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/__init__.py +0 -0
  96. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/display.py +0 -0
  97. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  98. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  99. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  100. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot/simple_plots.py +0 -0
  101. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/plot.py +0 -0
  102. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/projects/__init__.py +0 -0
  103. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/projects/_gen_base.txt +0 -0
  104. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/projects/_load_base.txt +0 -0
  105. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/projects/generate_project.py +0 -0
  106. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/selection.py +0 -0
  107. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/settings.py +0 -0
  108. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  109. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  110. {emerge-1.0.3 → emerge-1.0.5}/emerge/_emerge/system.py +0 -0
  111. {emerge-1.0.3 → emerge-1.0.5}/emerge/beta/dxf.py +0 -0
  112. {emerge-1.0.3 → emerge-1.0.5}/emerge/cli.py +0 -0
  113. {emerge-1.0.3 → emerge-1.0.5}/emerge/ext.py +0 -0
  114. {emerge-1.0.3 → emerge-1.0.5}/emerge/lib.py +0 -0
  115. {emerge-1.0.3 → emerge-1.0.5}/emerge/materials/__init__.py +0 -0
  116. {emerge-1.0.3 → emerge-1.0.5}/emerge/materials/isola.py +0 -0
  117. {emerge-1.0.3 → emerge-1.0.5}/emerge/materials/rogers.py +0 -0
  118. {emerge-1.0.3 → emerge-1.0.5}/emerge/plot.py +0 -0
  119. {emerge-1.0.3 → emerge-1.0.5}/emerge/pyvista.py +0 -0
  120. {emerge-1.0.3 → emerge-1.0.5}/src/__init__.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.0.3"
2
+ current_version = "1.0.5"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  search = "{current_version}"
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: FennisRobert
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce if applicable**
14
+ Provide the script you are running if possible or the lines of code that triggered it.
15
+
16
+ **Logfile**
17
+ By setting `emerge.SImulation('modelname', write_log=True)`, EMerge will dump a logging.log file in `modelname.EMResults/`. You can share this file. Make sure to vet it for potential confidential information and system information you might not want to share. The log file may contain
18
+ - Module version numbers
19
+ - Python version number
20
+ - OS
21
+ - Architecture
22
+ - CPU count
23
+
24
+ **Additional context**
25
+ Add any other context about the problem here.
@@ -18,3 +18,6 @@ A clear and concise description of any alternative solutions or features you've
18
18
 
19
19
  **Additional context**
20
20
  Add any other context or screenshots about the feature request here.
21
+
22
+ **IMPORTANT**
23
+ If you propose any support for file imports, please include an example file for me to use. Without file to be included, feature requests will not be worked on.
@@ -0,0 +1,3 @@
1
+ {
2
+ "python.pythonPath" : "\/Users\/robertfennis\/Documents\/EMerge\/.venv\/bin\/python"
3
+ }
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 1.0.3
3
+ Version: 1.0.5
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
7
7
  License-File: LICENSE
8
8
  Requires-Python: <4.0,>=3.10
9
- Requires-Dist: cloudpickle>=3.1.1
10
9
  Requires-Dist: gmsh<4.14.0,>=4.13.0
11
10
  Requires-Dist: joblib>=1.5.1
12
11
  Requires-Dist: loguru>=0.7.3
@@ -33,7 +32,7 @@ Hello everybody. Thanks for showing interest in this repository.
33
32
  Feel free to download your version of EMerge and start playing around with it!
34
33
  If you have suggestions/changes/questions either use the Github issue system or join the Discord using the following link:
35
34
 
36
- **https://discord.gg/7PF4WcS6uA**
35
+ **[Discord Invitation](https://discord.gg/VMftDCZcNz)**
37
36
 
38
37
  ## How to install
39
38
 
@@ -5,7 +5,7 @@ Hello everybody. Thanks for showing interest in this repository.
5
5
  Feel free to download your version of EMerge and start playing around with it!
6
6
  If you have suggestions/changes/questions either use the Github issue system or join the Discord using the following link:
7
7
 
8
- **https://discord.gg/7PF4WcS6uA**
8
+ **[Discord Invitation](https://discord.gg/VMftDCZcNz)**
9
9
 
10
10
  ## How to install
11
11
 
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "1.0.3"
21
+ __version__ = "1.0.5"
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
@@ -51,11 +51,13 @@ def add(main: T, tool: T,
51
51
  GeoSurface | GeoVolume
52
52
  A new object that is the union of the main and tool objects.
53
53
  '''
54
+
54
55
  out_dim_tags, out_dim_tags_map = gmsh.model.occ.fuse(main.dimtags, tool.dimtags, removeObject=remove_object, removeTool=remove_tool)
55
56
  if out_dim_tags[0][0] == 3:
56
57
  output = GeoVolume([dt[1] for dt in out_dim_tags])._take_tools(tool,main)
57
58
  elif out_dim_tags[0][0] == 2:
58
59
  output = GeoSurface([dt[1] for dt in out_dim_tags])._take_tools(tool,main)
60
+
59
61
  if remove_object:
60
62
  main._exists = False
61
63
  if remove_tool:
@@ -300,15 +302,16 @@ def unite(*objects: GeoObject) -> GeoObject:
300
302
  GeoObject: The resultant object
301
303
  """
302
304
  main, *rest = objects
305
+
303
306
  if not rest:
304
307
  return main
308
+
305
309
  main._exists = False
306
310
  dts = []
307
- for other in rest:
311
+ for other in objects:
308
312
  dts.extend(other.dimtags)
309
313
  other._exists = False
310
-
311
- new_dimtags, mapping = gmsh.model.occ.fuse(main.dimtags, dts)
314
+ new_dimtags, mapping = gmsh.model.occ.fuse(dts, main.dimtags)
312
315
 
313
316
  new_obj = GeoObject.from_dimtags(new_dimtags)._take_tools(*objects)
314
317
  new_obj.set_material(main.material)
@@ -24,6 +24,7 @@ from typing import Generator, Callable
24
24
  from ..selection import FaceSelection
25
25
  from typing import Literal
26
26
  from functools import reduce
27
+ from loguru import logger
27
28
 
28
29
 
29
30
  def _discretize_curve(xfunc: Callable, yfunc: Callable,
@@ -238,7 +239,8 @@ class XYPolygon:
238
239
  def __init__(self,
239
240
  xs: np.ndarray | list | tuple | None = None,
240
241
  ys: np.ndarray | list | tuple | None = None,
241
- cs: CoordinateSystem | None = None):
242
+ cs: CoordinateSystem | None = None,
243
+ resolution: float = 1e-6):
242
244
  """Constructs an XY-plane placed polygon.
243
245
 
244
246
  Args:
@@ -256,6 +258,7 @@ class XYPolygon:
256
258
  self.fillets: list[tuple[float, int]] = []
257
259
 
258
260
  self._cs: CoordinateSystem = cs
261
+ self.resolution: float = resolution
259
262
 
260
263
  @property
261
264
  def center(self) -> tuple[float, float]:
@@ -279,7 +282,7 @@ class XYPolygon:
279
282
  The XYPolygon does not store redundant points p[0]==p[N] so if these are
280
283
  the same, this function will remove the last point.
281
284
  """
282
- if np.sqrt((self.x[-1]-self.x[0])**2 + (self.y[-1]-self.y[0])**2) < 1e-6:
285
+ if np.sqrt((self.x[-1]-self.x[0])**2 + (self.y[-1]-self.y[0])**2) < 1e-9:
283
286
  self.x = self.x[:-1]
284
287
  self.y = self.y[:-1]
285
288
 
@@ -329,6 +332,23 @@ class XYPolygon:
329
332
  self.fillets.append((radius, i))
330
333
  return self
331
334
 
335
+ def _cleanup(self, resolution: float | None = None) -> None:
336
+ # Compute differences between consecutive points
337
+ if resolution is None:
338
+ resolution = self.resolution
339
+ dx = np.diff(self.x)
340
+ dy = np.diff(self.y)
341
+
342
+ # Distances between consecutive points
343
+ dist = np.sqrt(dx**2 + dy**2)
344
+
345
+ # Keep the first point, then points where distance >= threshold
346
+ keep = np.insert(dist >= resolution, 1, True)
347
+
348
+ # Apply mask
349
+ self.x = self.x[keep]
350
+ self.y = self.y[keep]
351
+
332
352
  def _make_wire(self, cs: CoordinateSystem) -> tuple[list[int], list[int], int]:
333
353
  """Turns the XYPolygon object into a GeoPolygon that is embedded in 3D space.
334
354
 
@@ -343,10 +363,23 @@ class XYPolygon:
343
363
  self._check()
344
364
 
345
365
  ptags = []
366
+
346
367
  xg, yg, zg = cs.in_global_cs(self.x, self.y, 0*self.x)
347
368
 
369
+ points = dict()
348
370
  for x,y,z in zip(xg, yg, zg):
349
- ptags.append(gmsh.model.occ.add_point(x,y,z))
371
+ reuse = False
372
+ for key, (px, py, pz) in points.items():
373
+ if ((x-px)**2 + (y-py)**2 + (z-pz)**2)**0.5 < 1e-12:
374
+ ptags.append(key)
375
+ reuse = True
376
+ break
377
+ if reuse:
378
+ logger.warning(f'Reusing {ptags[-1]}')
379
+ continue
380
+ ptag = gmsh.model.occ.add_point(x,y,z)
381
+ points[ptag] = (x,y,z)
382
+ ptags.append(ptag)
350
383
 
351
384
  lines = []
352
385
  for i1, p1 in enumerate(ptags):
@@ -375,6 +408,7 @@ class XYPolygon:
375
408
  Returns:
376
409
  GeoPolygon: The resultant 3D GeoPolygon object.
377
410
  """
411
+ self._cleanup()
378
412
  ptags, lines, wiretag = self._make_wire(cs)
379
413
  surftag = gmsh.model.occ.add_plane_surface([wiretag,])
380
414
  poly = GeoPolygon([surftag,], name=name)
@@ -635,8 +669,6 @@ class Curve(GeoEdge):
635
669
  self.dstart: tuple[float, float, float] = (p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2])
636
670
 
637
671
 
638
-
639
-
640
672
  @property
641
673
  def p0(self) -> tuple[float, float, float]:
642
674
  """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,