emerge 1.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of emerge might be problematic. Click here for more details.
- emerge/__init__.py +2 -1
- emerge/_emerge/const.py +2 -1
- emerge/_emerge/elements/ned2_interp.py +122 -42
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/operations.py +20 -0
- emerge/_emerge/geo/pcb.py +34 -16
- emerge/_emerge/geo/shapes.py +1 -0
- emerge/_emerge/geo/step.py +177 -41
- emerge/_emerge/geometry.py +166 -19
- emerge/_emerge/material.py +2 -0
- emerge/_emerge/mesh3d.py +1 -3
- emerge/_emerge/mesher.py +33 -9
- emerge/_emerge/mth/common_functions.py +1 -1
- emerge/_emerge/mth/optimized.py +2 -2
- emerge/_emerge/physics/microwave/adaptive_mesh.py +451 -100
- emerge/_emerge/physics/microwave/assembly/assembler.py +9 -1
- emerge/_emerge/physics/microwave/microwave_3d.py +2 -2
- emerge/_emerge/physics/microwave/microwave_bc.py +1 -0
- emerge/_emerge/physics/microwave/microwave_data.py +95 -5
- emerge/_emerge/plot/pyvista/display.py +42 -15
- emerge/_emerge/plot/simple_plots.py +116 -4
- emerge/_emerge/selection.py +17 -2
- emerge/_emerge/simmodel.py +111 -49
- emerge/_emerge/solve_interfaces/cudss_interface.py +1 -1
- emerge/_emerge/solver.py +3 -3
- emerge/plot.py +1 -1
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/METADATA +1 -1
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/RECORD +31 -31
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/WHEEL +0 -0
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/entry_points.txt +0 -0
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/licenses/LICENSE +0 -0
emerge/__init__.py
CHANGED
|
@@ -32,7 +32,7 @@ warnings.filterwarnings(
|
|
|
32
32
|
############################################################
|
|
33
33
|
import os
|
|
34
34
|
|
|
35
|
-
__version__ = "1.
|
|
35
|
+
__version__ = "1.2.0"
|
|
36
36
|
|
|
37
37
|
NTHREADS = "1"
|
|
38
38
|
os.environ["EMERGE_STD_LOGLEVEL"] = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
|
|
@@ -74,6 +74,7 @@ from ._emerge.mesher import Algorithm2D, Algorithm3D
|
|
|
74
74
|
from . import lib
|
|
75
75
|
from ._emerge.howto import _HowtoClass
|
|
76
76
|
from ._emerge.emerge_update import update_emerge
|
|
77
|
+
|
|
77
78
|
howto = _HowtoClass()
|
|
78
79
|
|
|
79
80
|
logger.debug('Importing complete!')
|
emerge/_emerge/const.py
CHANGED
|
@@ -14,9 +14,35 @@
|
|
|
14
14
|
# You should have received a copy of the GNU General Public License
|
|
15
15
|
# along with this program; if not, see
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
|
-
from numba import njit, f8, c16, i8, types # type: ignore
|
|
17
|
+
from numba import njit, f8, c16, i8, types, prange # type: ignore
|
|
18
18
|
import numpy as np
|
|
19
|
-
from ..mth.optimized import compute_distances
|
|
19
|
+
from ..mth.optimized import compute_distances, matmul
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@njit(f8[:,:](f8[:,:]), cache=True, nogil=True)
|
|
23
|
+
def matinv(M: np.ndarray) -> np.ndarray:
|
|
24
|
+
"""Optimized matrix inverse of 3x3 matrix
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
M (np.ndarray): Input matrix M of shape (3,3)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
np.ndarray: The matrix inverse inv(M)
|
|
31
|
+
"""
|
|
32
|
+
out = np.zeros((3,3), dtype=np.float64)
|
|
33
|
+
|
|
34
|
+
det = M[0,0]*M[1,1]*M[2,2] - M[0,0]*M[1,2]*M[2,1] - M[0,1]*M[1,0]*M[2,2] + M[0,1]*M[1,2]*M[2,0] + M[0,2]*M[1,0]*M[2,1] - M[0,2]*M[1,1]*M[2,0]
|
|
35
|
+
out[0,0] = M[1,1]*M[2,2] - M[1,2]*M[2,1]
|
|
36
|
+
out[0,1] = -M[0,1]*M[2,2] + M[0,2]*M[2,1]
|
|
37
|
+
out[0,2] = M[0,1]*M[1,2] - M[0,2]*M[1,1]
|
|
38
|
+
out[1,0] = -M[1,0]*M[2,2] + M[1,2]*M[2,0]
|
|
39
|
+
out[1,1] = M[0,0]*M[2,2] - M[0,2]*M[2,0]
|
|
40
|
+
out[1,2] = -M[0,0]*M[1,2] + M[0,2]*M[1,0]
|
|
41
|
+
out[2,0] = M[1,0]*M[2,1] - M[1,1]*M[2,0]
|
|
42
|
+
out[2,1] = -M[0,0]*M[2,1] + M[0,1]*M[2,0]
|
|
43
|
+
out[2,2] = M[0,0]*M[1,1] - M[0,1]*M[1,0]
|
|
44
|
+
out = out/det
|
|
45
|
+
return out
|
|
20
46
|
|
|
21
47
|
@njit(types.Tuple((f8[:], f8[:], f8[:], f8[:], f8))(f8[:], f8[:], f8[:]), cache = True, nogil=True)
|
|
22
48
|
def tet_coefficients(xs, ys, zs):
|
|
@@ -112,7 +138,7 @@ def local_mapping(vertex_ids, triangle_ids):
|
|
|
112
138
|
|
|
113
139
|
return out
|
|
114
140
|
|
|
115
|
-
@njit(types.Tuple((c16[:], c16[:], c16[:]))(f8[:,:], c16[:], i8[:,:], i8[:,:], i8[:,:], f8[:,:], i8[:,:], i8[:]), cache=True, nogil=True)
|
|
141
|
+
@njit(types.Tuple((c16[:], c16[:], c16[:]))(f8[:,:], c16[:], i8[:,:], i8[:,:], i8[:,:], f8[:,:], i8[:,:], i8[:]), cache=True, nogil=True, parallel=False)
|
|
116
142
|
def ned2_tet_interp(coords: np.ndarray,
|
|
117
143
|
solutions: np.ndarray,
|
|
118
144
|
tets: np.ndarray,
|
|
@@ -130,22 +156,20 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
130
156
|
ys = coords[1,:]
|
|
131
157
|
zs = coords[2,:]
|
|
132
158
|
|
|
133
|
-
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
134
|
-
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
135
|
-
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
159
|
+
# Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
160
|
+
# Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
161
|
+
# Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
162
|
+
Ex = np.zeros((nNodes, ), dtype=np.complex128)
|
|
163
|
+
Ey = np.zeros((nNodes, ), dtype=np.complex128)
|
|
164
|
+
Ez = np.zeros((nNodes, ), dtype=np.complex128)
|
|
165
|
+
setnan = np.zeros((nNodes, ), dtype=np.complex128)
|
|
166
|
+
assigned = np.zeros((nNodes,), dtype=np.int64)-1
|
|
136
167
|
|
|
137
168
|
for i_iter in range(tetids.shape[0]):
|
|
138
169
|
itet = tetids[i_iter]
|
|
139
170
|
|
|
140
171
|
iv1, iv2, iv3, iv4 = tets[:, itet]
|
|
141
172
|
|
|
142
|
-
g_node_ids = tets[:, itet]
|
|
143
|
-
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
144
|
-
g_tri_ids = tris[:, tet_to_field[6:10, itet]-nEdges]
|
|
145
|
-
|
|
146
|
-
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
147
|
-
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
148
|
-
|
|
149
173
|
v1 = nodes[:,iv1]
|
|
150
174
|
v2 = nodes[:,iv2]
|
|
151
175
|
v3 = nodes[:,iv3]
|
|
@@ -159,19 +183,30 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
159
183
|
blocal[:,0] = bv1
|
|
160
184
|
blocal[:,1] = bv2
|
|
161
185
|
blocal[:,2] = bv3
|
|
162
|
-
basis =
|
|
186
|
+
basis = matinv(blocal)
|
|
163
187
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
188
|
+
v1x, v1y, v1z = v1[0], v1[1], v1[2]
|
|
189
|
+
|
|
190
|
+
coords_offset = coords*1.0
|
|
191
|
+
coords_offset[0,:] = coords_offset[0,:] - v1x
|
|
192
|
+
coords_offset[1,:] = coords_offset[1,:] - v1y
|
|
193
|
+
coords_offset[2,:] = coords_offset[2,:] - v1z
|
|
194
|
+
|
|
195
|
+
coords_local = matmul(basis, coords_offset)#(basis @ (coords_offset))
|
|
169
196
|
|
|
170
197
|
inside = ((coords_local[0,:] + coords_local[1,:] + coords_local[2,:]) <= 1.00000001) & (coords_local[0,:] >= -1e-6) & (coords_local[1,:] >= -1e-6) & (coords_local[2,:] >= -1e-6)
|
|
171
198
|
|
|
172
199
|
if inside.sum() == 0:
|
|
173
200
|
continue
|
|
174
201
|
|
|
202
|
+
assigned[inside] = itet
|
|
203
|
+
|
|
204
|
+
for i_iter in range(tetids.shape[0]):
|
|
205
|
+
itet = tetids[i_iter]
|
|
206
|
+
|
|
207
|
+
inside = assigned==itet
|
|
208
|
+
if inside.sum() == 0:
|
|
209
|
+
continue
|
|
175
210
|
######### INSIDE THE TETRAHEDRON #########
|
|
176
211
|
|
|
177
212
|
x = xs[inside==1]
|
|
@@ -184,6 +219,16 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
184
219
|
|
|
185
220
|
a_s, b_s, c_s, d_s, V = tet_coefficients(xvs, yvs, zvs)
|
|
186
221
|
|
|
222
|
+
g_node_ids = tets[:, itet]
|
|
223
|
+
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
224
|
+
g_tri_ids = tris[:, tet_to_field[6:10, itet]-nEdges]
|
|
225
|
+
|
|
226
|
+
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
227
|
+
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
228
|
+
|
|
229
|
+
field_ids = tet_to_field[:, itet]
|
|
230
|
+
Etet = solutions[field_ids]
|
|
231
|
+
|
|
187
232
|
Em1s = Etet[0:6]
|
|
188
233
|
Ef1s = Etet[6:10]
|
|
189
234
|
Em2s = Etet[10:16]
|
|
@@ -192,6 +237,7 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
192
237
|
Exl = np.zeros(x.shape, dtype=np.complex128)
|
|
193
238
|
Eyl = np.zeros(x.shape, dtype=np.complex128)
|
|
194
239
|
Ezl = np.zeros(x.shape, dtype=np.complex128)
|
|
240
|
+
|
|
195
241
|
V1 = (216*V**3)
|
|
196
242
|
for ie in range(6):
|
|
197
243
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
@@ -243,12 +289,17 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
243
289
|
Eyl += ey
|
|
244
290
|
Ezl += ez
|
|
245
291
|
|
|
246
|
-
Ex[inside]
|
|
247
|
-
Ey[inside]
|
|
248
|
-
Ez[inside]
|
|
292
|
+
Ex[inside] += Exl
|
|
293
|
+
Ey[inside] += Eyl
|
|
294
|
+
Ez[inside] += Ezl
|
|
295
|
+
setnan[inside] += 1
|
|
296
|
+
|
|
297
|
+
Ex[setnan==0] = np.nan
|
|
298
|
+
Ey[setnan==0] = np.nan
|
|
299
|
+
Ez[setnan==0] = np.nan
|
|
249
300
|
return Ex, Ey, Ez
|
|
250
301
|
|
|
251
|
-
@njit(types.Tuple((c16[:], c16[:], c16[:]))(f8[:,:], c16[:], i8[:,:], i8[:,:], i8[:,:], f8[:,:], i8[:,:], c16[:], i8[:]), cache=True, nogil=True)
|
|
302
|
+
@njit(types.Tuple((c16[:], c16[:], c16[:]))(f8[:,:], c16[:], i8[:,:], i8[:,:], i8[:,:], f8[:,:], i8[:,:], c16[:], i8[:]), cache=True, nogil=True, parallel=False)
|
|
252
303
|
def ned2_tet_interp_curl(coords: np.ndarray,
|
|
253
304
|
solutions: np.ndarray,
|
|
254
305
|
tets: np.ndarray,
|
|
@@ -266,22 +317,21 @@ def ned2_tet_interp_curl(coords: np.ndarray,
|
|
|
266
317
|
xs = coords[0,:]
|
|
267
318
|
ys = coords[1,:]
|
|
268
319
|
zs = coords[2,:]
|
|
269
|
-
|
|
270
|
-
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
271
|
-
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
272
|
-
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
320
|
+
|
|
321
|
+
# Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
322
|
+
# Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
323
|
+
# Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
324
|
+
Ex = np.zeros((nNodes, ), dtype=np.complex128)
|
|
325
|
+
Ey = np.zeros((nNodes, ), dtype=np.complex128)
|
|
326
|
+
Ez = np.zeros((nNodes, ), dtype=np.complex128)
|
|
327
|
+
setnan = np.zeros((nNodes, ), dtype=np.complex128)
|
|
328
|
+
assigned = np.zeros((nNodes,), dtype=np.int64)-1
|
|
273
329
|
|
|
274
330
|
for i_iter in range(tetids.shape[0]):
|
|
275
331
|
itet = tetids[i_iter]
|
|
276
332
|
|
|
277
333
|
iv1, iv2, iv3, iv4 = tets[:, itet]
|
|
278
334
|
|
|
279
|
-
g_node_ids = tets[:, itet]
|
|
280
|
-
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
281
|
-
g_tri_ids = tris[:, tet_to_field[6:10, itet]-nEdges]
|
|
282
|
-
|
|
283
|
-
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
284
|
-
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
285
335
|
|
|
286
336
|
v1 = nodes[:,iv1]
|
|
287
337
|
v2 = nodes[:,iv2]
|
|
@@ -296,19 +346,43 @@ def ned2_tet_interp_curl(coords: np.ndarray,
|
|
|
296
346
|
blocal[:,0] = bv1
|
|
297
347
|
blocal[:,1] = bv2
|
|
298
348
|
blocal[:,2] = bv3
|
|
299
|
-
basis =
|
|
300
|
-
|
|
301
|
-
coords_offset = coords - v1[:,np.newaxis]
|
|
302
|
-
coords_local = (basis @ (coords_offset))
|
|
349
|
+
basis = matinv(blocal)
|
|
303
350
|
|
|
304
|
-
|
|
305
|
-
|
|
351
|
+
v1x, v1y, v1z = v1[0], v1[1], v1[2]
|
|
352
|
+
|
|
353
|
+
coords_offset = coords*1.0
|
|
354
|
+
coords_offset[0,:] = coords_offset[0,:] - v1x
|
|
355
|
+
coords_offset[1,:] = coords_offset[1,:] - v1y
|
|
356
|
+
coords_offset[2,:] = coords_offset[2,:] - v1z
|
|
357
|
+
|
|
358
|
+
#coords_local = (basis @ (coords_offset))
|
|
359
|
+
coords_local = matmul(basis, coords_offset)
|
|
306
360
|
|
|
307
361
|
inside = ((coords_local[0,:] + coords_local[1,:] + coords_local[2,:]) <= 1.00000001) & (coords_local[0,:] >= -1e-6) & (coords_local[1,:] >= -1e-6) & (coords_local[2,:] >= -1e-6)
|
|
308
362
|
|
|
309
363
|
if inside.sum() == 0:
|
|
310
364
|
continue
|
|
311
365
|
|
|
366
|
+
assigned[inside] = itet
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
for i_iter in range(tetids.shape[0]):
|
|
370
|
+
itet = tetids[i_iter]
|
|
371
|
+
|
|
372
|
+
inside = (assigned==itet)
|
|
373
|
+
if inside.sum() == 0:
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
g_node_ids = tets[:, itet]
|
|
377
|
+
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
378
|
+
g_tri_ids = tris[:, tet_to_field[6:10, itet]-nEdges]
|
|
379
|
+
|
|
380
|
+
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
381
|
+
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
382
|
+
|
|
383
|
+
field_ids = tet_to_field[:, itet]
|
|
384
|
+
Etet = solutions[field_ids]
|
|
385
|
+
|
|
312
386
|
const = c[itet]
|
|
313
387
|
######### INSIDE THE TETRAHEDRON #########
|
|
314
388
|
|
|
@@ -420,9 +494,15 @@ def ned2_tet_interp_curl(coords: np.ndarray,
|
|
|
420
494
|
Eyl += ey
|
|
421
495
|
Ezl += ez
|
|
422
496
|
|
|
423
|
-
Ex[inside]
|
|
424
|
-
Ey[inside]
|
|
425
|
-
Ez[inside]
|
|
497
|
+
Ex[inside] += Exl*const
|
|
498
|
+
Ey[inside] += Eyl*const
|
|
499
|
+
Ez[inside] += Ezl*const
|
|
500
|
+
setnan[inside] += 1
|
|
501
|
+
|
|
502
|
+
Ex[setnan==0] = np.nan
|
|
503
|
+
Ey[setnan==0] = np.nan
|
|
504
|
+
Ez[setnan==0] = np.nan
|
|
505
|
+
|
|
426
506
|
return Ex, Ey, Ez
|
|
427
507
|
|
|
428
508
|
@njit(types.Tuple((c16[:], c16[:], c16[:]))(f8[:,:], c16[:], i8[:,:], f8[:,:], i8[:,:]), cache=True, nogil=True)
|
emerge/_emerge/geo/__init__.py
CHANGED
|
@@ -19,6 +19,6 @@ from .pcb import PCB, PCBLayer
|
|
|
19
19
|
from .pmlbox import pmlbox
|
|
20
20
|
from .horn import Horn
|
|
21
21
|
from .shapes import Cylinder, CoaxCylinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment, Cone
|
|
22
|
-
from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite, expand_surface, stretch
|
|
22
|
+
from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite, expand_surface, stretch, extrude
|
|
23
23
|
from .polybased import XYPolygon, GeoPrism, Disc, Curve
|
|
24
24
|
from .step import STEPItems
|
emerge/_emerge/geo/operations.py
CHANGED
|
@@ -282,6 +282,24 @@ def stretch(main: GeoObject, fx: float = 1, fy: float = 1, fz: float = 1, origin
|
|
|
282
282
|
|
|
283
283
|
return main
|
|
284
284
|
|
|
285
|
+
def extrude(main: GeoSurface, dx: float = 0.0, dy: float = 0.0, dz: float = 0.0) -> GeoObject:
|
|
286
|
+
"""Extrudes a surface entity by a displacement
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
main (GeoSurface): _description_
|
|
290
|
+
dx (float): _description_
|
|
291
|
+
dy (float): _description_
|
|
292
|
+
dz (float): _description_
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
GeoObject: _description_
|
|
296
|
+
"""
|
|
297
|
+
dtout = gmsh.model.occ.extrude(main.dimtags, dx, dy, dz)
|
|
298
|
+
out = [dt[1] for dt in dtout if dt[0]==3]
|
|
299
|
+
obj_out = GeoVolume(out, name=f'Extrusion[{main.name}]')
|
|
300
|
+
gmsh.model.occ.synchronize()
|
|
301
|
+
return obj_out
|
|
302
|
+
|
|
285
303
|
|
|
286
304
|
@overload
|
|
287
305
|
def unite(*objects: GeoVolume) -> GeoVolume: ...
|
|
@@ -313,7 +331,9 @@ def unite(*objects: GeoObject) -> GeoObject:
|
|
|
313
331
|
other._exists = False
|
|
314
332
|
new_dimtags, mapping = gmsh.model.occ.fuse(dts, main.dimtags)
|
|
315
333
|
|
|
334
|
+
newname = 'Union[' + ','.join([obj.name for obj in objects]) + ']'
|
|
316
335
|
new_obj = GeoObject.from_dimtags(new_dimtags)._take_tools(*objects)
|
|
336
|
+
new_obj.name = newname
|
|
317
337
|
new_obj.set_material(main.material)
|
|
318
338
|
new_obj.prio_set(main._priority)
|
|
319
339
|
|
emerge/_emerge/geo/pcb.py
CHANGED
|
@@ -25,7 +25,7 @@ from .polybased import XYPolygon
|
|
|
25
25
|
from .operations import change_coordinate_system, unite
|
|
26
26
|
from .pcb_tools.macro import parse_macro
|
|
27
27
|
from .pcb_tools.calculator import PCBCalculator
|
|
28
|
-
|
|
28
|
+
from ..logsettings import DEBUG_COLLECTOR
|
|
29
29
|
import numpy as np
|
|
30
30
|
from loguru import logger
|
|
31
31
|
from typing import Literal, Callable, overload
|
|
@@ -960,7 +960,8 @@ class PCB:
|
|
|
960
960
|
stack: list[PCBLayer] = None,
|
|
961
961
|
name: str | None = None,
|
|
962
962
|
trace_thickness: float | None = None,
|
|
963
|
-
zs: np.ndarray | None = None
|
|
963
|
+
zs: np.ndarray | None = None,
|
|
964
|
+
thick_traces: bool = False,
|
|
964
965
|
):
|
|
965
966
|
"""Creates a new PCB layout class instance
|
|
966
967
|
|
|
@@ -974,9 +975,11 @@ class PCB:
|
|
|
974
975
|
stack (list[PCBLayer], optional): Optional list of PCBLayer classes for multilayer PCB with different dielectrics. Defaults to None.
|
|
975
976
|
name (str | None, optional): The PCB object name. Defaults to None.
|
|
976
977
|
trace_thickness (float | None, optional): The conductor trace thickness if important. Defaults to None.
|
|
978
|
+
thick_traces: (bool, optional): If traces should be given a thickness and modeled in 3D. Defaults to False
|
|
977
979
|
"""
|
|
978
980
|
|
|
979
981
|
self.thickness: float = thickness
|
|
982
|
+
self._thick_traces: bool = thick_traces
|
|
980
983
|
self._stack: list[PCBLayer] = []
|
|
981
984
|
|
|
982
985
|
if zs is not None:
|
|
@@ -1016,9 +1019,10 @@ class PCB:
|
|
|
1016
1019
|
|
|
1017
1020
|
self.dielectric_priority: int = 11
|
|
1018
1021
|
self.via_priority: int = 12
|
|
1022
|
+
self.conductor_priority: int = 13
|
|
1019
1023
|
|
|
1020
|
-
self.traces: list[GeoPolygon] = []
|
|
1021
|
-
self.ports: list[GeoPolygon] = []
|
|
1024
|
+
self.traces: list[GeoPolygon | GeoVolume] = []
|
|
1025
|
+
self.ports: list[GeoPolygon | GeoVolume] = []
|
|
1022
1026
|
self.vias: list[Via] = []
|
|
1023
1027
|
|
|
1024
1028
|
self.xs: list[float] = []
|
|
@@ -1032,7 +1036,10 @@ class PCB:
|
|
|
1032
1036
|
self.calc: PCBCalculator = PCBCalculator(self.thickness, self._zs, self.material, self.unit)
|
|
1033
1037
|
|
|
1034
1038
|
self.name: str = _NAME_MANAGER(name, self._DEFNAME)
|
|
1035
|
-
|
|
1039
|
+
|
|
1040
|
+
if not self._thick_traces and self.trace_material is not PEC:
|
|
1041
|
+
DEBUG_COLLECTOR.add_report('Non PEC surface materials are used without thick traces. The SurfaceImpedance boundary condition will be used that is known to not be accurate.' +
|
|
1042
|
+
'Please set thick_traces=True in the PCB constructor to ensure accurate losses until this issue is fixed.')
|
|
1036
1043
|
|
|
1037
1044
|
############################################################
|
|
1038
1045
|
# PROPERTIES #
|
|
@@ -1099,7 +1106,7 @@ class PCB:
|
|
|
1099
1106
|
self.paths.append(StripPath(self))
|
|
1100
1107
|
return self.paths[path_nr]
|
|
1101
1108
|
|
|
1102
|
-
def _gen_poly(self, xys: list[tuple[float, float]], z: float, name: str | None = None) -> GeoPolygon:
|
|
1109
|
+
def _gen_poly(self, xys: list[tuple[float, float]], z: float, name: str | None = None) -> GeoPolygon | GeoVolume:
|
|
1103
1110
|
""" Generates a GeoPoly out of a list of (x,y) coordinate tuples.
|
|
1104
1111
|
|
|
1105
1112
|
|
|
@@ -1120,8 +1127,16 @@ class PCB:
|
|
|
1120
1127
|
|
|
1121
1128
|
tag_wire = gmsh.model.occ.addWire(ltags)
|
|
1122
1129
|
planetag = gmsh.model.occ.addPlaneSurface([tag_wire,])
|
|
1123
|
-
|
|
1124
|
-
|
|
1130
|
+
if self._thick_traces:
|
|
1131
|
+
if self.trace_thickness is None:
|
|
1132
|
+
raise ValueError('Trace thickness not defined, cannot generate polygons. Make sure to define a trace thickness in the PCB() constructor.')
|
|
1133
|
+
dx, dy, dz = self.cs.zax.np*self.trace_thickness
|
|
1134
|
+
dimtags = gmsh.model.occ.extrude([(2,planetag),], dx, dy, dz)
|
|
1135
|
+
voltags = [dt[1] for dt in dimtags if dt[0]==3]
|
|
1136
|
+
poly = GeoVolume(voltags, name=name).prio_set(self.conductor_priority)
|
|
1137
|
+
else:
|
|
1138
|
+
poly = GeoPolygon([planetag,], name=name)
|
|
1139
|
+
poly._store('thickness', self.trace_thickness)
|
|
1125
1140
|
return poly
|
|
1126
1141
|
|
|
1127
1142
|
|
|
@@ -1236,7 +1251,7 @@ class PCB:
|
|
|
1236
1251
|
height: float | None = None,
|
|
1237
1252
|
origin: tuple[float, float] | None = None,
|
|
1238
1253
|
alignment: Alignment = Alignment.CORNER,
|
|
1239
|
-
name: str | None = None) -> GeoSurface:
|
|
1254
|
+
name: str | None = None) -> GeoSurface | GeoVolume:
|
|
1240
1255
|
"""Generates a generic rectangular plate in the XY grid.
|
|
1241
1256
|
If no size is provided, it defaults to the entire PCB size assuming that the bounds are determined.
|
|
1242
1257
|
|
|
@@ -1264,10 +1279,13 @@ class PCB:
|
|
|
1264
1279
|
origin[1] - height*self.unit/2,
|
|
1265
1280
|
origin[2])
|
|
1266
1281
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1282
|
+
if self._thick_traces:
|
|
1283
|
+
plane = Box(width*self.unit, height*self.unit, self.trace_thickness, position=origin, name=name).set_material(self.trace_material)
|
|
1284
|
+
else:
|
|
1285
|
+
plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0), name=name) # type: ignore
|
|
1286
|
+
plane._store('thickness', self.thickness)
|
|
1287
|
+
plane = change_coordinate_system(plane, self.cs) # type: ignore
|
|
1288
|
+
plane.set_material(self.trace_material)
|
|
1271
1289
|
return plane # type: ignore
|
|
1272
1290
|
|
|
1273
1291
|
def radial_stub(self, pos: tuple[float, float], length: float, angle: float, direction: tuple[float, float], Nsections: int = 8, w0: float = 0, z: float = 0, material: Material = None, name: str = None) -> None:
|
|
@@ -1525,12 +1543,12 @@ class PCB:
|
|
|
1525
1543
|
self.polies.append(poly)
|
|
1526
1544
|
|
|
1527
1545
|
@overload
|
|
1528
|
-
def compile_paths(self, merge: Literal[True]) -> GeoSurface: ...
|
|
1546
|
+
def compile_paths(self, merge: Literal[True]) -> GeoSurface | GeoVolume: ...
|
|
1529
1547
|
|
|
1530
1548
|
@overload
|
|
1531
|
-
def compile_paths(self, merge: Literal[False] = ...) -> list[GeoSurface]: ...
|
|
1549
|
+
def compile_paths(self, merge: Literal[False] = ...) -> list[GeoSurface] | list[GeoVolume]: ...
|
|
1532
1550
|
|
|
1533
|
-
def compile_paths(self, merge: bool = False) -> list[GeoPolygon] | GeoSurface:
|
|
1551
|
+
def compile_paths(self, merge: bool = False) -> list[GeoPolygon] | GeoSurface | GeoVolume:
|
|
1534
1552
|
"""Compiles the striplines and returns a list of polygons or asingle one.
|
|
1535
1553
|
|
|
1536
1554
|
The Z=0 argument determines the height of the striplines. Z=0 corresponds to the top of
|
emerge/_emerge/geo/shapes.py
CHANGED
|
@@ -314,6 +314,7 @@ class Cylinder(GeoVolume):
|
|
|
314
314
|
height*ax[0], height*ax[1], height*ax[2],
|
|
315
315
|
radius)
|
|
316
316
|
super().__init__(cyl, name=name)
|
|
317
|
+
|
|
317
318
|
self._add_face_pointer('front', cs.origin, -cs.zax.np)
|
|
318
319
|
self._add_face_pointer('back', cs.origin+height*cs.zax.np, cs.zax.np)
|
|
319
320
|
self._add_face_pointer('bottom', cs.origin, -cs.zax.np)
|