emerge 0.5.1__py3-none-any.whl → 0.5.2__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 (47) hide show
  1. emerge/_emerge/bc.py +11 -8
  2. emerge/_emerge/cs.py +2 -2
  3. emerge/_emerge/elements/femdata.py +14 -14
  4. emerge/_emerge/elements/index_interp.py +1 -1
  5. emerge/_emerge/elements/ned2_interp.py +1 -1
  6. emerge/_emerge/elements/nedelec2.py +4 -4
  7. emerge/_emerge/elements/nedleg2.py +9 -9
  8. emerge/_emerge/geo/horn.py +1 -1
  9. emerge/_emerge/geo/modeler.py +18 -19
  10. emerge/_emerge/geo/operations.py +13 -10
  11. emerge/_emerge/geo/pcb.py +70 -69
  12. emerge/_emerge/geo/pcb_tools/macro.py +14 -13
  13. emerge/_emerge/geo/pmlbox.py +1 -1
  14. emerge/_emerge/geometry.py +46 -32
  15. emerge/_emerge/logsettings.py +3 -3
  16. emerge/_emerge/material.py +11 -11
  17. emerge/_emerge/mesh3d.py +81 -59
  18. emerge/_emerge/mesher.py +26 -21
  19. emerge/_emerge/mth/pairing.py +2 -2
  20. emerge/_emerge/periodic.py +34 -31
  21. emerge/_emerge/physics/microwave/adaptive_freq.py +14 -11
  22. emerge/_emerge/physics/microwave/assembly/assembler.py +61 -57
  23. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
  24. emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
  25. emerge/_emerge/physics/microwave/microwave_3d.py +40 -20
  26. emerge/_emerge/physics/microwave/microwave_bc.py +114 -95
  27. emerge/_emerge/physics/microwave/microwave_data.py +33 -33
  28. emerge/_emerge/physics/microwave/simjob.py +12 -12
  29. emerge/_emerge/physics/microwave/sparam.py +12 -12
  30. emerge/_emerge/physics/microwave/touchstone.py +1 -1
  31. emerge/_emerge/plot/display.py +12 -6
  32. emerge/_emerge/plot/pyvista/display.py +44 -39
  33. emerge/_emerge/plot/pyvista/display_settings.py +1 -1
  34. emerge/_emerge/plot/simple_plots.py +15 -15
  35. emerge/_emerge/selection.py +35 -39
  36. emerge/_emerge/simmodel.py +29 -39
  37. emerge/_emerge/simulation_data.py +19 -14
  38. emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
  39. emerge/_emerge/solver.py +52 -52
  40. emerge/lib.py +243 -243
  41. {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/METADATA +1 -1
  42. emerge-0.5.2.dist-info/RECORD +81 -0
  43. emerge/_emerge/plot/grapher.py +0 -93
  44. emerge-0.5.1.dist-info/RECORD +0 -82
  45. {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/WHEEL +0 -0
  46. {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/entry_points.txt +0 -0
  47. {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,5 @@
1
- from .cs import Axis, _parse_axis, GCS
2
- from .selection import FaceSelection, SELECTOR_OBJ
1
+ from .cs import Axis, _parse_axis, GCS, _parse_vector
2
+ from .selection import FaceSelection, SELECTOR_OBJ, Selection
3
3
  from .geo import GeoPrism, XYPolygon, Alignment, XYPlate
4
4
  from .bc import BoundaryCondition
5
5
  from typing import Generator
@@ -21,7 +21,7 @@ def _rotnorm(v: np.ndarray) -> np.ndarray:
21
21
  ax = ax/np.linalg.norm(ax)
22
22
  return ax
23
23
 
24
- def _pair_selection(f1: FaceSelection, f2: FaceSelection, translation: tuple[float, float, float]):
24
+ def _pair_selection(f1: Selection, f2: Selection, translation: tuple[float, float, float]):
25
25
  if len(f1.tags) == 1:
26
26
  return [f1,], [f2,]
27
27
  c1s = [np.array(c) for c in f1.centers]
@@ -32,8 +32,8 @@ def _pair_selection(f1: FaceSelection, f2: FaceSelection, translation: tuple[flo
32
32
  for t1, c1 in zip(f1.tags, c1s):
33
33
  for t2, c2 in zip(f2.tags, c2s):
34
34
  if np.linalg.norm((c1 + ds)-c2) < 1e-6:
35
- f1s.append(FaceSelection([t1,]))
36
- f2s.append(FaceSelection([t2,]))
35
+ f1s.append(Selection([t1,]))
36
+ f2s.append(Selection([t2,]))
37
37
  return f1s, f2s
38
38
 
39
39
 
@@ -42,15 +42,16 @@ def _pair_selection(f1: FaceSelection, f2: FaceSelection, translation: tuple[flo
42
42
  # BASE PERIODIC CELL CLASS #
43
43
  ############################################################
44
44
 
45
-
45
+ # TODO: This must be moved to mw physics if possible
46
46
  class PeriodicCell:
47
47
 
48
48
  def __init__(self,
49
- origins: list[tuple[float, float, float]],
50
- vectors: list[tuple[float, float, float] | Axis]):
51
- self.origins: list[tuple[float, float, float]] = origins
49
+ origins: list[tuple[float, float, float] | list[float] | np.ndarray | Axis],
50
+ vectors: list[tuple[float, float, float] | list[float] | np.ndarray | Axis]):
51
+
52
+ self.origins: list[tuple[float, float, float]] = [_parse_vector(origin) for origin in origins] # type: ignore
52
53
  self.vectors: list[Axis] = [_parse_axis(vec) for vec in vectors]
53
- self.excluded_faces: FaceSelection = None
54
+ self.excluded_faces: Selection | None = None
54
55
  self._bcs: list[Periodic] = []
55
56
  self._ports: list[BoundaryCondition] = []
56
57
 
@@ -71,7 +72,7 @@ class PeriodicCell:
71
72
  """
72
73
  raise NotImplementedError('This method is not implemented for this subclass.')
73
74
 
74
- def cell_data(self) -> Generator[tuple[FaceSelection,FaceSelection,tuple[float, float, float]], None, None]:
75
+ def cell_data(self) -> Generator[tuple[Selection, Selection, np.ndarray], None, None]:
75
76
  """An iterator that yields the two faces of the hex cell plus a cell periodicity vector
76
77
 
77
78
  Yields:
@@ -84,15 +85,17 @@ class PeriodicCell:
84
85
  """
85
86
  bcs = []
86
87
  for f1, f2, a in self.cell_data():
88
+ f1_new = f1
89
+ f2_new = f2
87
90
  if self.excluded_faces is not None:
88
- f1 = f1 - self.excluded_faces
89
- f2 = f2 - self.excluded_faces
90
- bcs.append(Periodic(f1, f2, a))
91
+ f1_new = f1 - self.excluded_faces # type: ignore
92
+ f2_new = f2 - self.excluded_faces # type: ignore
93
+ bcs.append(Periodic(f1_new, f2_new, tuple(a)))
91
94
  self._bcs = bcs
92
95
  return bcs
93
96
 
94
97
  @property
95
- def bcs(self) -> list[Periodic]:
98
+ def bcs(self) -> list[BoundaryCondition]:
96
99
  """Returns a list of Periodic boundary conditions for the given PeriodicCell
97
100
 
98
101
  Args:
@@ -103,7 +106,7 @@ class PeriodicCell:
103
106
  """
104
107
  if not self._bcs:
105
108
  raise ValueError('Periodic Boundary conditions not generated')
106
- return self._bcs + self._ports
109
+ return self._bcs + self._ports # type: ignore
107
110
 
108
111
  def set_scanangle(self, theta: float, phi: float, degree: bool = True) -> None:
109
112
  """Sets the scanangle for the periodic condition. (0,0) is defined along the Z-axis
@@ -126,8 +129,8 @@ class PeriodicCell:
126
129
  bc.uy = uy
127
130
  bc.uz = uz
128
131
  for port in self._ports:
129
- port.scan_theta = theta
130
- port.scan_phi = phi
132
+ port.scan_theta = theta # type: ignore
133
+ port.scan_phi = phi # type: ignore
131
134
 
132
135
  def port_face(self, z: float):
133
136
  raise NotImplementedError('')
@@ -200,9 +203,9 @@ class RectCell(PeriodicCell):
200
203
  class HexCell(PeriodicCell):
201
204
 
202
205
  def __init__(self,
203
- p1: tuple[float, float, float],
204
- p2: tuple[float, float, float],
205
- p3: tuple[float, float, float]):
206
+ point1: tuple[float, float, float],
207
+ point2: tuple[float, float, float],
208
+ point3: tuple[float, float, float]):
206
209
  """Generates a Hexagonal periodic tiling by providing 4 coordinates. The layout of the tiling is as following
207
210
  Assuming a hexagon with a single vertext at the top and bottom and two vertices on the left and right faces ⬢
208
211
 
@@ -211,7 +214,7 @@ class HexCell(PeriodicCell):
211
214
  p2 (tuple[float, float, float]): left face bottom vertex
212
215
  p3 (tuple[float, float, float]): bottom vertex
213
216
  """
214
- p1, p2, p3 = [np.array(p) for p in [p1, p2, p3]]
217
+ p1, p2, p3 = np.array(point1), np.array(point2), np.array(point3)
215
218
  p4 = -p1
216
219
  self.p1: np.ndarray = p1
217
220
  self.p2: np.ndarray = p2
@@ -240,7 +243,7 @@ class HexCell(PeriodicCell):
240
243
  poly = XYPolygon(xs, ys).geo(GCS.displace(0,0,zs[0]))
241
244
  return poly
242
245
 
243
- def cell_data(self) -> Generator[FaceSelection, FaceSelection, np.ndarray]:
246
+ def cell_data(self) -> Generator[tuple[Selection, Selection, np.ndarray], None, None]:
244
247
  nrm = np.linalg.norm
245
248
 
246
249
  o = self.o1[:-1]
@@ -250,7 +253,7 @@ class HexCell(PeriodicCell):
250
253
  f2s = SELECTOR_OBJ.inplane(*self.f12[0], *self.f12[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
251
254
  vec = - (self.p1 + self.p2)
252
255
 
253
- for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
256
+ for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
254
257
  yield f1, f2, vec
255
258
 
256
259
  o = self.o2[:-1]
@@ -259,7 +262,7 @@ class HexCell(PeriodicCell):
259
262
  f1s = SELECTOR_OBJ.inplane(*self.f21[0], *self.f21[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
260
263
  f2s = SELECTOR_OBJ.inplane(*self.f22[0], *self.f22[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
261
264
  vec = - (self.p2 + self.p3)
262
- for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
265
+ for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
263
266
  yield f1, f2, vec
264
267
 
265
268
  o = self.o3[:-1]
@@ -268,18 +271,18 @@ class HexCell(PeriodicCell):
268
271
  f1s = SELECTOR_OBJ.inplane(*self.f31[0], *self.f31[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
269
272
  f2s = SELECTOR_OBJ.inplane(*self.f32[0], *self.f32[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
270
273
  vec = - (self.p3 - self.p1)
271
- for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
274
+ for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
272
275
  yield f1, f2, vec
273
276
 
274
277
  def volume(self,
275
278
  z1: float,
276
279
  z2: float) -> GeoPrism:
277
280
  xs, ys, zs = zip(self.p1, self.p2, self.p3)
278
- xs = np.array(xs)
279
- ys = np.array(ys)
280
- xs = np.concatenate([xs, -xs])
281
- ys = np.concatenate([ys, -ys])
282
- poly = XYPolygon(xs, ys)
281
+ xs2 = np.array(xs) # type: ignore
282
+ ys2 = np.array(ys) # type: ignore
283
+ xs3 = np.concatenate([xs2, -xs2]) # type: ignore
284
+ ys3 = np.concatenate([ys2, -ys2]) # type: ignore
285
+ poly = XYPolygon(xs3, ys3)
283
286
  length = z2-z1
284
287
  return poly.extrude(length, cs=GCS.displace(0,0,z1))
285
288
 
@@ -49,14 +49,17 @@ Modification history
49
49
 
50
50
  """
51
51
 
52
+ # ty: ignore
53
+
52
54
  import numpy as np
53
55
  from typing import Literal
54
56
  from loguru import logger
57
+
55
58
  def cc(z):
56
59
  return z.conjugate()
57
60
 
58
61
  def model(s, poles, residues, d, h):
59
- return np.sum(r/(s-p) for p, r in zip(poles, residues)) + d + s*h
62
+ return sum([r/(s-p) for p, r in zip(poles, residues)]) + d + s*h
60
63
 
61
64
  def vectfit_step(f: np.ndarray, s: np.ndarray, poles: np.ndarray) -> np.ndarray:
62
65
  """
@@ -166,12 +169,12 @@ def calculate_residues(f: np.ndarray, s: np.ndarray, poles: np.ndarray, rcond=-1
166
169
  b = np.concatenate((b.real, b.imag))
167
170
  cA = np.linalg.cond(A)
168
171
  if cA > 1e13:
169
- print('Warning!: Ill Conditioned Matrix. Consider scaling the problem down')
170
- print('Cond(A)', cA)
172
+ logger.warning('Warning!: Ill Conditioned Matrix. Consider scaling the problem down')
173
+ logger.warning('Cond(A)', cA)
171
174
  x, residuals, rnk, s = np.linalg.lstsq(A, b, rcond=rcond)
172
175
 
173
176
  # Recover complex values
174
- x = np.complex128(x)
177
+ x = x.astype(np.complex128)
175
178
  for i, ci in enumerate(cindex):
176
179
  if ci == 1:
177
180
  r1, r2 = x[i:i+2]
@@ -190,7 +193,7 @@ def vectfit_auto(f: np.ndarray,
190
193
  inc_real: bool = False,
191
194
  loss_ratio: float = 1e-2,
192
195
  rcond: int = -1,
193
- track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float]:
196
+ track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float, np.ndarray]:
194
197
  w = s.imag
195
198
  pole_locs = np.linspace(w[0], w[-1], n_poles+2)[1:-1]
196
199
  lr = loss_ratio
@@ -208,7 +211,7 @@ def vectfit_auto(f: np.ndarray,
208
211
 
209
212
  if track_poles:
210
213
  return poles, residues, d, h, np.array(poles_list)
211
- return poles, residues, d, h
214
+ return poles, residues, d, h, np.array([])
212
215
 
213
216
  def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
214
217
  n_poles: int = 10,
@@ -216,10 +219,10 @@ def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
216
219
  inc_real: bool = False,
217
220
  loss_ratio: float = 1e-2,
218
221
  rcond: int = -1,
219
- track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float]:
222
+ track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float, np.ndarray]:
220
223
  s_scale = abs(s[-1])
221
224
  f_scale = abs(f[-1])
222
- poles_s, residues_s, d_s, h_s = vectfit_auto(f / f_scale,
225
+ poles_s, residues_s, d_s, h_s, tracked_poles = vectfit_auto(f / f_scale,
223
226
  s / s_scale,
224
227
  n_poles=n_poles,
225
228
  n_iter = n_iter,
@@ -231,7 +234,7 @@ def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
231
234
  residues = residues_s * f_scale * s_scale
232
235
  d = d_s * f_scale
233
236
  h = h_s * f_scale / s_scale
234
- return poles, residues, d, h
237
+ return poles, residues, d, h, tracked_poles
235
238
 
236
239
 
237
240
  class SparamModel:
@@ -251,7 +254,7 @@ class SparamModel:
251
254
  fdense = np.linspace(min(self.f), max(self.f), max(201, 10*self.f.shape[0]))
252
255
  success = False
253
256
  for nps in range(1,maxpoles):
254
- poles, residues, d, h = vectfit_auto_rescale(Sparam, s, n_poles=nps, inc_real=inc_real)
257
+ poles, residues, d, h, _ = vectfit_auto_rescale(Sparam, s, n_poles=nps, inc_real=inc_real)
255
258
  self.poles: np.ndarray = poles
256
259
  self.residues: np.ndarray = residues
257
260
  self.d = d
@@ -268,7 +271,7 @@ class SparamModel:
268
271
  logger.warning('Could not model S-parameters. Try a denser grid')
269
272
 
270
273
  else:
271
- poles, residues, d, h = vectfit_auto_rescale(Sparam, s, n_poles=n_poles, inc_real=inc_real)
274
+ poles, residues, d, h, _ = vectfit_auto_rescale(Sparam, s, n_poles=n_poles, inc_real=inc_real)
272
275
  self.poles: np.ndarray = poles
273
276
  self.residues: np.ndarray = residues
274
277
  self.d = d
@@ -124,7 +124,7 @@ class Assembler:
124
124
  sig: np.ndarray,
125
125
  k0: float,
126
126
  port: PortBC,
127
- bcs: list[BoundaryCondition]) -> tuple[np.ndarray, np.ndarray, np.ndarray, NedelecLegrange2]:
127
+ bcs: list[BoundaryCondition]) -> tuple[csr_matrix, csr_matrix, np.ndarray, NedelecLegrange2]:
128
128
  """Computes the boundary mode analysis matrices
129
129
 
130
130
  Args:
@@ -194,12 +194,9 @@ class Assembler:
194
194
  tids = nedlegfield.tri_to_field[:, i2]
195
195
  pec_ids.extend(list(tids))
196
196
 
197
- port._field = nedlegfield
198
- port._pece = pec_edges
199
- port._pecv = pec_vertices
200
197
  # Process all port boundary Conditions
201
- pec_ids = set(pec_ids)
202
- solve_ids = [i for i in range(nedlegfield.n_field) if i not in pec_ids]
198
+ pec_ids_set: set[int] = set(pec_ids)
199
+ solve_ids = [i for i in range(nedlegfield.n_field) if i not in pec_ids_set]
203
200
 
204
201
  return E, B, np.array(solve_ids), nedlegfield
205
202
 
@@ -234,7 +231,7 @@ class Assembler:
234
231
 
235
232
  mesh = field.mesh
236
233
  er = er - 1j*sig/(W0*EPS0)*np.repeat(np.eye(3)[:, :, np.newaxis], er.shape[2], axis=2)
237
- is_frequency_dependent: bool = np.any((sig > 0) & (sig < self.conductivity_limit))
234
+ is_frequency_dependent: bool = np.any((sig > 0) & (sig < self.conductivity_limit)) # type: ignore
238
235
 
239
236
 
240
237
  if cache_matrices and not is_frequency_dependent and self.cached_matrices is not None:
@@ -254,7 +251,7 @@ class Assembler:
254
251
 
255
252
  # ISOLATE BOUNDARY CONDITIONS TO ASSEMBLE
256
253
  pec_bcs: list[PEC] = [bc for bc in bcs if isinstance(bc,PEC)]
257
- robin_bcs: list[RectangularWaveguide] = [bc for bc in bcs if isinstance(bc,RobinBC)]
254
+ robin_bcs: list[RobinBC] = [bc for bc in bcs if isinstance(bc,RobinBC)]
258
255
  port_bcs: list[PortBC] = [bc for bc in bcs if isinstance(bc, PortBC)]
259
256
  periodic_bcs: list[Periodic] = [bc for bc in bcs if isinstance(bc, Periodic)]
260
257
 
@@ -264,7 +261,7 @@ class Assembler:
264
261
 
265
262
  # Process all PEC Boundary Conditions
266
263
  logger.debug(' Implementing PEC Boundary Conditions.')
267
- pec_ids = []
264
+ pec_ids: list[int] = []
268
265
  # Conductivity above al imit, consider it all PEC
269
266
  for itet in range(field.n_tets):
270
267
  if sig[itet] > self.conductivity_limit:
@@ -310,13 +307,13 @@ class Assembler:
310
307
  basis = plane_basis_from_points(mesh.nodes[:,nodes]) + 1e-16
311
308
  ibasis = np.linalg.pinv(basis)
312
309
  if bc._include_force:
313
-
314
- Bempty, b_p = assemble_robin_bc_excited(field, Bempty, tri_ids, Ufunc, gamma, ibasis, bc.cs.origin, gauss_points)
315
310
 
316
- port_vectors[bc.port_number] += b_p
311
+ Bempty, b_p = assemble_robin_bc_excited(field, Bempty, tri_ids, Ufunc, gamma, ibasis, bc.cs.origin, gauss_points) # type: ignore
312
+
313
+ port_vectors[bc.port_number] += b_p # type: ignore
317
314
 
318
315
  else:
319
- Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma)
316
+ Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma) # type: ignore
320
317
  B_p = field.generate_csr(Bempty)
321
318
  K = K + B_p
322
319
 
@@ -326,20 +323,20 @@ class Assembler:
326
323
 
327
324
  # Periodic BCs
328
325
  Pmats = []
329
- remove = set()
326
+ remove: set[int] = set()
330
327
  has_periodic = False
331
328
 
332
- for bc in periodic_bcs:
329
+ for pbc in periodic_bcs:
333
330
  has_periodic = True
334
- tri_ids_1 = mesh.get_triangles(bc.face1.tags)
335
- edge_ids_1 = mesh.get_edges(bc.face1.tags)
336
- tri_ids_2 = mesh.get_triangles(bc.face2.tags)
337
- edge_ids_2 = mesh.get_edges(bc.face2.tags)
338
- dv = np.array(bc.dv)
331
+ tri_ids_1 = mesh.get_triangles(pbc.face1.tags)
332
+ edge_ids_1 = mesh.get_edges(pbc.face1.tags)
333
+ tri_ids_2 = mesh.get_triangles(pbc.face2.tags)
334
+ edge_ids_2 = mesh.get_edges(pbc.face2.tags)
335
+ dv = np.array(pbc.dv)
339
336
  linked_tris = pair_coordinates(mesh.tri_centers, tri_ids_1, tri_ids_2, dv, 1e-9)
340
337
  linked_edges = pair_coordinates(mesh.edge_centers, edge_ids_1, edge_ids_2, dv, 1e-9)
341
- dv = np.array(bc.dv)
342
- phi = bc.phi(K0)
338
+ dv = np.array(pbc.dv)
339
+ phi = pbc.phi(K0)
343
340
 
344
341
  Pmat, rows = gen_periodic_matrix(tri_ids_1,
345
342
  edge_ids_1,
@@ -356,15 +353,15 @@ class Assembler:
356
353
  Pmat = Pmats[0]
357
354
  for P2 in Pmats[1:]:
358
355
  Pmat = Pmat @ P2
359
- remove = np.array(sorted(list(remove)))
356
+ remove_array = np.sort(np.unique(list(remove)))
360
357
  all_indices = np.arange(NF)
361
- keep_indices = np.setdiff1d(all_indices, remove)
358
+ keep_indices = np.setdiff1d(all_indices, remove_array)
362
359
  Pmat = Pmat[:,keep_indices]
363
360
  else:
364
361
  Pmat = None
365
362
 
366
- pec_ids = set(pec_ids)
367
- solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids])
363
+ pec_ids_set = set(pec_ids)
364
+ solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids_set])
368
365
 
369
366
  if has_periodic:
370
367
  mask = np.zeros((NF,))
@@ -426,11 +423,11 @@ class Assembler:
426
423
  NF = E.shape[0]
427
424
 
428
425
  pecs: list[PEC] = [bc for bc in bcs if isinstance(bc,PEC)]
429
- robin_bcs: list[RectangularWaveguide] = [bc for bc in bcs if isinstance(bc,RobinBC)]
426
+ robin_bcs: list[RectangularWaveguide] = [bc for bc in bcs if isinstance(bc,RobinBC)] # type: ignore
430
427
  periodic: list[Periodic] = [bc for bc in bcs if isinstance(bc, Periodic)]
431
428
 
432
429
  # Process all PEC Boundary Conditions
433
- pec_ids = []
430
+ pec_ids: list = []
434
431
 
435
432
  logger.debug(' Implementing PEC Boundary Conditions.')
436
433
 
@@ -459,23 +456,30 @@ class Assembler:
459
456
  if len(robin_bcs) > 0:
460
457
  logger.debug(' Implementing Robin Boundary Conditions.')
461
458
 
462
- for bc in robin_bcs:
463
- for tag in bc.tags:
464
- face_tags = [tag,]#bc.tags
465
-
466
- tri_ids = mesh.get_triangles(face_tags)
467
- nodes = mesh.get_nodes(face_tags)
468
- edge_ids = list(mesh.tri_to_edge[:,tri_ids].flatten())
469
-
470
- gamma = bc.get_gamma(k0)
471
-
472
- ibasis = bc.get_inv_basis()
473
- if ibasis is None:
474
- basis = plane_basis_from_points(mesh.nodes[:,nodes]) + 1e-16
475
- ibasis = np.linalg.pinv(basis)
476
- B_p = assemble_robin_bc(field, tri_ids, gamma)
477
- if bc._include_stiff:
478
- B = B + B_p
459
+ if len(robin_bcs) > 0:
460
+ logger.debug(' Implementing Robin Boundary Conditions.')
461
+
462
+ gauss_points = gaus_quad_tri(4)
463
+ Bempty = field.empty_tri_matrix()
464
+ for bc in robin_bcs:
465
+
466
+ for tag in bc.tags:
467
+ face_tags = [tag,]
468
+
469
+ tri_ids = mesh.get_triangles(face_tags)
470
+ nodes = mesh.get_nodes(face_tags)
471
+ edge_ids = list(mesh.tri_to_edge[:,tri_ids].flatten())
472
+
473
+ gamma = bc.get_gamma(k0)
474
+
475
+ ibasis = bc.get_inv_basis()
476
+ if ibasis is None:
477
+ basis = plane_basis_from_points(mesh.nodes[:,nodes]) + 1e-16
478
+ ibasis = np.linalg.pinv(basis)
479
+
480
+ Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma) # type: ignore
481
+ B_p = field.generate_csr(Bempty)
482
+ B = B + B_p
479
483
 
480
484
  if len(periodic) > 0:
481
485
  logger.debug(' Implementing Periodic Boundary Conditions.')
@@ -485,17 +489,17 @@ class Assembler:
485
489
  remove = set()
486
490
  has_periodic = False
487
491
 
488
- for bc in periodic:
492
+ for bcp in periodic:
489
493
  has_periodic = True
490
- tri_ids_1 = mesh.get_triangles(bc.face1.tags)
491
- edge_ids_1 = mesh.get_edges(bc.face1.tags)
492
- tri_ids_2 = mesh.get_triangles(bc.face2.tags)
493
- edge_ids_2 = mesh.get_edges(bc.face2.tags)
494
- dv = np.array(bc.dv)
494
+ tri_ids_1 = mesh.get_triangles(bcp.face1.tags)
495
+ edge_ids_1 = mesh.get_edges(bcp.face1.tags)
496
+ tri_ids_2 = mesh.get_triangles(bcp.face2.tags)
497
+ edge_ids_2 = mesh.get_edges(bcp.face2.tags)
498
+ dv = np.array(bcp.dv)
495
499
  linked_tris = pair_coordinates(mesh.tri_centers, tri_ids_1, tri_ids_2, dv, 1e-9)
496
500
  linked_edges = pair_coordinates(mesh.edge_centers, edge_ids_1, edge_ids_2, dv, 1e-9)
497
- dv = np.array(bc.dv)
498
- phi = bc.phi(k0)
501
+ dv = np.array(bcp.dv)
502
+ phi = bcp.phi(k0)
499
503
 
500
504
  Pmat, rows = gen_periodic_matrix(tri_ids_1,
501
505
  edge_ids_1,
@@ -513,15 +517,15 @@ class Assembler:
513
517
  for P2 in Pmats[1:]:
514
518
  Pmat = Pmat @ P2
515
519
  Pmat = Pmat.tocsr()
516
- remove = np.array(sorted(list(remove)))
520
+ remove_array = np.sort(np.array(list(remove)))
517
521
  all_indices = np.arange(NF)
518
- keep_indices = np.setdiff1d(all_indices, remove)
522
+ keep_indices = np.setdiff1d(all_indices, remove_array)
519
523
  Pmat = Pmat[:,keep_indices]
520
524
  else:
521
525
  Pmat = None
522
526
 
523
- pec_ids = set(pec_ids)
524
- solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids])
527
+ pec_ids_set = set(pec_ids)
528
+ solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids_set])
525
529
 
526
530
  if has_periodic:
527
531
  mask = np.zeros((NF,))
@@ -17,21 +17,33 @@
17
17
 
18
18
  import numpy as np
19
19
  from ....elements.nedleg2 import NedelecLegrange2
20
- from scipy.sparse import coo_matrix
20
+ from scipy.sparse import csr_matrix
21
21
  from numba_progress import ProgressBar, ProgressBarType
22
22
  from ....mth.optimized import local_mapping, matinv, compute_distances
23
23
  from numba import c16, types, f8, i8, njit, prange
24
24
 
25
+
26
+
27
+ ############################################################
28
+ # FIELD MAPPING #
29
+ ############################################################
30
+
25
31
  @njit(i8[:,:](i8, i8[:,:], i8[:,:], i8[:,:]), cache=True, nogil=True)
26
32
  def local_tri_to_edgeid(itri: int, tris, edges, tri_to_edge) -> np.ndarray:
27
33
  global_edge_map = edges[:, tri_to_edge[:,itri]]
28
34
  return local_mapping(tris[:, itri], global_edge_map)
29
35
 
36
+
37
+
38
+ ############################################################
39
+ # PYTHON INTERFACE #
40
+ ############################################################
41
+
30
42
  def generelized_eigenvalue_matrix(field: NedelecLegrange2,
31
43
  er: np.ndarray,
32
44
  ur: np.ndarray,
33
45
  basis: np.ndarray,
34
- k0: float,) -> tuple[coo_matrix, coo_matrix]:
46
+ k0: float,) -> tuple[csr_matrix, csr_matrix]:
35
47
 
36
48
  tris = field.mesh.tris
37
49
  edges = field.mesh.edges
@@ -47,11 +59,16 @@ def generelized_eigenvalue_matrix(field: NedelecLegrange2,
47
59
 
48
60
  nfield = field.n_field
49
61
 
50
- E = coo_matrix((dataE, (rows, cols)), shape=(nfield, nfield)).tocsr()
51
- B = coo_matrix((dataB, (rows, cols)), shape=(nfield, nfield)).tocsr()
62
+ E = csr_matrix((dataE, (rows, cols)), shape=(nfield, nfield))
63
+ B = csr_matrix((dataB, (rows, cols)), shape=(nfield, nfield))
52
64
 
53
65
  return E, B
54
66
 
67
+
68
+ ############################################################
69
+ # MATRIX MULTIPLICATION #
70
+ ############################################################
71
+
55
72
  @njit(c16[:,:](c16[:,:], c16[:,:]), cache=True, nogil=True)
56
73
  def matmul(a, b):
57
74
  out = np.empty((2,b.shape[1]), dtype=np.complex128)
@@ -59,7 +76,10 @@ def matmul(a, b):
59
76
  out[1,:] = a[1,0]*b[0,:] + a[1,1]*b[1,:]
60
77
  return out
61
78
 
62
- ### GAUSS QUADRATURE IMPLEMENTATION
79
+
80
+ ############################################################
81
+ # GAUSS QUADRATURE IMPLEMENTATION #
82
+ ############################################################
63
83
 
64
84
  @njit(c16(c16[:], c16[:], types.Array(types.float64, 1, 'A', readonly=True)), cache=True, nogil=True)
65
85
  def _gqi(v1, v2, W):
@@ -185,7 +205,12 @@ def _nf2_curl(coeff, coords):
185
205
  ys = coords[1,:]
186
206
  return b3*(c1*(a2 + b2*xs + c2*ys) - c2*(a1 + b1*xs + c1*ys)) - c3*(b1*(a2 + b2*xs + c2*ys) - b2*(a1 + b1*xs + c1*ys)) - 2*(b1*c2 - b2*c1)*(a3 + b3*xs + c3*ys) + 0*1j
187
207
 
188
- ####
208
+
209
+ ############################################################
210
+ # TRIANGLE BARYCENTRIC COORDINATE LIN. COEFFICIENTS #
211
+ ############################################################
212
+
213
+
189
214
  @njit(types.Tuple((f8[:], f8[:], f8[:], f8))(f8[:], f8[:]), cache = True, nogil=True)
190
215
  def tri_coefficients(vxs, vys):
191
216
 
@@ -211,13 +236,23 @@ def tri_coefficients(vxs, vys):
211
236
  Cs = np.array([c1, c2, c3])*sign
212
237
  return As, Bs, Cs, A
213
238
 
214
- #DPTS = gaus_quad_tri(4).astype(np.float64)
239
+
240
+ ############################################################
241
+ # CONSTANT DEFINITION #
242
+ ############################################################
243
+
215
244
 
216
245
  DPTS = np.array([[0.22338159, 0.22338159, 0.22338159, 0.10995174, 0.10995174, 0.10995174],
217
246
  [0.10810302, 0.44594849, 0.44594849, 0.81684757, 0.09157621, 0.09157621],
218
247
  [0.44594849, 0.44594849, 0.10810302, 0.09157621, 0.09157621, 0.81684757],
219
248
  [0.44594849, 0.10810302, 0.44594849, 0.09157621, 0.81684757, 0.09157621]], dtype=np.float64)
220
249
 
250
+
251
+ ############################################################
252
+ # NUMBA OPTIMIZED ASSEMBLER #
253
+ ############################################################
254
+
255
+
221
256
  @njit(types.Tuple((c16[:,:], c16[:,:]))(f8[:,:], i8[:,:], c16[:,:], c16[:,:], f8), cache=True, nogil=True)
222
257
  def generalized_matrix_GQ(tri_vertices, local_edge_map, Ms, Mm, k0):
223
258
  '''Nedelec-2 Triangle stiffness and mass submatrix'''
@@ -402,7 +437,7 @@ def _matrix_builder(nodes, tris, edges, tri_to_field, ur, er, k0, pgb: ProgressB
402
437
 
403
438
  tri_to_edge = tri_to_field[:3,:]
404
439
 
405
- for itri in prange(ntritot):
440
+ for itri in prange(ntritot): # type: ignore
406
441
  p = itri*196
407
442
  if np.mod(itri,10)==0:
408
443
  pgb.update(10)
@@ -252,7 +252,7 @@ def ned2_tri_stiff_force(lcs_vertices, gamma, lcs_Uinc, DPTs):
252
252
  def compute_bc_entries_excited(vertices_local, tris, Bmat, Bvec, surf_triangle_indices, gamma, Ulocal_all, DPTs, tri_to_field):
253
253
  N = 64
254
254
  Niter = surf_triangle_indices.shape[0]
255
- for i in prange(Niter):
255
+ for i in prange(Niter): # type: ignore
256
256
  itri = surf_triangle_indices[i]
257
257
 
258
258
  vertex_ids = tris[:, itri]
@@ -388,7 +388,7 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
388
388
  def compute_bc_entries(vertices, tris, Bmat, all_edge_lengths, surf_triangle_indices, gamma):
389
389
  N = 64
390
390
  Niter = surf_triangle_indices.shape[0]
391
- for i in prange(Niter):
391
+ for i in prange(Niter): # type: ignore
392
392
  itri = surf_triangle_indices[i]
393
393
 
394
394
  vertex_ids = tris[:, itri]
@@ -404,7 +404,7 @@ def assemble_robin_bc_excited(field: Nedelec2,
404
404
  Bmat: np.ndarray,
405
405
  surf_triangle_indices: np.ndarray,
406
406
  Ufunc: Callable,
407
- gamma: np.ndarray,
407
+ gamma: complex,
408
408
  local_basis: np.ndarray,
409
409
  origin: np.ndarray,
410
410
  DPTs: np.ndarray):
@@ -424,8 +424,8 @@ def assemble_robin_bc_excited(field: Nedelec2,
424
424
 
425
425
  def assemble_robin_bc(field: Nedelec2,
426
426
  Bmat: np.ndarray,
427
- surf_triangle_indices: np.ndarray,
428
- gamma: np.ndarray):
427
+ surf_triangle_indices: np.ndarray,
428
+ gamma: np.ndarray):
429
429
  vertices = field.mesh.nodes
430
430
  all_edge_lengths = field.mesh.edge_lengths[field.mesh.tri_to_edge]
431
431
  Bmat = compute_bc_entries(vertices, field.mesh.tris, Bmat, all_edge_lengths, surf_triangle_indices, gamma)