emerge 0.4.7__py3-none-any.whl → 0.4.8__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 (78) hide show
  1. emerge/__init__.py +14 -14
  2. emerge/_emerge/__init__.py +42 -0
  3. emerge/_emerge/bc.py +197 -0
  4. emerge/_emerge/coord.py +119 -0
  5. emerge/_emerge/cs.py +523 -0
  6. emerge/_emerge/dataset.py +36 -0
  7. emerge/_emerge/elements/__init__.py +19 -0
  8. emerge/_emerge/elements/femdata.py +212 -0
  9. emerge/_emerge/elements/index_interp.py +64 -0
  10. emerge/_emerge/elements/legrange2.py +172 -0
  11. emerge/_emerge/elements/ned2_interp.py +645 -0
  12. emerge/_emerge/elements/nedelec2.py +140 -0
  13. emerge/_emerge/elements/nedleg2.py +217 -0
  14. emerge/_emerge/geo/__init__.py +24 -0
  15. emerge/_emerge/geo/horn.py +107 -0
  16. emerge/_emerge/geo/modeler.py +449 -0
  17. emerge/_emerge/geo/operations.py +254 -0
  18. emerge/_emerge/geo/pcb.py +1244 -0
  19. emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
  20. emerge/_emerge/geo/pcb_tools/macro.py +79 -0
  21. emerge/_emerge/geo/pmlbox.py +204 -0
  22. emerge/_emerge/geo/polybased.py +529 -0
  23. emerge/_emerge/geo/shapes.py +427 -0
  24. emerge/_emerge/geo/step.py +77 -0
  25. emerge/_emerge/geo2d.py +86 -0
  26. emerge/_emerge/geometry.py +510 -0
  27. emerge/_emerge/howto.py +214 -0
  28. emerge/_emerge/logsettings.py +5 -0
  29. emerge/_emerge/material.py +118 -0
  30. emerge/_emerge/mesh3d.py +730 -0
  31. emerge/_emerge/mesher.py +339 -0
  32. emerge/_emerge/mth/common_functions.py +33 -0
  33. emerge/_emerge/mth/integrals.py +71 -0
  34. emerge/_emerge/mth/optimized.py +357 -0
  35. emerge/_emerge/periodic.py +263 -0
  36. emerge/_emerge/physics/__init__.py +0 -0
  37. emerge/_emerge/physics/microwave/__init__.py +1 -0
  38. emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
  39. emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
  40. emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
  41. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
  42. emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
  43. emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
  44. emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
  45. emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
  46. emerge/_emerge/physics/microwave/periodic.py +82 -0
  47. emerge/_emerge/physics/microwave/port_functions.py +53 -0
  48. emerge/_emerge/physics/microwave/sc.py +175 -0
  49. emerge/_emerge/physics/microwave/simjob.py +147 -0
  50. emerge/_emerge/physics/microwave/sparam.py +138 -0
  51. emerge/_emerge/physics/microwave/touchstone.py +140 -0
  52. emerge/_emerge/plot/__init__.py +0 -0
  53. emerge/_emerge/plot/display.py +394 -0
  54. emerge/_emerge/plot/grapher.py +93 -0
  55. emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
  56. emerge/_emerge/plot/pyvista/__init__.py +1 -0
  57. emerge/_emerge/plot/pyvista/display.py +931 -0
  58. emerge/_emerge/plot/pyvista/display_settings.py +24 -0
  59. emerge/_emerge/plot/simple_plots.py +551 -0
  60. emerge/_emerge/plot.py +225 -0
  61. emerge/_emerge/projects/__init__.py +0 -0
  62. emerge/_emerge/projects/_gen_base.txt +32 -0
  63. emerge/_emerge/projects/_load_base.txt +24 -0
  64. emerge/_emerge/projects/generate_project.py +40 -0
  65. emerge/_emerge/selection.py +596 -0
  66. emerge/_emerge/simmodel.py +444 -0
  67. emerge/_emerge/simulation_data.py +411 -0
  68. emerge/_emerge/solver.py +993 -0
  69. emerge/_emerge/system.py +54 -0
  70. emerge/cli.py +19 -0
  71. emerge/lib.py +1 -1
  72. emerge/plot.py +1 -1
  73. {emerge-0.4.7.dist-info → emerge-0.4.8.dist-info}/METADATA +1 -1
  74. emerge-0.4.8.dist-info/RECORD +78 -0
  75. emerge-0.4.8.dist-info/entry_points.txt +2 -0
  76. emerge-0.4.7.dist-info/RECORD +0 -9
  77. emerge-0.4.7.dist-info/entry_points.txt +0 -2
  78. {emerge-0.4.7.dist-info → emerge-0.4.8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,448 @@
1
+ # EMerge is an open source Python based FEM EM simulation module.
2
+ # Copyright (C) 2025 Robert Fennis.
3
+
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, see
16
+ # <https://www.gnu.org/licenses/>.
17
+
18
+ import numpy as np
19
+ from ....elements import Nedelec2
20
+ from scipy.sparse import csr_matrix, coo_matrix
21
+ from numba_progress import ProgressBar, ProgressBarType
22
+ from ....mth.optimized import local_mapping, matinv, dot_c, cross_c, compute_distances
23
+ from numba import c16, types, f8, i8, njit, prange
24
+
25
+ # --- CACHED FACTORIALS ---------------
26
+
27
+ _FACTORIALS = np.array([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880], dtype=np.int64)
28
+
29
+
30
+ # --- MAPPING FUNCTIONS ---------------
31
+ # These mapping functions return edge and face coordinates in the appropriate order.
32
+
33
+ @njit(i8[:,:](i8[:,:], i8[:,:], i8[:,:], i8, i8), cache=True, nogil=True)
34
+ def local_tet_to_triid(tet_to_field, tets, tris, itet, nedges) -> np.ndarray:
35
+ """Returns the triangle node indices in the right order given a tet-index"""
36
+ tri_ids = tet_to_field[6:10, itet] - nedges
37
+ global_tri_map = tris[:, tri_ids]
38
+ return local_mapping(tets[:, itet], global_tri_map)
39
+
40
+ @njit(i8[:,:](i8[:,:], i8[:,:], i8[:,:], i8), cache=True, nogil=True)
41
+ def local_tet_to_edgeid(tets, edges, tet_to_field, itet) -> np.ndarray:
42
+ """Returns the edge node indices in the right order given a tet-index"""
43
+ global_edge_map = edges[:, tet_to_field[:6,itet]]
44
+ return local_mapping(tets[:, itet], global_edge_map)
45
+
46
+ @njit(i8[:,:](i8[:,:], i8[:,:], i8[:,:], i8), cache=True, nogil=True)
47
+ def local_tri_to_edgeid(tris, edges, tri_to_field, itri: int) -> np.ndarray:
48
+ """Returns the edge node indices in the right order given a triangle-index"""
49
+ global_edge_map = edges[:, tri_to_field[:3,itri]]
50
+ return local_mapping(tris[:, itri], global_edge_map)
51
+
52
+ @njit(c16[:](c16[:,:], c16[:]), cache=True, nogil=True)
53
+ def matmul(Mat, Vec):
54
+ ## Matrix multiplication of a 3D vector
55
+ Vout = np.empty((3,), dtype=np.complex128)
56
+ Vout[0] = Mat[0,0]*Vec[0] + Mat[0,1]*Vec[1] + Mat[0,2]*Vec[2]
57
+ Vout[1] = Mat[1,0]*Vec[0] + Mat[1,1]*Vec[1] + Mat[1,2]*Vec[2]
58
+ Vout[2] = Mat[2,0]*Vec[0] + Mat[2,1]*Vec[1] + Mat[2,2]*Vec[2]
59
+ return Vout
60
+
61
+ @njit(f8(i8, i8, i8, i8), cache=True, fastmath=True, nogil=True)
62
+ def volume_coeff(a: int, b: int, c: int, d: int):
63
+ """ Computes the appropriate matrix coefficients given a list of
64
+ barycentric coordinate functions mentioned.
65
+ Example:
66
+ - L1^2 * L2 - volume_coeff(1,1,2,0) """
67
+ klmn = np.array([0,0,0,0,0,0,0])
68
+ klmn[a] += 1
69
+ klmn[b] += 1
70
+ klmn[c] += 1
71
+ klmn[d] += 1
72
+ output = (_FACTORIALS[klmn[1]]*_FACTORIALS[klmn[2]]*_FACTORIALS[klmn[3]]
73
+ *_FACTORIALS[klmn[4]]*_FACTORIALS[klmn[5]]*_FACTORIALS[klmn[6]])/_FACTORIALS[(np.sum(klmn[1:])+3)]
74
+ return output
75
+
76
+ # --- PRECOMPUTE VOLUME COEFFICINETS --------------------
77
+ NFILL = 5
78
+ VOLUME_COEFF_CACHE_BASE = np.zeros((NFILL,NFILL,NFILL,NFILL), dtype=np.float64)
79
+ for I in range(NFILL):
80
+ for J in range(NFILL):
81
+ for K in range(NFILL):
82
+ for L in range(NFILL):
83
+ VOLUME_COEFF_CACHE_BASE[I,J,K,L] = volume_coeff(I,J,K,L)
84
+
85
+ VOLUME_COEFF_CACHE = VOLUME_COEFF_CACHE_BASE
86
+
87
+ @njit(types.Tuple((f8[:], f8[:], f8[:], f8))(f8[:], f8[:], f8[:]), cache = True, nogil=True)
88
+ def tet_coefficients_bcd(xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray, float]:
89
+ """Computes the a,b,c and d coefficients of a tet barycentric coordinate functions and the volume
90
+
91
+ Args:
92
+ xs (np.ndarray): The tetrahedron X-coordinates
93
+ ys (np.ndarray): The tetrahedron Y-coordinates
94
+ zs (np.ndrray): The tetrahedron Z-coordinates
95
+
96
+ Returns:
97
+ tuple[np.ndarray, np.ndarray, np.ndarray, float]: The a, b, c, d coefficients and volume
98
+ """
99
+ x1, x2, x3, x4 = xs
100
+ y1, y2, y3, y4 = ys
101
+ z1, z2, z3, z4 = zs
102
+
103
+ bbs = np.empty((4,), dtype=np.float64)
104
+ ccs = np.empty((4,), dtype=np.float64)
105
+ dds = np.empty((4,), dtype=np.float64)
106
+
107
+ V = np.abs(-x1*y2*z3/6 + x1*y2*z4/6 + x1*y3*z2/6 - x1*y3*z4/6 - x1*y4*z2/6 + \
108
+ x1*y4*z3/6 + x2*y1*z3/6 - x2*y1*z4/6 - x2*y3*z1/6 + x2*y3*z4/6 + \
109
+ x2*y4*z1/6 - x2*y4*z3/6 - x3*y1*z2/6 + x3*y1*z4/6 + x3*y2*z1/6 - \
110
+ x3*y2*z4/6 - x3*y4*z1/6 + x3*y4*z2/6 + x4*y1*z2/6 - x4*y1*z3/6 - \
111
+ x4*y2*z1/6 + x4*y2*z3/6 + x4*y3*z1/6 - x4*y3*z2/6)
112
+
113
+ bbs[0] = -y2*z3 + y2*z4 + y3*z2 - y3*z4 - y4*z2 + y4*z3
114
+ bbs[1] = y1*z3 - y1*z4 - y3*z1 + y3*z4 + y4*z1 - y4*z3
115
+ bbs[2] = -y1*z2 + y1*z4 + y2*z1 - y2*z4 - y4*z1 + y4*z2
116
+ bbs[3] = y1*z2 - y1*z3 - y2*z1 + y2*z3 + y3*z1 - y3*z2
117
+ ccs[0] = x2*z3 - x2*z4 - x3*z2 + x3*z4 + x4*z2 - x4*z3
118
+ ccs[1] = -x1*z3 + x1*z4 + x3*z1 - x3*z4 - x4*z1 + x4*z3
119
+ ccs[2] = x1*z2 - x1*z4 - x2*z1 + x2*z4 + x4*z1 - x4*z2
120
+ ccs[3] = -x1*z2 + x1*z3 + x2*z1 - x2*z3 - x3*z1 + x3*z2
121
+ dds[0] = -x2*y3 + x2*y4 + x3*y2 - x3*y4 - x4*y2 + x4*y3
122
+ dds[1] = x1*y3 - x1*y4 - x3*y1 + x3*y4 + x4*y1 - x4*y3
123
+ dds[2] = -x1*y2 + x1*y4 + x2*y1 - x2*y4 - x4*y1 + x4*y2
124
+ dds[3] = x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2
125
+
126
+ return bbs, ccs, dds, V
127
+
128
+ def tet_mass_stiffness_matrices(field: Nedelec2,
129
+ er: np.ndarray,
130
+ ur: np.ndarray) -> tuple[csr_matrix, csr_matrix]:
131
+ """Computes the curl-curl Nedelec-2 mass and stiffness matrices
132
+
133
+ Args:
134
+ field (Nedelec2): The Nedelec2 Field object
135
+ er (np.ndarray): a 3x3xN array with permittivity tensors
136
+ ur (np.ndarray): a 3x3xN array with permeability tensors
137
+
138
+ Returns:
139
+ tuple[csr_matrix, csr_matrix]: The stiffness and mass matrix.
140
+ """
141
+ tets = field.mesh.tets
142
+ tris = field.mesh.tris
143
+ edges = field.mesh.edges
144
+ nodes = field.mesh.nodes
145
+
146
+ nT = tets.shape[1]
147
+ tet_to_field = field.tet_to_field
148
+ tet_to_edge = field.mesh.tet_to_edge
149
+ nE = edges.shape[1]
150
+ nTri = tris.shape[1]
151
+
152
+ with ProgressBar(total=nT, ncols=100, dynamic_ncols=False) as pgb:
153
+ dataE, dataB, rows, cols = _matrix_builder(nodes, tets, tris, edges, field.mesh.edge_lengths, tet_to_field, tet_to_edge, ur, er, pgb)
154
+
155
+ E = coo_matrix((dataE, (rows, cols)), shape=(nE*2 + nTri*2, nE*2 + nTri*2)).tocsr()
156
+ B = coo_matrix((dataB, (rows, cols)), shape=(nE*2 + nTri*2, nE*2 + nTri*2)).tocsr()
157
+
158
+ return E, B
159
+
160
+ @njit(types.Tuple((c16[:,:],c16[:,:]))(f8[:,:], f8[:], i8[:,:], i8[:,:], c16[:,:], c16[:,:]), nogil=True, cache=True, parallel=False, fastmath=True)
161
+ def ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_map, Ms, Mm):
162
+ ''' Nedelec 2 tetrahedral stiffness and mass matrix submatrix Calculation
163
+
164
+ '''
165
+
166
+ Dmat = np.empty((20,20), dtype=np.complex128)
167
+ Fmat = np.empty((20,20), dtype=np.complex128)
168
+
169
+ xs, ys, zs = tet_vertices
170
+
171
+ bbs, ccs, dds, V = tet_coefficients_bcd(xs, ys, zs)
172
+ b1, b2, b3, b4 = bbs
173
+ c1, c2, c3, c4 = ccs
174
+ d1, d2, d3, d4 = dds
175
+
176
+ Ds = compute_distances(xs, ys, zs)
177
+
178
+ GL1 = np.array([b1, c1, d1]).astype(np.complex128)
179
+ GL2 = np.array([b2, c2, d2]).astype(np.complex128)
180
+ GL3 = np.array([b3, c3, d3]).astype(np.complex128)
181
+ GL4 = np.array([b4, c4, d4]).astype(np.complex128)
182
+
183
+ GLs = (GL1, GL2, GL3, GL4)
184
+
185
+ letters = [1,2,3,4,5,6]
186
+
187
+ KA = 1/(6*V)**4
188
+ KB = 1/(6*V)**2
189
+
190
+ V6 = 6*V
191
+
192
+ VOLUME_COEFF_CACHE = VOLUME_COEFF_CACHE_BASE*V6
193
+ for ei in range(6):
194
+ ei1, ei2 = local_edge_map[:, ei]
195
+ GA = GLs[ei1]
196
+ GB = GLs[ei2]
197
+ A, B = letters[ei1], letters[ei2]
198
+ L1 = edge_lengths[ei]
199
+
200
+
201
+ for ej in range(6):
202
+ ej1, ej2 = local_edge_map[:, ej]
203
+
204
+ C,D = letters[ej1], letters[ej2]
205
+
206
+ GC = GLs[ej1]
207
+ GD = GLs[ej2]
208
+
209
+ VAD = VOLUME_COEFF_CACHE[A,D,0,0]
210
+ VAC = VOLUME_COEFF_CACHE[A,C,0,0]
211
+ VBC = VOLUME_COEFF_CACHE[B,C,0,0]
212
+ VBD = VOLUME_COEFF_CACHE[B,D,0,0]
213
+ VABCD = VOLUME_COEFF_CACHE[A,B,C,D]
214
+ VABCC = VOLUME_COEFF_CACHE[A,B,C,C]
215
+ VABDD = VOLUME_COEFF_CACHE[A,B,D,D]
216
+ VBBCD = VOLUME_COEFF_CACHE[B,B,C,D]
217
+ VABCD = VOLUME_COEFF_CACHE[A,B,C,D]
218
+ VBBCD = VOLUME_COEFF_CACHE[B,B,C,D]
219
+ VAACD = VOLUME_COEFF_CACHE[A,A,C,D]
220
+ VAADD = VOLUME_COEFF_CACHE[A,A,D,D]
221
+ VBBCC = VOLUME_COEFF_CACHE[B,B,C,C]
222
+ VBBDD = VOLUME_COEFF_CACHE[B,B,D,D]
223
+ VAACC = VOLUME_COEFF_CACHE[A,A,C,C]
224
+
225
+ L2 = edge_lengths[ej]
226
+
227
+ BB1 = matmul(Mm,GC)
228
+ BC1 = matmul(Mm,GD)
229
+ BD1 = dot_c(GA,BB1)
230
+ BE1 = dot_c(GA,BC1)
231
+ BF1 = dot_c(GB,BB1)
232
+ BG1 = dot_c(GB,BC1)
233
+
234
+ Q2 = L1*L2
235
+ Q = Q2*9*dot_c(cross_c(GA,GB),matmul(Ms,cross_c(GC,GD)))
236
+ Dmat[ei+0,ej+0] = Q*VAC
237
+ Dmat[ei+0,ej+10] = Q*VAD
238
+ Dmat[ei+10,ej+0] = Q*VBC
239
+ Dmat[ei+10,ej+10] = Q*VBD
240
+
241
+
242
+ Fmat[ei+0,ej+0] = Q2*(VABCD*BD1-VABCC*BE1-VAACD*BF1+VAACC*BG1)
243
+ Fmat[ei+0,ej+10] = Q2*(VABDD*BD1-VABCD*BE1-VAADD*BF1+VAACD*BG1)
244
+ Fmat[ei+10,ej+0] = Q2*(VBBCD*BD1-VBBCC*BE1-VABCD*BF1+VABCC*BG1)
245
+ Fmat[ei+10,ej+10] = Q2*(VBBDD*BD1-VBBCD*BE1-VABDD*BF1+VABCD*BG1)
246
+
247
+ for ej in range(4):
248
+ ej1, ej2, fj = local_tri_map[:, ej]
249
+
250
+ C,D,F = letters[ej1], letters[ej2], letters[fj]
251
+
252
+ GC = GLs[ej1]
253
+ GD = GLs[ej2]
254
+ GF = GLs[fj]
255
+
256
+ VABCD = VOLUME_COEFF_CACHE[A,B,C,D]
257
+ VBBCD = VOLUME_COEFF_CACHE[B,B,C,D]
258
+ VAD = VOLUME_COEFF_CACHE[A,D,0,0]
259
+ VAC = VOLUME_COEFF_CACHE[A,C,0,0]
260
+ VAF = VOLUME_COEFF_CACHE[A,F,0,0]
261
+ VBF = VOLUME_COEFF_CACHE[B,F,0,0]
262
+ VBC = VOLUME_COEFF_CACHE[B,C,0,0]
263
+ VBD = VOLUME_COEFF_CACHE[B,D,0,0]
264
+ VABDF = VOLUME_COEFF_CACHE[A,B,D,F]
265
+ VABCF = VOLUME_COEFF_CACHE[A,B,F,C]
266
+ VAADF = VOLUME_COEFF_CACHE[A,A,D,F]
267
+ VAACD = VOLUME_COEFF_CACHE[A,A,C,D]
268
+ VBBDF = VOLUME_COEFF_CACHE[B,B,D,F]
269
+ VBBCD = VOLUME_COEFF_CACHE[B,B,C,D]
270
+ VBBCF = VOLUME_COEFF_CACHE[B,B,F,C]
271
+ VAACF = VOLUME_COEFF_CACHE[A,A,C,F]
272
+
273
+ Lab2 = Ds[ej1, ej2]
274
+ Lac2 = Ds[ej1, fj]
275
+
276
+ AB1 = cross_c(GA,GB)
277
+ AI1 = dot_c(AB1,matmul(Ms,cross_c(GC,GF)))
278
+ AJ1 = dot_c(AB1,matmul(Ms,cross_c(GD,GF)))
279
+ AK1 = dot_c(AB1,matmul(Ms,cross_c(GC,GD)))
280
+ BB1 = matmul(Mm,GF)
281
+ BC1 = matmul(Mm,GC)
282
+ BD1 = matmul(Mm,GD)
283
+ BE1 = dot_c(GA,BB1)
284
+ BF1 = dot_c(GA,BC1)
285
+ BG1 = dot_c(GB,BB1)
286
+ BH1 = dot_c(GB,BC1)
287
+ BI1 = dot_c(GA,BD1)
288
+ BJ1 = dot_c(GB,BD1)
289
+
290
+
291
+ Dmat[ei+0,ej+6] = L1*Lac2*(-6*VAD*AI1-3*VAC*AJ1-3*VAF*AK1)
292
+ Dmat[ei+0,ej+16] = L1*Lab2*(6*VAF*AK1+3*VAD*AI1-3*VAC*AJ1)
293
+ Dmat[ei+10,ej+6] = L1*Lac2*(-6*VBD*AI1-3*VBC*AJ1-3*VBF*AK1)
294
+ Dmat[ei+10,ej+16] = L1*Lab2*(6*VBF*AK1+3*VBD*AI1-3*VBC*AJ1)
295
+
296
+ Fmat[ei+0,ej+6] = L1*Lac2*(VABCD*BE1-VABDF*BF1-VAACD*BG1+VAADF*BH1)
297
+ Fmat[ei+0,ej+16] = L1*Lab2*(VABDF*BF1-VABCF*BI1-VAADF*BH1+VAACF*BJ1)
298
+ Fmat[ei+10,ej+6] = L1*Lac2*(VBBCD*BE1-VBBDF*BF1-VABCD*BG1+VABDF*BH1)
299
+ Fmat[ei+10,ej+16] = L1*Lab2*(VBBDF*BF1-VBBCF*BI1-VABDF*BH1+VABCF*BJ1)
300
+
301
+ ## Mirror the transpose part of the previous iteration as its symmetrical
302
+
303
+ Dmat[6:10, :6] = Dmat[:6, 6:10].T
304
+ Fmat[6:10, :6] = Fmat[:6, 6:10].T
305
+ Dmat[16:20, :6] = Dmat[:6, 16:20].T
306
+ Fmat[16:20, :6] = Fmat[:6, 16:20].T
307
+ Dmat[6:10, 10:16] = Dmat[10:16, 6:10].T
308
+ Fmat[6:10, 10:16] = Fmat[10:16, 6:10].T
309
+ Dmat[16:20, 10:16] = Dmat[10:16, 16:20].T
310
+ Fmat[16:20, 10:16] = Fmat[10:16, 16:20].T
311
+
312
+ for ei in range(4):
313
+ ei1, ei2, fi = local_tri_map[:, ei]
314
+ A, B, E = letters[ei1], letters[ei2], letters[fi]
315
+ GA = GLs[ei1]
316
+ GB = GLs[ei2]
317
+ GE = GLs[fi]
318
+ Lac1 = Ds[ei1, fi]
319
+ Lab1 = Ds[ei1, ei2]
320
+ for ej in range(4):
321
+ ej1, ej2, fj = local_tri_map[:, ej]
322
+
323
+ C,D,F = letters[ej1], letters[ej2], letters[fj]
324
+
325
+ GC = GLs[ej1]
326
+ GD = GLs[ej2]
327
+ GF = GLs[fj]
328
+
329
+ VABCD = VOLUME_COEFF_CACHE[A,B,C,D]
330
+ VAD = VOLUME_COEFF_CACHE[A,D,0,0]
331
+ VAC = VOLUME_COEFF_CACHE[A,C,0,0]
332
+ VAF = VOLUME_COEFF_CACHE[A,F,0,0]
333
+ VBF = VOLUME_COEFF_CACHE[B,F,0,0]
334
+ VBC = VOLUME_COEFF_CACHE[B,C,0,0]
335
+ VBD = VOLUME_COEFF_CACHE[B,D,0,0]
336
+ VDE = VOLUME_COEFF_CACHE[E,D,0,0]
337
+ VEF = VOLUME_COEFF_CACHE[E,F,0,0]
338
+ VCE = VOLUME_COEFF_CACHE[E,C,0,0]
339
+ VABDF = VOLUME_COEFF_CACHE[A,B,D,F]
340
+ VACEF = VOLUME_COEFF_CACHE[A,C,E,F]
341
+ VABCF = VOLUME_COEFF_CACHE[A,B,F,C]
342
+ VBCDE = VOLUME_COEFF_CACHE[B,C,D,F]
343
+ VBDEF = VOLUME_COEFF_CACHE[B,E,D,F]
344
+ VACDE = VOLUME_COEFF_CACHE[E,A,C,D]
345
+ VBCEF = VOLUME_COEFF_CACHE[B,E,F,C]
346
+ VADEF = VOLUME_COEFF_CACHE[E,A,D,F]
347
+
348
+
349
+ Lac2 = Ds[ej1, fj]
350
+ Lab2 = Ds[ej1, ej2]
351
+
352
+ AB1 = cross_c(GA,GE)
353
+ AF1 = cross_c(GB,GE)
354
+ AG1 = cross_c(GA,GB)
355
+ AH1 = matmul(Ms,cross_c(GC,GF))
356
+ AI1 = matmul(Ms,cross_c(GD,GF))
357
+ AJ1 = matmul(Ms,cross_c(GC,GD))
358
+ AK1 = dot_c(AB1,AH1)
359
+ AL1 = dot_c(AB1,AI1)
360
+ AM1 = dot_c(AB1,AJ1)
361
+ AN1 = dot_c(AF1,AH1)
362
+ AO1 = dot_c(AF1,AI1)
363
+ AP1 = dot_c(AF1,AJ1)
364
+ AQ1 = dot_c(AG1,AH1)
365
+ AR1 = dot_c(AG1,AI1)
366
+ AS1 = dot_c(AG1,AJ1)
367
+ BB1 = matmul(Mm,GF)
368
+ BC1 = matmul(Mm,GC)
369
+ BD1 = matmul(Mm,GD)
370
+ BE1 = dot_c(GE,BB1)
371
+ BF1 = dot_c(GE,BC1)
372
+ BG1 = dot_c(GA,BB1)
373
+ BH1 = dot_c(GA,BC1)
374
+ BI1 = dot_c(GE,BD1)
375
+ BJ1 = dot_c(GA,BD1)
376
+ BK1 = dot_c(GB,BB1)
377
+ BL1 = dot_c(GB,BC1)
378
+ BM1 = dot_c(GB,BD1)
379
+
380
+ Q1 = 2*VAD*AN1+VAC*AO1+VAF*AP1
381
+ Q2 = -2*VAF*AP1-VAD*AN1+VAC*AO1
382
+ Dmat[ei+6,ej+6] = Lac1*Lac2*(4*VBD*AK1+2*VBC*AL1+2*VBF*AM1+Q1+2*VDE*AQ1+VCE*AR1+VEF*AS1)
383
+ Dmat[ei+6,ej+16] = Lac1*Lab2*(-4*VBF*AM1-2*VBD*AK1+2*VBC*AL1+Q2-2*VEF*AS1-VDE*AQ1+VCE*AR1)
384
+ Dmat[ei+16,ej+6] = Lab1*Lac2*(-4*VDE*AQ1-2*VCE*AR1-2*VEF*AS1-2*VBD*AK1-VBC*AL1-VBF*AM1+Q1)
385
+ Dmat[ei+16,ej+16] = Lab1*Lab2*(4*VEF*AS1+2*VDE*AQ1-2*VCE*AR1+2*VBF*AM1+VBD*AK1-VBC*AL1+Q2)
386
+ Fmat[ei+6,ej+6] = Lac1*Lac2*(VABCD*BE1-VABDF*BF1-VBCDE*BG1+VBDEF*BH1)
387
+ Fmat[ei+6,ej+16] = Lac1*Lab2*(VABDF*BF1-VABCF*BI1-VBDEF*BH1+VBCEF*BJ1)
388
+ Fmat[ei+16,ej+6] = Lab1*Lac2*(VBCDE*BG1-VBDEF*BH1-VACDE*BK1+VADEF*BL1)
389
+ Fmat[ei+16,ej+16] = Lab1*Lab2*(VBDEF*BH1-VBCEF*BJ1-VADEF*BL1+VACEF*BM1)
390
+
391
+ Dmat = Dmat*KA
392
+ Fmat = Fmat*KB
393
+
394
+ return Dmat, Fmat
395
+
396
+ @njit(types.Tuple((c16[:], c16[:], i8[:], i8[:]))(f8[:,:],
397
+ i8[:,:],
398
+ i8[:,:],
399
+ i8[:,:],
400
+ f8[:],
401
+ i8[:,:],
402
+ i8[:,:],
403
+ c16[:,:,:],
404
+ c16[:,:,:],
405
+ ProgressBarType), cache=True, nogil=True, parallel=True)
406
+ def _matrix_builder(nodes, tets, tris, edges, all_edge_lengths, tet_to_field, tet_to_edge, ur, er, pgb: ProgressBar):
407
+ nT = tets.shape[1]
408
+ nedges = edges.shape[1]
409
+
410
+ nnz = nT*400
411
+
412
+ rows = np.empty(nnz, dtype=np.int64)
413
+ cols = np.empty_like(rows)
414
+ dataE = np.empty_like(rows, dtype=np.complex128)
415
+ dataB = np.empty_like(rows, dtype=np.complex128)
416
+
417
+
418
+ for itet in prange(nT):
419
+ p = itet*400
420
+ if np.mod(itet,10)==0:
421
+ pgb.update(10)
422
+ urt = ur[:,:,itet]
423
+ ert = er[:,:,itet]
424
+
425
+ # Construct a local mapping to global triangle orientations
426
+
427
+ local_tri_map = local_tet_to_triid(tet_to_field, tets, tris, itet, nedges)
428
+ local_edge_map = local_tet_to_edgeid(tets, edges, tet_to_field, itet)
429
+ edge_lengths = all_edge_lengths[tet_to_edge[:,itet]]
430
+
431
+ # Construct the local edge map
432
+
433
+ Esub, Bsub = ned2_tet_stiff_mass(nodes[:,tets[:,itet]],
434
+ edge_lengths,
435
+ local_edge_map,
436
+ local_tri_map,
437
+ matinv(urt), ert)
438
+
439
+ indices = tet_to_field[:, itet]
440
+ for ii in range(20):
441
+ rows[p+20*ii:p+20*(ii+1)] = indices[ii]
442
+ cols[p+ii:p+400+ii:20] = indices[ii]
443
+
444
+ dataE[p:p+400] = Esub.ravel()
445
+ dataB[p:p+400] = Bsub.ravel()
446
+ return dataE, dataB, rows, cols
447
+
448
+