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
@@ -16,13 +16,13 @@
16
16
  # <https://www.gnu.org/licenses/>.
17
17
 
18
18
  from __future__ import annotations
19
- import gmsh
19
+ import gmsh # type: ignore
20
20
  import numpy as np
21
- from scipy.spatial import ConvexHull
21
+ from scipy.spatial import ConvexHull # type: ignore
22
22
  from .cs import Axis, CoordinateSystem, _parse_vector, Plane
23
- from typing import Callable, TypeVar
23
+ from typing import Callable, TypeVar, Iterable, Any
24
24
 
25
- def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, np.ndarray]:
25
+ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, Any]:
26
26
  """Tries to find a rectangle as convex-hull of a set of points with a given normal vector.
27
27
 
28
28
  Args:
@@ -66,9 +66,9 @@ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, np
66
66
  xmax, ymax = rot.max(axis=0)
67
67
  area = (xmax-xmin)*(ymax-ymin)
68
68
  if area < best[1]:
69
- best = (theta, area, (xmin,xmax,ymin,ymax), R)
69
+ best = (theta, area, (xmin,xmax,ymin,ymax), R) # type: ignore
70
70
 
71
- theta, _, (xmin,xmax,ymin,ymax), R = best
71
+ theta, _, (xmin,xmax,ymin,ymax), R = best # type: ignore
72
72
 
73
73
  # 6. rectangle axes in 3D
74
74
  u = np.cos(-theta)*e_x + np.sin(-theta)*e_y
@@ -172,9 +172,9 @@ class Selection:
172
172
 
173
173
  """
174
174
  dim: int = -1
175
- def __init__(self, tags: list[int] = None):
175
+ def __init__(self, tags: list[int] | set[int] | tuple[int] | None = None):
176
176
 
177
- self._tags: set[int] = {}
177
+ self._tags: set[int] = set()
178
178
 
179
179
  if tags is not None:
180
180
  if not isinstance(tags, (list,set,tuple)):
@@ -182,7 +182,7 @@ class Selection:
182
182
  self._tags = set(tags)
183
183
 
184
184
  @staticmethod
185
- def from_dim_tags(dim: int, tags: list[int]) -> DomainSelection | PointSelection | FaceSelection | EdgeSelection:
185
+ def from_dim_tags(dim: int, tags: list[int] | set[int]) -> Selection:
186
186
  if dim==0:
187
187
  return PointSelection(tags)
188
188
  elif dim==1:
@@ -198,7 +198,7 @@ class Selection:
198
198
  return list(self._tags)
199
199
 
200
200
  @property
201
- def color_rgb(self) -> tuple[int,int,int]:
201
+ def color_rgb(self) -> tuple[float, float, float]:
202
202
  return (0.5,0.5,1.0)
203
203
 
204
204
  @property
@@ -225,14 +225,14 @@ class Selection:
225
225
  return [gmsh.model.occ.getCenterOfMass(self.dim, tag) for tag in self.tags]
226
226
 
227
227
  @property
228
- def points(self) -> np.ndarray | list[np.ndarray]:
228
+ def points(self) -> list[np.ndarray]:
229
229
  '''A list of 3D coordinates of all nodes comprising the selection.'''
230
230
  points = gmsh.model.get_boundary(self.dimtags, recursive=True)
231
231
  coordinates = [gmsh.model.getValue(*p, []) for p in points]
232
232
  return coordinates
233
233
 
234
234
  @property
235
- def bounding_box(self) -> tuple[np.ndarray, np.ndarray]:
235
+ def bounding_box(self) -> tuple[Iterable, Iterable]:
236
236
  if len(self.tags)==1:
237
237
  return gmsh.model.occ.getBoundingBox(self.dim, self.tags[0])
238
238
  else:
@@ -248,7 +248,7 @@ class Selection:
248
248
  maxz = max(maxz, z1)
249
249
  return (minx, miny, minz), (maxx, maxy, maxz)
