emerge 1.0.1__tar.gz → 1.0.2__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 (117) hide show
  1. {emerge-1.0.1 → emerge-1.0.2}/.bumpversion.toml +1 -1
  2. {emerge-1.0.1 → emerge-1.0.2}/PKG-INFO +16 -4
  3. {emerge-1.0.1 → emerge-1.0.2}/README.md +15 -3
  4. {emerge-1.0.1 → emerge-1.0.2}/emerge/__init__.py +6 -7
  5. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/femdata.py +4 -3
  6. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/nedelec2.py +8 -4
  7. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/nedleg2.py +6 -2
  8. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/pcb.py +6 -2
  9. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/pcb_tools/dxf.py +4 -3
  10. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/polybased.py +2 -59
  11. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mesh3d.py +23 -31
  12. {emerge-1.0.1/emerge/_emerge → emerge-1.0.2/emerge/_emerge/mth}/_cache_check.py +2 -2
  13. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mth/optimized.py +69 -3
  14. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/assembler.py +27 -5
  15. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +2 -3
  16. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -1
  17. emerge-1.0.2/emerge/_emerge/physics/microwave/assembly/robin_abc_order2.py +375 -0
  18. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/robinbc.py +37 -38
  19. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/microwave_3d.py +11 -19
  20. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/microwave_bc.py +21 -17
  21. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/microwave_data.py +0 -26
  22. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/port_functions.py +4 -4
  23. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/pyvista/display.py +4 -1
  24. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/selection.py +2 -1
  25. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/simmodel.py +1 -1
  26. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/solver.py +19 -14
  27. {emerge-1.0.1 → emerge-1.0.2}/emerge/lib.py +0 -2
  28. {emerge-1.0.1 → emerge-1.0.2}/examples/demo10_sgh.py +1 -1
  29. {emerge-1.0.1 → emerge-1.0.2}/examples/demo11_lumped_element_filter.py +1 -1
  30. {emerge-1.0.1 → emerge-1.0.2}/examples/demo12_mode_alignment.py +1 -1
  31. {emerge-1.0.1 → emerge-1.0.2}/examples/demo13_helix_antenna.py +1 -1
  32. {emerge-1.0.1 → emerge-1.0.2}/examples/demo14_boundary_selection.py +1 -1
  33. {emerge-1.0.1 → emerge-1.0.2}/examples/demo1_stepped_imp_filter.py +2 -2
  34. {emerge-1.0.1 → emerge-1.0.2}/examples/demo2_combline_filter.py +1 -1
  35. {emerge-1.0.1 → emerge-1.0.2}/examples/demo3_coupled_line_filter.py +1 -1
  36. {emerge-1.0.1 → emerge-1.0.2}/examples/demo4_patch_antenna.py +1 -1
  37. {emerge-1.0.1 → emerge-1.0.2}/examples/demo5_revolve.py +1 -1
  38. {emerge-1.0.1 → emerge-1.0.2}/examples/demo6_striplines_with_vias.py +1 -1
  39. {emerge-1.0.1 → emerge-1.0.2}/examples/demo7_periodic_cells.py +1 -1
  40. {emerge-1.0.1 → emerge-1.0.2}/examples/demo8_waveguide_bpf_synthesis.py +1 -1
  41. {emerge-1.0.1 → emerge-1.0.2}/examples/demo9_dielectric_resonator.py +1 -1
  42. {emerge-1.0.1 → emerge-1.0.2}/pyproject.toml +1 -1
  43. emerge-1.0.2/src/__init__.py +0 -0
  44. {emerge-1.0.1 → emerge-1.0.2}/uv.lock +1 -1
  45. emerge-1.0.1/emerge/_emerge/physics/microwave/__init__.py +0 -1
  46. {emerge-1.0.1 → emerge-1.0.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  47. {emerge-1.0.1 → emerge-1.0.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  48. {emerge-1.0.1 → emerge-1.0.2}/.gitignore +0 -0
  49. {emerge-1.0.1 → emerge-1.0.2}/.python-version +0 -0
  50. {emerge-1.0.1 → emerge-1.0.2}/LICENSE +0 -0
  51. {emerge-1.0.1 → emerge-1.0.2}/THIRD_PARTY_LICENSES.md +0 -0
  52. {emerge-1.0.1 → emerge-1.0.2}/UMFPACK_Install_windows.md +0 -0
  53. {emerge-1.0.1 → emerge-1.0.2}/UMFPACK_installer_windows.py +0 -0
  54. {emerge-1.0.1 → emerge-1.0.2}/emerge/__main__.py +0 -0
  55. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/__init__.py +0 -0
  56. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/bc.py +0 -0
  57. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/cacherun.py +0 -0
  58. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/const.py +0 -0
  59. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/coord.py +0 -0
  60. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/cs.py +0 -0
  61. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/dataset.py +0 -0
  62. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/__init__.py +0 -0
  63. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/index_interp.py +0 -0
  64. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/elements/ned2_interp.py +0 -0
  65. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/__init__.py +0 -0
  66. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/horn.py +0 -0
  67. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/modeler.py +0 -0
  68. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/operations.py +0 -0
  69. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
  70. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
  71. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/pmlbox.py +0 -0
  72. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/shapes.py +0 -0
  73. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo/step.py +0 -0
  74. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geo2d.py +0 -0
  75. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/geometry.py +0 -0
  76. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/howto.py +0 -0
  77. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/logsettings.py +0 -0
  78. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/material.py +0 -0
  79. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mesher.py +0 -0
  80. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mth/common_functions.py +0 -0
  81. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mth/integrals.py +0 -0
  82. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/mth/pairing.py +0 -0
  83. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/periodic.py +0 -0
  84. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/__init__.py +0 -0
  85. {emerge-1.0.1/emerge/_emerge/plot → emerge-1.0.2/emerge/_emerge/physics/microwave}/__init__.py +0 -0
  86. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
  87. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
  88. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
  89. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/periodic.py +0 -0
  90. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/sc.py +0 -0
  91. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/simjob.py +0 -0
  92. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/sparam.py +0 -0
  93. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
  94. {emerge-1.0.1/emerge/_emerge/projects → emerge-1.0.2/emerge/_emerge/plot}/__init__.py +0 -0
  95. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/display.py +0 -0
  96. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
  97. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
  98. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
  99. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot/simple_plots.py +0 -0
  100. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/plot.py +0 -0
  101. {emerge-1.0.1/src → emerge-1.0.2/emerge/_emerge/projects}/__init__.py +0 -0
  102. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/projects/_gen_base.txt +0 -0
  103. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/projects/_load_base.txt +0 -0
  104. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/projects/generate_project.py +0 -0
  105. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/settings.py +0 -0
  106. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/simulation_data.py +0 -0
  107. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
  108. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
  109. {emerge-1.0.1 → emerge-1.0.2}/emerge/_emerge/system.py +0 -0
  110. {emerge-1.0.1 → emerge-1.0.2}/emerge/beta/dxf.py +0 -0
  111. {emerge-1.0.1 → emerge-1.0.2}/emerge/cli.py +0 -0
  112. {emerge-1.0.1 → emerge-1.0.2}/emerge/ext.py +0 -0
  113. {emerge-1.0.1 → emerge-1.0.2}/emerge/materials/__init__.py +0 -0
  114. {emerge-1.0.1 → emerge-1.0.2}/emerge/materials/isola.py +0 -0
  115. {emerge-1.0.1 → emerge-1.0.2}/emerge/materials/rogers.py +0 -0
  116. {emerge-1.0.1 → emerge-1.0.2}/emerge/plot.py +0 -0
  117. {emerge-1.0.1 → emerge-1.0.2}/emerge/pyvista.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.0.1"
2
+ current_version = "1.0.2"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  search = "{current_version}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emerge
3
- Version: 1.0.1
3
+ Version: 1.0.2
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
@@ -44,8 +44,17 @@ pip install emerge
44
44
  On MacOS and Linux you can install it with the very fast UMFPACK through scikit-umfpack
45
45
 
46
46
  ```
47
- brew install swig suite-sparse #MacOS
47
+ brew install cmake swig suite-sparse #MacOS
48
48
  sudo apt-get install libsuitesparse-dev #Linux
49
+ ```
50
+ Then on MacOS do:
51
+ ```
52
+ export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"
53
+ export CFLAGS="-I/opt/homebrew/include"
54
+ export LDFLAGS="-L/opt/homebrew/lib"
55
+ ```
56
+ Finally:
57
+ ```
49
58
  pip install emerge[umfpack]
50
59
  ```
51
60
 
@@ -55,11 +64,13 @@ If you have a new NVidia card you can try the first test implementation of the c
55
64
  ```
56
65
  pip install emerge[cudss]
57
66
  ```
58
- The `scikit-umfpack` solver can be installed on Windows as well from binaries with conda. This is a bit more complicated and is described in the installation guide.
67
+ The `scikit-umfpack` solver can be installed on Windows as well from binaries with conda. This is a bit more complicated and is described in the installation guide which can be downloaded from the official website:
68
+
69
+ https://www.emerge-software.com/resources
59
70
 
60
71
  ## Compatibility
61
72
 
62
- As far as I know, the library should work on all systems. PyPARDISO is not supported on ARM but the current SuperLU and UMFPACK solvers work on ARM as well. Both SuperLU and UMFPACK can run on multi-processing implementations as long as you do entry-point protection:
73
+ As far as I know, the library should work on all systems. PARDISO is not supported on ARM but the current SuperLU and UMFPACK solvers work on ARM as well. Both SuperLU and UMFPACK can run on multi-processing implementations as long as you do entry-point protection:
63
74
  ```
64
75
  import emerge as em
65
76
 
@@ -90,6 +101,7 @@ To run this FEM library you need the following libraries
90
101
  Optional:
91
102
  - scikit-umfpack
92
103
  - cudss
104
+ - ezdxf
93
105
 
94
106
  ## Resources / Manual
95
107
 
@@ -16,8 +16,17 @@ pip install emerge
16
16
  On MacOS and Linux you can install it with the very fast UMFPACK through scikit-umfpack
17
17
 
18
18
  ```
19
- brew install swig suite-sparse #MacOS
19
+ brew install cmake swig suite-sparse #MacOS
20
20
  sudo apt-get install libsuitesparse-dev #Linux
21
+ ```
22
+ Then on MacOS do:
23
+ ```
24
+ export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"
25
+ export CFLAGS="-I/opt/homebrew/include"
26
+ export LDFLAGS="-L/opt/homebrew/lib"
27
+ ```
28
+ Finally:
29
+ ```
21
30
  pip install emerge[umfpack]
22
31
  ```
23
32
 
@@ -27,11 +36,13 @@ If you have a new NVidia card you can try the first test implementation of the c
27
36
  ```
28
37
  pip install emerge[cudss]
29
38
  ```
30
- The `scikit-umfpack` solver can be installed on Windows as well from binaries with conda. This is a bit more complicated and is described in the installation guide.
39
+ The `scikit-umfpack` solver can be installed on Windows as well from binaries with conda. This is a bit more complicated and is described in the installation guide which can be downloaded from the official website:
40
+
41
+ https://www.emerge-software.com/resources
31
42
 
32
43
  ## Compatibility
33
44
 
34
- As far as I know, the library should work on all systems. PyPARDISO is not supported on ARM but the current SuperLU and UMFPACK solvers work on ARM as well. Both SuperLU and UMFPACK can run on multi-processing implementations as long as you do entry-point protection:
45
+ As far as I know, the library should work on all systems. PARDISO is not supported on ARM but the current SuperLU and UMFPACK solvers work on ARM as well. Both SuperLU and UMFPACK can run on multi-processing implementations as long as you do entry-point protection:
35
46
  ```
36
47
  import emerge as em
37
48
 
@@ -62,6 +73,7 @@ To run this FEM library you need the following libraries
62
73
  Optional:
63
74
  - scikit-umfpack
64
75
  - cudss
76
+ - ezdxf
65
77
 
66
78
  ## Resources / Manual
67
79
 
@@ -18,7 +18,7 @@ along with this program; if not, see
18
18
  """
19
19
  import os
20
20
 
21
- __version__ = "1.0.1"
21
+ __version__ = "1.0.2"
22
22
 
23
23
  ############################################################
24
24
  # HANDLE ENVIRONMENT VARIABLES #
@@ -33,11 +33,10 @@ os.environ["OPENBLAS_NUM_THREADS"] = NTHREADS
33
33
  os.environ["VECLIB_MAXIMUM_THREADS"] = NTHREADS
34
34
  os.environ["NUMEXPR_NUM_THREADS"] = NTHREADS
35
35
 
36
-
37
36
  ############################################################
38
37
  # IMPORT MODULES #
39
38
  ############################################################
40
- from ._emerge import _cache_check
39
+
41
40
  from ._emerge.logsettings import LOG_CONTROLLER
42
41
  from loguru import logger
43
42
 
@@ -54,8 +53,7 @@ from ._emerge.coord import Line
54
53
  from ._emerge import geo
55
54
  from ._emerge.selection import Selection, FaceSelection, DomainSelection, EdgeSelection
56
55
  from ._emerge.geometry import select
57
- from ._emerge.mth.common_functions import norm, coax_rout, coax_rin
58
- from ._emerge.physics.microwave.sc import stratton_chu
56
+ #from ._emerge.mth.common_functions import norm, coax_rout, coax_rin
59
57
  from ._emerge.periodic import RectCell, HexCell
60
58
  from ._emerge.mesher import Algorithm2D, Algorithm3D
61
59
  from . import lib
@@ -65,10 +63,11 @@ howto = _HowtoClass()
65
63
 
66
64
  logger.debug('Importing complete!')
67
65
 
68
-
69
66
  ############################################################
70
67
  # CONSTANTS #
71
68
  ############################################################
72
69
 
73
70
  CENTER = geo.Alignment.CENTER
74
- CORNER = geo.Alignment.CORNER
71
+ CORNER = geo.Alignment.CORNER
72
+ EISO = lib.EISO
73
+ EOMNI = lib.EOMNI
@@ -19,9 +19,7 @@ from __future__ import annotations
19
19
  from ..mesh3d import Mesh3D
20
20
  import numpy as np
21
21
  from typing import Callable
22
- from scipy.sparse import csr_matrix # type: ignore
23
22
 
24
- from ..mth.optimized import matmul
25
23
 
26
24
  class FEMBasis:
27
25
 
@@ -48,6 +46,8 @@ class FEMBasis:
48
46
 
49
47
  def interpolate_Ef(self, field: np.ndarray, basis: np.ndarray | None = None, origin: np.ndarray | None = None, tetids: np.ndarray | None = None) -> Callable:
50
48
  '''Generates the Interpolation function as a function object for a given coordiante basis and origin.'''
49
+ from ..mth.optimized import matmul
50
+
51
51
  if basis is None:
52
52
  basis = np.eye(3)
53
53
 
@@ -124,7 +124,8 @@ class FEMBasis:
124
124
  N = self.n_tri_dofs**2
125
125
  return slice(itri*N,(itri+1)*N)
126
126
 
127
- def generate_csr(self, data: np.ndarray) -> csr_matrix:
127
+ def generate_csr(self, data: np.ndarray):
128
+ from scipy.sparse import csr_matrix # type: ignore
128
129
  ids = np.argwhere(data!=0)[:,0]
129
130
  return csr_matrix((data[ids], (self._rows[ids], self._cols[ids])), shape=(self.n_field, self.n_field))
130
131
  ### QUANTITIES
@@ -19,9 +19,6 @@ from __future__ import annotations
19
19
  import numpy as np
20
20
  from ..mesh3d import Mesh3D
21
21
  from .femdata import FEMBasis
22
- from .ned2_interp import ned2_tet_interp, ned2_tet_interp_curl
23
- from ..mth.optimized import local_mapping
24
- from .index_interp import index_interp
25
22
 
26
23
  ############### Nedelec2 Class
27
24
 
@@ -72,6 +69,7 @@ class Nedelec2(FEMBasis):
72
69
  '''
73
70
  Interpolate the provided field data array at the given xs, ys and zs coordinates
74
71
  '''
72
+ from .ned2_interp import ned2_tet_interp
75
73
  if tetids is None:
76
74
  tetids = self._all_tet_ids
77
75
  vals = ned2_tet_interp(np.array([xs, ys, zs]), field, self.mesh.tets, self.mesh.tris, self.mesh.edges, self.mesh.nodes, self.tet_to_field, tetids)
@@ -83,8 +81,11 @@ class Nedelec2(FEMBasis):
83
81
  """
84
82
  Interpolates the curl of the field at the given points.
85
83
  """
84
+ from .ned2_interp import ned2_tet_interp_curl
85
+
86
86
  if tetids is None:
87
87
  tetids = self._all_tet_ids
88
+
88
89
  vals = ned2_tet_interp_curl(np.array([xs, ys, zs]), field, self.mesh.tets, self.mesh.tris, self.mesh.edges, self.mesh.nodes, self.tet_to_field, c, tetids)
89
90
  if not usenan:
90
91
  vals = np.nan_to_num(vals)
@@ -97,7 +98,7 @@ class Nedelec2(FEMBasis):
97
98
  usenan: bool = True) -> np.ndarray:
98
99
  if tetids is None:
99
100
  tetids = self._all_tet_ids
100
-
101
+ from .index_interp import index_interp
101
102
  vals = index_interp(np.array([xs, ys, zs]), self.mesh.tets, self.mesh.nodes, tetids)
102
103
  if not usenan:
103
104
  vals[vals==-1]==0
@@ -106,15 +107,18 @@ class Nedelec2(FEMBasis):
106
107
  ###### INDEX MAPPINGS
107
108
 
108
109
  def local_tet_to_triid(self, itet: int) -> np.ndarray:
110
+ from ..mth.optimized import local_mapping
109
111
  tri_ids = self.tet_to_field[6:10, itet] - self.n_edges
110
112
  global_tri_map = self.mesh.tris[:, tri_ids]
111
113
  return local_mapping(self.mesh.tets[:, itet], global_tri_map)
112
114
 
113
115
  def local_tet_to_edgeid(self, itet: int) -> np.ndarray:
116
+ from ..mth.optimized import local_mapping
114
117
  global_edge_map = self.mesh.edges[:, self.tet_to_field[:6,itet]]
115
118
  return local_mapping(self.mesh.tets[:, itet], global_edge_map)
116
119
 
117
120
  def local_tri_to_edgeid(self, itri: int) -> np.ndarray:
121
+ from ..mth.optimized import local_mapping
118
122
  global_edge_map = self.mesh.edges[:, self.tri_to_field[:3,itri]]
119
123
  return local_mapping(self.mesh.tris[:, itri], global_edge_map)
120
124
 
@@ -19,8 +19,6 @@ from __future__ import annotations
19
19
  import numpy as np
20
20
  from ..mesh3d import SurfaceMesh
21
21
  from .femdata import FEMBasis
22
- from .ned2_interp import ned2_tri_interp_full, ned2_tri_interp_curl
23
- from ..mth.optimized import matinv
24
22
  from ..cs import CoordinateSystem
25
23
  from ..const import MU0, C0
26
24
 
@@ -74,6 +72,8 @@ class FieldFunctionClass:
74
72
  return np.array([Fx, Fy, Fz])*self.constant
75
73
 
76
74
  def calcE(self, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
75
+ from .ned2_interp import ned2_tri_interp_full
76
+
77
77
  coordinates = np.array([xs, ys])
78
78
  vals = ned2_tri_interp_full(coordinates,
79
79
  self.field,
@@ -85,6 +85,7 @@ class FieldFunctionClass:
85
85
  return vals
86
86
 
87
87
  def calcH(self, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
88
+ from .ned2_interp import ned2_tri_interp_curl
88
89
  coordinates = np.array([xs, ys])
89
90
 
90
91
  vals = ned2_tri_interp_curl(coordinates,
@@ -176,6 +177,7 @@ class NedelecLegrange2(FEMBasis):
176
177
 
177
178
  def interpolate_Hf(self, field: np.ndarray, k0: float, ur: np.ndarray, beta: float) -> FieldFunctionClass:
178
179
  '''Generates the Interpolation function as a function object for a given coordiante basis and origin.'''
180
+ from ..mth.optimized import matinv
179
181
  constant = 1j / ((k0*C0)*MU0)
180
182
  urinv = np.zeros_like(ur)
181
183
 
@@ -185,6 +187,7 @@ class NedelecLegrange2(FEMBasis):
185
187
  return FieldFunctionClass(field, self.cs, self.local_nodes, self.mesh.tris, self.tri_to_field, 'H', urinv, beta, constant)
186
188
 
187
189
  def tri_interpolate(self, field, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
190
+ from .ned2_interp import ned2_tri_interp_full
188
191
  coordinates = np.array([xs, ys])
189
192
  vals = ned2_tri_interp_full(coordinates,
190
193
  field,
@@ -196,6 +199,7 @@ class NedelecLegrange2(FEMBasis):
196
199
  return vals
197
200
 
198
201
  def tri_interpolate_curl(self, field, xs: np.ndarray, ys: np.ndarray, diadic: np.ndarray | None = None, beta: float = 0.0, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
202
+ from .ned2_interp import ned2_tri_interp_curl
199
203
  coordinates = np.array([xs, ys])
200
204
  if diadic is None:
201
205
  diadic = np.eye(3)[:,:,np.newaxis()] * np.ones((self.mesh.n_tris)) # type: ignore
@@ -958,6 +958,7 @@ class PCB:
958
958
  stack: list[PCBLayer] = None,
959
959
  name: str | None = None,
960
960
  trace_thickness: float | None = None,
961
+ zs: np.ndarray | None = None
961
962
  ):
962
963
  """Creates a new PCB layout class instance
963
964
 
@@ -975,8 +976,11 @@ class PCB:
975
976
 
976
977
  self.thickness: float = thickness
977
978
  self._stack: list[PCBLayer] = []
978
-
979
- if stack is not None:
979
+ if zs is not None:
980
+ self._zs = zs
981
+ self.thickness = np.max(zs)-np.min(zs)
982
+ self._stack = [PCBLayer(th, material) for th in np.diff(self._zs)]
983
+ elif stack is not None:
980
984
  self._stack = stack
981
985
  ths = [ly.th for ly in stack]
982
986
  zbot = -sum(ths)
@@ -325,8 +325,9 @@ def import_dxf(filename: str,
325
325
  unit: float | None = None,
326
326
  cs: CoordinateSystem | None = GCS,
327
327
  trace_material: Material = PEC) -> PCB:
328
+
328
329
  polies = extract_polygons_with_meta(filename)
329
- prop = inspect_pcb_from_dxf('LP5G_Chb3.dxf')
330
+ prop = inspect_pcb_from_dxf(filename)
330
331
 
331
332
  if prop['units']['name'] == 'unitless':
332
333
  if unit is None:
@@ -335,7 +336,7 @@ def import_dxf(filename: str,
335
336
  else:
336
337
  pcb_unit = 0.001 * prop['units']['to_mm']
337
338
 
338
- if prop['thickness']==0.0:
339
+ if prop['thickness'] == 0.0:
339
340
  if thickness is None:
340
341
  raise RouteException(f'Cannot generate PCB because no thickness is found int he DXF file and none is provided in the import_dxf function.')
341
342
  pcb_thickness = thickness
@@ -346,7 +347,7 @@ def import_dxf(filename: str,
346
347
  cs = GCS
347
348
 
348
349
  zs = sorted(list(set([pol['z'] for pol in polies])))
349
- pcb = PCB(pcb_thickness, pcb_unit, cs, material=material, trace_material=trace_material, _zs=zs)
350
+ pcb = PCB(pcb_thickness, pcb_unit, cs, material=material, trace_material=trace_material)
350
351
 
351
352
  for poly in polies:
352
353
  xs, ys = zip(*poly['ring'])
@@ -24,66 +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 numba import njit
28
27
 
29
- @njit(cache=True)
30
- def _subsample_coordinates(xs: np.ndarray, ys: np.ndarray, tolerance: float, xmin: float) -> tuple[np.ndarray, np.ndarray]:
31
- """This function takes a set of x and y coordinates in a finely sampled set and returns a reduced
32
- set of numbers that traces the input curve within a provided tolerance.
33
-
34
- Args:
35
- xs (np.ndarray): The set of X-coordinates
36
- ys (np.ndarray): The set of Y-coordinates
37
- tolerance (float): The maximum deviation of the curve in meters
38
- xmin (float): The minimal distance to the next point.
39
-
40
- Returns:
41
- np.ndarray: The output X-coordinates
42
- np.ndarray: The output Y-coordinates
43
- """
44
- N = xs.shape[0]
45
- ids = np.zeros((N,), dtype=np.int32)
46
- store_index = 1
47
- start_index = 0
48
- final_index = 0
49
- for iteration in range(N):
50
- i1 = start_index
51
- done = 0
52
- for i2 in range(i1+1,N):
53
- x_true = xs[i1:i2+1]
54
- y_true = ys[i1:i2+1]
55
-
56
- x_f = np.linspace(xs[i1],xs[i2], i2-i1+1)
57
- y_f = np.linspace(ys[i1],ys[i2], i2-i1+1)
58
- error = np.max(np.sqrt((x_f-x_true)**2 + (y_f-y_true)**2))
59
- ds = np.sqrt((xs[i2]-xs[i1])**2 + (ys[i2]-ys[i1])**2)
60
- # If at the end
61
- if i2==N-1:
62
- ids[store_index] = i2-1
63
- final_index = store_index + 1
64
- done = 1
65
- break
66
- # If not yet past the minimum distance, accumulate more
67
- if ds < xmin:
68
- continue
69
- # If the end is less than a minimum distance
70
- if np.sqrt((ys[-1]-ys[i2])**2 + (xs[-1]-xs[i2])**2) < xmin:
71
- imid = i1 + (N-1-i1)//2
72
- ids[store_index] = imid
73
- ids[store_index+1] = N-1
74
- final_index = store_index + 2
75
- done = 1
76
- break
77
- if error < tolerance:
78
- continue
79
- else:
80
- ids[store_index] = i2-1
81
- start_index = i2
82
- store_index = store_index + 1
83
- break
84
- if done==1:
85
- break
86
- return xs[ids[0:final_index]], ys[ids[0:final_index]]
87
28
 
88
29
  def _discretize_curve(xfunc: Callable, yfunc: Callable,
89
30
  t0: float, t1: float, xmin: float, tol: float=1e-4) -> tuple[np.ndarray, np.ndarray]:
@@ -100,6 +41,8 @@ def _discretize_curve(xfunc: Callable, yfunc: Callable,
100
41
  Returns:
101
42
  tuple[np.ndarray, np.ndarray]: _description_
102
43
  """
44
+ from ..mth.optimized import _subsample_coordinates
45
+
103
46
  td = np.linspace(t0, t1, 10001)
104
47
  xs = xfunc(td)
105
48
  ys = yfunc(td)
@@ -18,25 +18,14 @@
18
18
  from __future__ import annotations
19
19
  import gmsh # type: ignore
20
20
  import numpy as np
21
- from numba import njit, f8 # type: ignore
22
21
  from .mesher import Mesher
23
22
  from typing import Union, List, Tuple, Callable, Any
24
23
  from collections import defaultdict
25
24
  from .geometry import GeoVolume
26
- from .mth.optimized import outward_normal
27
25
  from loguru import logger
28
- from functools import cache
29
26
  from .bc import Periodic
30
- from .mth.pairing import pair_coordinates
31
27
  from .material import Material
32
28
 
33
- @njit(f8(f8[:], f8[:], f8[:]), cache=True, nogil=True)
34
- def area(x1: np.ndarray, x2: np.ndarray, x3: np.ndarray):
35
- e1 = x2 - x1
36
- e2 = x3 - x1
37
- av = np.array([e1[1]*e2[2] - e1[2]*e2[1], e1[2]*e2[0] - e1[0]*e2[2], e1[0]*e2[1] - e1[1]*e2[0]])
38
- return np.sqrt(av[0]**2 + av[1]**2 + av[2]**2)/2
39
-
40
29
  def shortest_distance(point_cloud):
41
30
  """
42
31
  Compute the shortest distance between any two points in a 3D point cloud.
@@ -172,7 +161,7 @@ class Mesh3D(Mesh):
172
161
  def n_nodes(self) -> int:
173
162
  '''Return the number of nodes'''
174
163
  return self.nodes.shape[1]
175
-
164
+
176
165
  def get_edge(self, i1: int, i2: int, skip: bool = False) -> int:
177
166
  '''Return the edge index given the two node indices'''
178
167
  if i1==i2:
@@ -204,21 +193,6 @@ class Mesh3D(Mesh):
204
193
  if output is None:
205
194
  raise ValueError(f'There is no tetrahedron with indices {i1}, {i2}, {i3}, {i4}')
206
195
  return output
207
-
208
- def boundary_triangles(self, dimtags: list[tuple[int, int]] | None = None) -> np.ndarray:
209
- if dimtags is None:
210
- outputtags = []
211
- for tags in self.ftag_to_tri.values():
212
- outputtags.extend(tags)
213
- return np.array(outputtags)
214
- else:
215
- dts = []
216
- for dimtag in dimtags:
217
- if dimtag[0]==2:
218
- dts.append(dimtag)
219
- elif dimtag[0]==3:
220
- dts.extend(gmsh.model.get_boundary(dimtags))
221
- return self.get_triangles([tag[1] for tag in dts])
222
196
 
223
197
 
224
198
  def get_tetrahedra(self, vol_tags: Union[int, list[int]]) -> np.ndarray:
@@ -317,7 +291,16 @@ class Mesh3D(Mesh):
317
291
  return np.array(sorted(list(set(edges))))
318
292
 
319
293
 
320
- def update(self, periodic_bcs: list[Periodic] | None = None):
294
+ def _pre_update(self, periodic_bcs: list[Periodic] | None = None):
295
+ """Builds the mesh data properties
296
+
297
+ Args:
298
+ periodic_bcs (list[Periodic] | None, optional): A list of periodic boundary conditions. Defaults to None.
299
+
300
+ Returns:
301
+ None: None
302
+ """
303
+ from .mth.optimized import area
321
304
 
322
305
  logger.trace('Generating mesh data.')
323
306
  if periodic_bcs is None:
@@ -357,7 +340,7 @@ class Mesh3D(Mesh):
357
340
 
358
341
  for bc in periodic_bcs:
359
342
  logger.trace(f'reassigning ordered node numbers for periodic boundary {bc}')
360
- nodemap, ids1, ids2 = self._derive_node_map(bc)
343
+ nodemap, ids1, ids2 = self._pre_derive_node_map(bc)
361
344
  nodemap = {int(a): int(b) for a,b in nodemap.items()}
362
345
  self.nodes[:,ids2] = self.nodes[:,ids1]
363
346
  for itet in range(self.tets.shape[1]):
@@ -508,6 +491,11 @@ class Mesh3D(Mesh):
508
491
 
509
492
  self.defined = True
510
493
 
494
+
495
+ ############################################################
496
+ # GMSH CACHE #
497
+ ############################################################
498
+
511
499
  for dim in (0,1,2,3):
512
500
  dts= gmsh.model.get_entities(dim)
513
501
  for dt in dts:
@@ -519,7 +507,7 @@ class Mesh3D(Mesh):
519
507
 
520
508
  ## Higher order functions
521
509
 
522
- def _derive_node_map(self, bc: Periodic) -> tuple[dict[int, int], np.ndarray, np.ndarray]:
510
+ def _pre_derive_node_map(self, bc: Periodic) -> tuple[dict[int, int], np.ndarray, np.ndarray]:
523
511
  """Computes an old to new node index mapping that preserves global sorting
524
512
 
525
513
  Since basis function field direction is based on the order of indices in tetrahedron
@@ -534,6 +522,8 @@ class Mesh3D(Mesh):
534
522
  tuple[dict[int, int], np.ndarray, np.ndarray]: The node index mapping and the node index arrays
535
523
  """
536
524
 
525
+ from .mth.pairing import pair_coordinates
526
+
537
527
  node_ids_1 = []
538
528
  node_ids_2 = []
539
529
 
@@ -566,7 +556,7 @@ class Mesh3D(Mesh):
566
556
  return conv_map, np.array(node_ids_2_unsorted), np.array(node_ids_2_sorted)
567
557
 
568
558
 
569
- def retreive(self, volumes: list[GeoVolume]) -> list[Material]:
559
+ def _get_material_assignment(self, volumes: list[GeoVolume]) -> list[Material]:
570
560
  '''Retrieve the material properties of the geometry'''
571
561
  #arry = np.zeros((3,3,self.n_tets,), dtype=np.complex128)
572
562
  for vol in volumes:
@@ -818,6 +808,8 @@ class SurfaceMesh(Mesh):
818
808
  def update(self) -> None:
819
809
  ## First Edges
820
810
 
811
+ from .mth.optimized import outward_normal, area
812
+
821
813
  edges = set()
822
814
  for i in range(self.n_tris):
823
815
  i1, i2, i3 = self.tris[:,i]
@@ -15,12 +15,12 @@
15
15
  # along with this program; if not, see
16
16
  # <https://www.gnu.org/licenses/>.
17
17
 
18
- from numba.core import event, types
18
+ from numba.core import event
19
19
  from numba import njit
20
20
 
21
21
  _COMPILE_MESSAGE = """
22
22
  [ EMERGE ]
23
- ⚠ Numba is compiling optimized code; this may take a few minutes.
23
+ ⚠ Numba will be compiling optimized code in this first run; this may take a few minutes.
24
24
  • Additional functions may be compiled on-the-fly.
25
25
  • Compilation happens only once-subsequent runs load from cache.
26
26
  Please wait…"""
@@ -17,8 +17,7 @@
17
17
 
18
18
  from numba import njit, f8, i8, types, c16
19
19
  import numpy as np
20
-
21
-
20
+ from . import _cache_check
22
21
 
23
22
  ############################################################
24
23
  # TRIANGLE GAUSS QUADRATURE POINTS #
@@ -446,4 +445,71 @@ def matmul(M: np.ndarray, vecs: np.ndarray):
446
445
  out[0,:] = M[0,0]*vecs[0,:] + M[0,1]*vecs[1,:] + M[0,2]*vecs[2,:]
447
446
  out[1,:] = M[1,0]*vecs[0,:] + M[1,1]*vecs[1,:] + M[1,2]*vecs[2,:]
448
447
  out[2,:] = M[2,0]*vecs[0,:] + M[2,1]*vecs[1,:] + M[2,2]*vecs[2,:]
449
- return out
448
+ return out
449
+
450
+ @njit(cache=True)
451
+ def _subsample_coordinates(xs: np.ndarray, ys: np.ndarray, tolerance: float, xmin: float) -> tuple[np.ndarray, np.ndarray]:
452
+ """This function takes a set of x and y coordinates in a finely sampled set and returns a reduced
453
+ set of numbers that traces the input curve within a provided tolerance.
454
+
455
+ Args:
456
+ xs (np.ndarray): The set of X-coordinates
457
+ ys (np.ndarray): The set of Y-coordinates
458
+ tolerance (float): The maximum deviation of the curve in meters
459
+ xmin (float): The minimal distance to the next point.
460
+
461
+ Returns:
462
+ np.ndarray: The output X-coordinates
463
+ np.ndarray: The output Y-coordinates
464
+ """
465
+ N = xs.shape[0]
466
+ ids = np.zeros((N,), dtype=np.int32)
467
+ store_index = 1
468
+ start_index = 0
469
+ final_index = 0
470
+ for iteration in range(N):
471
+ i1 = start_index
472
+ done = 0
473
+ for i2 in range(i1+1,N):
474
+ x_true = xs[i1:i2+1]
475
+ y_true = ys[i1:i2+1]
476
+
477
+ x_f = np.linspace(xs[i1],xs[i2], i2-i1+1)
478
+ y_f = np.linspace(ys[i1],ys[i2], i2-i1+1)
479
+ error = np.max(np.sqrt((x_f-x_true)**2 + (y_f-y_true)**2))
480
+ ds = np.sqrt((xs[i2]-xs[i1])**2 + (ys[i2]-ys[i1])**2)
481
+ # If at the end
482
+ if i2==N-1:
483
+ ids[store_index] = i2-1
484
+ final_index = store_index + 1
485
+ done = 1
486
+ break
487
+ # If not yet past the minimum distance, accumulate more
488
+ if ds < xmin:
489
+ continue
490
+ # If the end is less than a minimum distance
491
+ if np.sqrt((ys[-1]-ys[i2])**2 + (xs[-1]-xs[i2])**2) < xmin:
492
+ imid = i1 + (N-1-i1)//2
493
+ ids[store_index] = imid
494
+ ids[store_index+1] = N-1
495
+ final_index = store_index + 2
496
+ done = 1
497
+ break
498
+ if error < tolerance:
499
+ continue
500
+ else:
501
+ ids[store_index] = i2-1
502
+ start_index = i2
503
+ store_index = store_index + 1
504
+ break
505
+ if done==1:
506
+ break
507
+ return xs[ids[0:final_index]], ys[ids[0:final_index]]
508
+
509
+
510
+ @njit(f8(f8[:], f8[:], f8[:]), cache=True, nogil=True)
511
+ def area(x1: np.ndarray, x2: np.ndarray, x3: np.ndarray):
512
+ e1 = x2 - x1
513
+ e2 = x3 - x1
514
+ av = np.array([e1[1]*e2[2] - e1[2]*e2[1], e1[2]*e2[0] - e1[0]*e2[2], e1[0]*e2[1] - e1[1]*e2[0]])
515
+ return np.sqrt(av[0]**2 + av[1]**2 + av[2]**2)/2