250
250
 
251
- def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
251
+ def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
252
252
  """Exclude points by evaluating a function(x,y,z)-> bool
253
253
 
254
254
  This modifies the selection such that the selection does not contain elements
@@ -266,10 +266,10 @@ class Selection:
266
266
  norm = axis.np
267
267
  include2 = [abs(gmsh.model.getNormal(tag, np.array([0,0]))@norm)<0.9 for tag in self.tags]
268
268
  include = [i1 for i1, i2 in zip(include, include2) if i1 and i2]
269
- self._tags = [t for incl, t in zip(include, self._tags) if incl]
269
+ self._tags = set([t for incl, t in zip(include, self._tags) if incl])
270
270
  return self
271
271
 
272
- def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
272
+ def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
273
273
  """Include points by evaluating a function(x,y,z)-> bool
274
274
 
275
275
  This modifies the selection such that the selection does not contain elements
@@ -287,7 +287,7 @@ class Selection:
287
287
  norm = axis.np
288
288
  include2 = [(gmsh.model.getNormal(tag, np.array([0,0]))@norm)>0.99 for tag in self.tags]
289
289
  include1 = [i1 for i1, i2 in zip(include1, include2) if i1 and i2]
290
- self._tags = [t for incl, t in zip(include1, self._tags) if incl]
290
+ self._tags = set([t for incl, t in zip(include1, self._tags) if incl])
291
291
  return self
292
292
 
293
293
  def __operable__(self, other: Selection) -> None:
@@ -295,30 +295,28 @@ class Selection:
295
295
  raise ValueError(f'Selection dimensions must be equal. Trying to operate on dim {self.dim} and {other.dim}')
296
296
  pass
297
297
 
298
- def __add__(self, other: TSelection) -> TSelection:
298
+ def __add__(self, other: Selection) -> Selection:
299
299
  self.__operable__(other)
300
- return Selection.from_dim_tags(self.dim, self._tags + other._tags)
300
+ return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
301
301
 
302
- def __and__(self, other: TSelection) -> TSelection:
302
+ def __and__(self, other: Selection) -> Selection:
303
303
  self.__operable__(other)
304
304
  return Selection.from_dim_tags(self.dim, self._tags.intersection(other._tags))
305
305
 
306
- def __or__(self, other: TSelection) -> TSelection:
306
+ def __or__(self, other: Selection) -> Selection:
307
307
  self.__operable__(other)
308
308
  return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
309
309
 
310
- def __sub__(self, other: TSelection) -> TSelection:
310
+ def __sub__(self, other: Selection) -> Selection:
311
311
  self.__operable__(other)
312
312
  return Selection.from_dim_tags(self.dim, self._tags.difference(other.tags))
313
-
314
-
315
313
 
316
314
  class PointSelection(Selection):
317
315
  """A Class representing a selection of points.
318
316
 
319
317
  """
320
318
  dim: int = 0
321
- def __init__(self, tags: list[int] = None):
319
+ def __init__(self, tags: list[int] | set [int] | None = None):
322
320
  super().__init__(tags)
323
321
 
324
322
  class EdgeSelection(Selection):
@@ -326,7 +324,7 @@ class EdgeSelection(Selection):
326
324
 
327
325
  """
328
326
  dim: int = 1
329
- def __init__(self, tags: list[int] = None):
327
+ def __init__(self, tags: list[int] | set [int] | None = None):
330
328
  super().__init__(tags)
331
329
 
332
330
  class FaceSelection(Selection):
@@ -334,7 +332,7 @@ class FaceSelection(Selection):
334
332
 
335
333
  """
336
334
  dim: int = 2
337
- def __init__(self, tags: list[int] = None):
335
+ def __init__(self, tags: list[int] | set [int] | None = None):
338
336
  super().__init__(tags)
339
337
 
340
338
  @property
@@ -355,14 +353,14 @@ class FaceSelection(Selection):
355
353
 
356
354
  pts3d = self.points
357
355
  normal = self.normal
358
- data = align_rectangle_frame(pts3d, normal)
356
+ data = align_rectangle_frame(np.array(pts3d), normal)
359
357
  plane = data['axes'][:2]
360
358
  origin = data['origin']
361
359
 
362
360
  cs = CoordinateSystem(Axis(plane[0]), Axis(plane[1]), Axis(data['axes'][2]), origin)
363
361
 
364
- size1 = np.linalg.norm(data['corners'][1] - data['corners'][0])
365
- size2 = np.linalg.norm(data['corners'][2] - data['corners'][0])
362
+ size1 = float(np.linalg.norm(data['corners'][1] - data['corners'][0]))
363
+ size2 = float(np.linalg.norm(data['corners'][2] - data['corners'][0]))
366
364
 
367
365
  if size1>size2:
368
366
  cs.swapxy()
@@ -382,7 +380,7 @@ class FaceSelection(Selection):
382
380
  a NxN numpy array of Y coordinates.
383
381
  Z: np.ndarray
384
382
  a NxN numpy array of Z coordinates'''
385
- coordset = []
383
+ coordset: list[tuple[np.ndarray, np.ndarray, np.ndarray]] = []
386
384
  for tag in self.tags:
387
385
  tags, coord, param = gmsh.model.mesh.getNodes(2, tag, includeBoundary=True)
388
386
 
@@ -419,17 +417,15 @@ class FaceSelection(Selection):
419
417
  Y = [c[1] for c in coordset]
420
418
  Z = [c[2] for c in coordset]
421
419
  if len(X) == 1:
422
- X = X[0]
423
- Y = Y[0]
424
- Z = Z[0]
425
- return X, Y, Z
420
+ return X[0], Y[0], Z[0]
421
+ return np.array(X), np.array(Y), np.array(Z)
426
422
 
427
423
  class DomainSelection(Selection):
428
424
  """A Class representing a selection of domains.
429
425
 
430
426
  """
431
427
  dim: int = 3
432
- def __init__(self, tags: list[int] = None):
428
+ def __init__(self, tags: list[int] | set[int] | None = None):
433
429
  super().__init__(tags)
434
430
 
435
431
  SELECT_CLASS: dict[int, type[Selection]] = {
@@ -501,7 +497,7 @@ class Selector:
501
497
  x: float,
502
498
  y: float,
503
499
  z: float,
504
- vector: tuple[float, float, float] | np.ndarray | Axis) -> FaceSelection | EdgeSelection | DomainSelection:
500
+ vector: tuple[float, float, float] | np.ndarray | Axis) -> Selection:
505
501
  '''Returns a list of selections that are in the layer defined by the plane normal vector and the point (x,y,z)
506
502
 
507
503
  The layer is specified by two infinite planes normal to the provided vector. The first plane is originated
@@ -529,7 +525,7 @@ class Selector:
529
525
  output = []
530
526
  for i, c in enumerate(coords):
531
527
  c_local = c - np.array([x,y,z])
532
- if 0 < np.dot(vector, c_local) < L:
528
+ if 0 < np.dot(vector, c_local) < L: # type: ignore
533
529
  output.append(dimtags[i][1])
534
530
  return SELECT_CLASS[self._current_dim](output)
535
531
 
@@ -565,8 +561,8 @@ class Selector:
565
561
  normals = [gmsh.model.get_normal(t, (0,0)) for d,t, in dimtags]
566
562
  tags = []
567
563
  for (d,t), o, n in zip(dimtags, coords, normals):
568
- normdist = np.abs((o-orig)@norm)
569
- dotnorm = np.abs(n@norm)
564
+ normdist = np.abs((o-orig) @ norm)
565
+ dotnorm = np.abs(n @ norm)
570
566
  if normdist < tolerance and dotnorm > 1-tolerance:
571
567
  tags.append(t)
572
568
  return FaceSelection(tags)
@@ -27,12 +27,11 @@ from .plot.pyvista import PVDisplay
27
27
  from .dataset import SimulationDataset
28
28
  from .periodic import PeriodicCell
29
29
  from .bc import BoundaryCondition
30
- from typing import Literal, Type, Generator, Any
30
+ from typing import Literal, Generator, Any
31
31
  from loguru import logger
32
32
  import numpy as np
33
- import sys
34
- import gmsh
35
- import joblib
33
+ import gmsh # type: ignore
34
+ import joblib # type: ignore
36
35
  import os
37
36
  import inspect
38
37
  from pathlib import Path
@@ -94,15 +93,14 @@ class Simulation3D:
94
93
 
95
94
  self.mesh: Mesh3D = Mesh3D(self.mesher)
96
95
  self.select: Selector = Selector()
97
- self.display: PVDisplay = None
98
96
  self.set_loglevel(loglevel)
99
97
 
100
98
  ## STATES
101
99
  self.__active: bool = False
102
100
  self._defined_geometries: bool = False
103
- self._cell: PeriodicCell = None
101
+ self._cell: PeriodicCell | None = None
104
102
 
105
- self.display = PVDisplay(self.mesh)
103
+ self.display: PVDisplay = PVDisplay(self.mesh)
106
104
 
107
105
  if logfile:
108
106
  self.set_logfile()
@@ -203,7 +201,9 @@ class Simulation3D:
203
201
  if self.save_file:
204
202
  self.save()
205
203
  # Finalize GMSH
206
- gmsh.finalize()
204
+ if gmsh.isInitialized():
205
+ gmsh.finalize()
206
+
207
207
  logger.debug('GMSH Shut down successful')
208
208
  # set the state to active
209
209
  self.__active = False
@@ -214,28 +214,22 @@ class Simulation3D:
214
214
 
215
215
  def all_geometries(self) -> list[GeoObject]:
216
216
  """Returns all geometries stored in the simulation file."""
217
- return [obj for obj in self.sim.default.values() if isinstance(obj, GeoObject)]
217
+ return [obj for obj in self.data.sim.default.values() if isinstance(obj, GeoObject)]
218
218
 
219
219
  def all_bcs(self) -> list[BoundaryCondition]:
220
220
  """Returns all boundary condition objects stored in the simulation file"""
221
- return [obj for obj in self.sim.default.values() if isinstance(obj, BoundaryCondition)]
221
+ return [obj for obj in self.data.sim.default.values() if isinstance(obj, BoundaryCondition)]
222
222
 
223
223
  def _set_mesh(self, mesh: Mesh3D) -> None:
224
224
  """Set the current model mesh to a given mesh."""
225
225
  self.mesh = mesh
226
226
  self.mw.mesh = mesh
227
- self.mesher.mesh = mesh
228
227
  self.display._mesh = mesh
229
228
 
230
229
  ############################################################
231
230
  # PUBLIC FUNCTIONS #
232
231
  ############################################################
233
232
 
234
- @property
235
- def passed_geometries(self) -> list[GeoObject]:
236
- """"""
237
- return self.data.sim['geometries']
238
-
239
233
  def save(self) -> None:
240
234
  """Saves the current model in the provided project directory."""
241
235
  # Ensure directory exists
@@ -298,7 +292,7 @@ class Simulation3D:
298
292
  LOG_CONTROLLER.set_write_file(self.modelpath)
299
293
 
300
294
  def view(self,
301
- selections: list[Selection] = None,
295
+ selections: list[Selection] | None = None,
302
296
  use_gmsh: bool = False,
303
297
  volume_opacity: float = 0.1,
304
298
  surface_opacity: float = 1,
@@ -316,22 +310,16 @@ class Simulation3D:
316
310
  gmsh.model.occ.synchronize()
317
311
  gmsh.fltk.run()
318
312
  return
319
- try:
320
- for obj in self.data.sim['geometries']:
321
- if obj.dim==2:
322
- opacity=surface_opacity
323
- elif obj.dim==3:
324
- opacity=volume_opacity
325
- self.display.add_object(obj, show_edges=show_edges, opacity=opacity)
326
- if selections:
327
- [self.display.add_object(sel, color='red', opacity=0.7) for sel in selections]
328
- self.display.show()
329
- return
330
- except NotImplementedError as e:
331
- logger.warning('The provided BaseDisplay class does not support object display. Please make' \
332
- 'sure that this method is properly implemented.')
313
+ for geo in _GEOMANAGER.all_geometries():
314
+ self.display.add_object(geo)
315
+ if selections:
316
+ [self.display.add_object(sel, color='red', opacity=0.7) for sel in selections]
317
+ self.display.show()
318
+
319
+ return None
320
+
333
321
 
334
- def set_periodic_cell(self, cell: PeriodicCell, excluded_faces: list[FaceSelection] = None):
322
+ def set_periodic_cell(self, cell: PeriodicCell, excluded_faces: list[FaceSelection] | None = None):
335
323
  """Set the given periodic cell object as the simulations peridicity.
336
324
 
337
325
  Args:
@@ -341,18 +329,20 @@ class Simulation3D:
341
329
  self.mw.bc._cell = cell
342
330
  self._cell = cell
343
331
 
344
- def commit_geometry(self, *geometries: list[GeoObject]) -> None:
332
+ def commit_geometry(self, *geometries: GeoObject | list[GeoObject]) -> None:
345
333
  """Finalizes and locks the current geometry state of the simulation.
346
334
 
347
335
  The geometries may be provided (legacy behavior) but are automatically managed underwater.
348
336
 
349
337
  """
338
+ geometries_parsed: Any = None
350
339
  if not geometries:
351
- geometries = _GEOMANAGER.all_geometries()
340
+ geometries_parsed = _GEOMANAGER.all_geometries()
352
341
  else:
353
- geometries = unpack_lists(geometries + tuple([item for item in self.data.sim.default.values() if isinstance(item, GeoObject)]))
354
- self.data.sim['geometries'] = geometries
355
- self.mesher.submit_objects(geometries)
342
+ geometries_parsed = unpack_lists(geometries + tuple([item for item in self.data.sim.default.values() if isinstance(item, GeoObject)]))
343
+
344
+ self.data.sim['geometries'] = geometries_parsed
345
+ self.mesher.submit_objects(geometries_parsed)
356
346
  self._defined_geometries = True
357
347
  self.display._facetags = [dt[1] for dt in gmsh.model.get_entities(2)]
358
348
 
@@ -438,9 +428,9 @@ class Simulation3D:
438
428
 
439
429
  logger.info(f'Iterating: {params}')
440
430
  if len(dims_flat)==1:
441
- yield dims_flat[0][i_iter]
431
+ yield (dims_flat[0][i_iter],)
442
432
  else:
443
- yield (dim[i_iter] for dim in dims_flat)
433
+ yield (dim[i_iter] for dim in dims_flat) # type: ignore
444
434
  self.mw.cache_matrices = True
445
435
 
446
436
  ############################################################
@@ -84,7 +84,7 @@ def generate_ndim(
84
84
  outer_data: dict[str, list[float]],
85
85
  inner_data: list[float],
86
86
  outer_labels: tuple[str, ...]
87
- ) -> np.ndarray:
87
+ ) -> tuple[np.ndarray,...]:
88
88
  """
89
89
  Generates an N-dimensional grid of values from flattened data, and returns each axis array plus the grid.
90
90
 
@@ -126,7 +126,7 @@ def generate_ndim(
126
126
  grid[tuple(idxs)] = values
127
127
 
128
128
  # Return each axis array followed by the grid
129
- return (*axes, grid)
129
+ return tuple(axes) + (grid,)
130
130
 
131
131
 
132
132
  class DataEntry:
@@ -142,7 +142,7 @@ class DataEntry:
142
142
 
143
143
  def values(self) -> list[Any]:
144
144
  """ Return all values stored in the DataEntry"""
145
- return self.data.values()
145
+ return list(self.data.values())
146
146
 
147
147
  def keys(self) -> list[str]:
148
148
  """ Return all names of data stored in the DataEntry"""
@@ -150,12 +150,15 @@ class DataEntry:
150
150
 
151
151
  def items(self) -> list[tuple[str, Any]]:
152
152
  """ Returns a list of all key: value pairs of the DataEntry."""
153
+ return list(self.data.items())
153
154
 
154
- def __eq__(self, other: dict[str, float]) -> bool:
155
+ def __eq__(self, other: Any) -> bool:
156
+ if not isinstance(other, dict):
157
+ return False
155
158
  allkeys = set(list(self.vars.keys()) + list(other.keys()))
156
159
  return all(self.vars[key]==other[key] for key in allkeys)
157
160
 
158
- def _dist(self, other: dict[str, float]) -> bool:
161
+ def _dist(self, other: dict[str, float]) -> float:
159
162
  return sum([(abs(self.vars.get(key,1e20)-other[key])/other[key]) for key in other.keys()])
160
163
 
161
164
  def __getitem__(self, key) -> Any:
@@ -218,18 +221,18 @@ class DataContainer:
218
221
 
219
222
 
220
223
  class BaseDataset(Generic[T,M]):
221
- def __init__(self, datatype: T, matrixtype: M, scalar: bool):
224
+ def __init__(self, datatype: type[T], matrixtype: type[M], scalar: bool):
222
225
  self._datatype: type[T] = datatype
223
226
  self._matrixtype: type[M] = matrixtype
224
227
  self._variables: list[dict[str, float]] = []
225
228
  self._data_entries: list[T] = []
226
229
  self._scalar: bool = scalar
227
230
 
228
- self._gritted: bool = None
229
- self._axes: dict[str, np.ndarray] = None
230
- self._ax_ids: dict[str, int] = None
231
- self._ids: np.ndarray = None
232
- self._gridobj: M = None
231
+ self._gritted: bool | None = None
232
+ self._axes: dict[str, np.ndarray]| None = None
233
+ self._ax_ids: dict[str, int]| None = None
234
+ self._ids: np.ndarray| None = None
235
+ self._gridobj: M | None = None
233
236
 
234
237
  self._data: dict[str, Any] = dict()
235
238
 
@@ -238,11 +241,11 @@ class BaseDataset(Generic[T,M]):
238
241
 
239
242
  @property
240
243
  def _fields(self) -> list[str]:
241
- return self._datatype._fields
244
+ return self._datatype._fields # type: ignore
242
245
 
243
246
  @property
244
247
  def _copy(self) -> list[str]:
245
- return self._datatype._copy
248
+ return self._datatype._copy # type: ignore
246
249
 
247
250
  def store(self, key: str, value: Any) -> None:
248
251
  """Stores a variable with some value in the provided key.
@@ -335,7 +338,7 @@ class BaseDataset(Generic[T,M]):
335
338
  self._data_entries.append(new_entry)
336
339
  return new_entry
337
340
 
338
- def _grid_axes(self) -> None:
341
+ def _grid_axes(self) -> bool:
339
342
  """This method attepmts to create a gritted version of the scalar dataset
340
343
 
341
344
  Returns:
@@ -405,7 +408,9 @@ class BaseDataset(Generic[T,M]):
405
408
  """
406
409
  if self._gritted is None:
407
410
  self._grid_axes()
411
+
408
412
  if self._gritted is False:
409
413
  logger.error('The dataset cannot be cast to a structured grid.')
410
414
  raise ValueError('Data not in regular grid')
415
+
411
416
  return self._gridobj
@@ -24,7 +24,7 @@ import site
24
24
  from ctypes.util import find_library
25
25
  from enum import Enum
26
26
  import numpy as np
27
- from scipy import sparse
27
+ from scipy import sparse # type: ignore
28
28
  from pathlib import Path
29
29
  from typing import Iterable, Iterator
30
30
  import pickle
@@ -60,7 +60,8 @@ PARDISO_ERROR_CODES = {
60
60
 
61
61
 
62
62
  #: Environment variable that overrides automatic searching
63
- ENV_VAR = "PYPARDISO_MKL_RT"
63
+
64
+ ENV_VAR = "EMERGE_PARDISO_PATH"
64
65
 
65
66
 
66
67
  def _candidate_dirs() -> Iterable[Path]:
@@ -71,7 +72,7 @@ def _candidate_dirs() -> Iterable[Path]:
71
72
  for p in ( # likely “local” env first
72
73
  Path(sys.prefix),
73
74
  Path(getattr(sys, "base_prefix", sys.prefix)),
74
- Path(site.USER_BASE),
75
+ Path(str(site.USER_BASE)),
75
76
  *(Path(x) for x in os.getenv("LD_LIBRARY_PATH", "").split(":") if x),
76
77
  ):
77
78
  if p not in seen:
@@ -93,7 +94,7 @@ def _search_mkl() -> Iterator[Path]:
93
94
  if regex.match(path.name):
94
95
  yield path
95
96
 
96
- def cache_path_result(tag: str, compute_fn, force: bool = False):
97
+ def cache_path_result(tag: str, compute_fn, force: bool = False) -> str:
97
98
  """
98
99
  Retrieve a cached Path object or compute it and store it.
99
100
 
@@ -113,13 +114,13 @@ def cache_path_result(tag: str, compute_fn, force: bool = False):
113
114
  if not force and cache_file.exists():
114
115
  with open(cache_file, "rb") as f:
115
116
  filename = pickle.load(f)
116
- print(f"Using cached MKL file: {filename}")
117
- return filename
117
+ logger.debug(f"Using cached MKL file: {filename}")
118
+ return str(filename)
118
119
 
119
120
  result = compute_fn()
120
121
  with open(cache_file, "wb") as f:
121
122
  pickle.dump(result, f)
122
- return result
123
+ return str(result)
123
124
 
124
125
  def search_mkl() -> str:
125
126
  """Searches for the file path of the PARDISO MKL executable
@@ -130,11 +131,11 @@ def search_mkl() -> str:
130
131
  logger.debug('Searching for MKL executable...')
131
132
  for candidate in _search_mkl():
132
133
  try:
133
- ctypes.CDLL(candidate)
134
+ ctypes.CDLL(str(candidate))
134
135
  except OSError:
135
136
  continue # try the next one
136
137
  logger.debug(f'Executable found: {candidate}')
137
- return candidate
138
+ return str(candidate)
138
139
 
139
140
  def load_mkl() -> ctypes.CDLL:
140
141
  """Locate and load **mkl_rt**; raise ImportError on failure."""
@@ -200,6 +201,9 @@ CFLOAT32_P = ctypes.POINTER(CFLOAT32)
200
201
  CFLOAT64_P = ctypes.POINTER(CFLOAT64)
201
202
  VOID_SIZE = ctypes.sizeof(ctypes.c_void_p)
202
203
 
204
+ PT_A: type = CINT32
205
+ PT_B: type = np.int32
206
+
203
207
  if VOID_SIZE == 8:
204
208
  PT_A = CINT64
205
209
  PT_B = np.int64
@@ -210,7 +214,7 @@ elif VOID_SIZE == 4:
210
214
  def c_int(value: int):
211
215
  return ctypes.byref(ctypes.c_int32(value))
212
216
 
213
- PARDISO_ARG_TYPES = (ctypes.POINTER(PT_A),CINT32_P,CINT32_P,
217
+ PARDISO_ARG_TYPES: tuple = (ctypes.POINTER(PT_A),CINT32_P,CINT32_P,
214
218
  CINT32_P,CINT32_P,CINT32_P,CNONE_P,CINT32_P,CINT32_P,
215
219
  CINT32_P,CINT32_P,CINT32_P,CINT32_P,CNONE_P,CNONE_P,CINT32_P,
216
220
  )
@@ -314,7 +318,7 @@ class PardisoInterface:
314
318
  self.MATRIX_TYPE = PARDISOMType.REAL_SYM_STRUCT
315
319
  self.complex = False
316
320
 
317
- def _prepare_B(self, b: np.ndarray) -> np.ndarray:
321
+ def _prepare_B(self, b: np.ndarray | sparse.sparray) -> np.ndarray:
318
322
  """Fixes the forcing-vector for the solution process
319
323
 
320
324
  Args:
@@ -324,7 +328,8 @@ class PardisoInterface:
324
328
  np.ndarray: The prepared forcing-vector
325
329
  """
326
330
  if sparse.issparse(b):
327
- b = b.todense()
331
+ b = b.todense() # type: ignore
332
+
328
333
  if np.iscomplexobj(b):
329
334
  b = b.astype(np.complex128)
330
335
  else:
@@ -337,7 +342,7 @@ class PardisoInterface:
337
342
  Returns:
338
343
  int: The error code
339
344
  """
340
- print("SYMBOLIC FACTORIZATION")
345
+
341
346
  self._configure(A)
342
347
  zerovec = np.zeros_like((A.shape[0], 1), dtype=A.dtype)
343
348
  _, error = self._call_solver(A, zerovec, phase=PARDISOPhase.SYMBOLIC_FACTOR)
@@ -349,7 +354,7 @@ class PardisoInterface:
349
354
  Returns:
350
355
  int: The error code
351
356
  """
352
- print("NUMERIC FACTORIZATION")
357
+
353
358
  self._configure(A)
354
359
  zerovec = np.zeros_like((A.shape[0], 1), dtype=A.dtype)
355
360
  _, error = self._call_solver(A, zerovec, phase=PARDISOPhase.NUMERIC_FACTOR)
@@ -372,7 +377,7 @@ class PardisoInterface:
372
377
 
373
378
  def configure_solver(self,
374
379
  perm_algo: int = 3,
375
- nthreads: int = None,
380
+ nthreads: int | None = None,
376
381
  user_perm: int = 0,
377
382
  n_refine_steps: int = 0,
378
383
  pivot_pert: int = 13,
@@ -388,7 +393,7 @@ class PardisoInterface:
388
393
  weighted_matching (int, optional): weighted matching mode. Defaults to 2.
389
394
  """
390
395
  if nthreads is None:
391
- nthreads = int(os.environ.get('OMP_NUM_THREADS'))
396
+ nthreads = int(os.environ.get('OMP_NUM_THREADS', default="4"))
392
397
 
393
398
  self.IPARM[1] = perm_algo
394
399
  self.IPARM[2] = nthreads
@@ -418,14 +423,15 @@ class PardisoInterface:
418
423
  A_indices = A.indices + 1
419
424
 
420
425
  # Define the appropriate data type (complex vs real)
426
+
421
427
  if self.complex:
422
428
  VALUE_P = A.data.ctypes.data_as(CPX16_P)
423
429
  RHS_P = b.ctypes.data_as(CPX16_P)
424
430
  X_P = x.ctypes.data_as(CPX16_P)
425
431
  else:
426
432
  VALUE_P = A.data.ctypes.data_as(CFLOAT64_P)
427
- RHS_P = b.ctypes.data_as(CFLOAT64_P)
428
- X_P = x.ctypes.data_as(CFLOAT64_P)
433
+ RHS_P = b.ctypes.data_as(CFLOAT64_P) # type: ignore
434
+ X_P = x.ctypes.data_as(CFLOAT64_P) # type: ignore
429
435
 
430
436
  # Calls the pardiso function
431
437
  self._pardiso_interface(