emerge 1.0.5__py3-none-any.whl → 1.0.6__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 -2
- emerge/_emerge/geo/horn.py +17 -1
- emerge/_emerge/geo/pcb.py +10 -6
- emerge/_emerge/geo/polybased.py +16 -1
- emerge/_emerge/geo/shapes.py +93 -1
- emerge/_emerge/geometry.py +22 -4
- emerge/_emerge/material.py +31 -2
- emerge/_emerge/mesher.py +2 -1
- emerge/_emerge/physics/microwave/assembly/assembler.py +1 -1
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +78 -77
- emerge/_emerge/physics/microwave/microwave_3d.py +12 -9
- emerge/_emerge/physics/microwave/microwave_bc.py +2 -2
- emerge/_emerge/physics/microwave/microwave_data.py +9 -0
- emerge/_emerge/plot/pyvista/display.py +88 -0
- emerge/_emerge/simmodel.py +1 -1
- emerge/_emerge/solver.py +92 -72
- {emerge-1.0.5.dist-info → emerge-1.0.6.dist-info}/METADATA +1 -1
- {emerge-1.0.5.dist-info → emerge-1.0.6.dist-info}/RECORD +21 -21
- {emerge-1.0.5.dist-info → emerge-1.0.6.dist-info}/WHEEL +0 -0
- {emerge-1.0.5.dist-info → emerge-1.0.6.dist-info}/entry_points.txt +0 -0
- {emerge-1.0.5.dist-info → emerge-1.0.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -61,6 +61,9 @@ def run_job_multi(job: SimJob) -> SimJob:
|
|
|
61
61
|
job.submit_solution(solution, report)
|
|
62
62
|
return job
|
|
63
63
|
|
|
64
|
+
def _init_worker():
|
|
65
|
+
nr = int(mp.current_process().name.split('-')[1])
|
|
66
|
+
DEFAULT_ROUTINE._configure_routine(proc_nr=nr)
|
|
64
67
|
|
|
65
68
|
def _dimstring(data: list[float] | np.ndarray) -> str:
|
|
66
69
|
"""A String formatter for dimensions in millimeters
|
|
@@ -190,7 +193,6 @@ class Microwave3D:
|
|
|
190
193
|
logger.debug(f'Assinging PEC to {surf}')
|
|
191
194
|
self.bc.PEC(surf)
|
|
192
195
|
elif surf.material.cond.scalar(1e9) > self._settings.mw_2dbc_lim:
|
|
193
|
-
logger.debug(f'Assigning SurfaceImpedance to {surf}')
|
|
194
196
|
self.bc.SurfaceImpedance(surf, surf.material)
|
|
195
197
|
|
|
196
198
|
|
|
@@ -619,7 +621,7 @@ class Microwave3D:
|
|
|
619
621
|
|
|
620
622
|
def run_sweep(self,
|
|
621
623
|
parallel: bool = False,
|
|
622
|
-
|
|
624
|
+
n_workers: int = 2,
|
|
623
625
|
harddisc_threshold: int | None = None,
|
|
624
626
|
harddisc_path: str = 'EMergeSparse',
|
|
625
627
|
frequency_groups: int = -1,
|
|
@@ -627,7 +629,7 @@ class Microwave3D:
|
|
|
627
629
|
automatic_modal_analysis: bool = True) -> MWData:
|
|
628
630
|
"""Executes a frequency domain study
|
|
629
631
|
|
|
630
|
-
The study is distributed over "
|
|
632
|
+
The study is distributed over "n_workers" workers.
|
|
631
633
|
As optional parameter you may set a harddisc_threshold as integer. This determines the maximum
|
|
632
634
|
number of degrees of freedom before which the jobs will be cahced to the harddisk. The
|
|
633
635
|
path that will be used to cache the sparse matrices can be specified.
|
|
@@ -637,7 +639,7 @@ class Microwave3D:
|
|
|
637
639
|
frequency indices will be precomputed and then solved: [[1,2,3,4],[5,6,7,8],[9,10,11]]
|
|
638
640
|
|
|
639
641
|
Args:
|
|
640
|
-
|
|
642
|
+
n_workers (int, optional): The number of workers. Defaults to 2.
|
|
641
643
|
harddisc_threshold (int, optional): The number of DOF limit. Defaults to None.
|
|
642
644
|
harddisc_path (str, optional): The cached matrix path name. Defaults to 'EMergeSparse'.
|
|
643
645
|
frequency_groups (int, optional): The number of frequency points in a solve group. Defaults to -1.
|
|
@@ -687,7 +689,8 @@ class Microwave3D:
|
|
|
687
689
|
## DEFINE SOLVE FUNCTIONS
|
|
688
690
|
def get_routine():
|
|
689
691
|
if not hasattr(thread_local, "routine"):
|
|
690
|
-
|
|
692
|
+
worker_nr = int(threading.current_thread().name.split('_')[1])+1
|
|
693
|
+
thread_local.routine = self.solveroutine.duplicate()._configure_routine('MT', thread_nr=worker_nr)
|
|
691
694
|
return thread_local.routine
|
|
692
695
|
|
|
693
696
|
def run_job(job: SimJob):
|
|
@@ -750,7 +753,7 @@ class Microwave3D:
|
|
|
750
753
|
results.extend(group_results)
|
|
751
754
|
elif not multi_processing:
|
|
752
755
|
# MULTI THREADED
|
|
753
|
-
with ThreadPoolExecutor(max_workers=
|
|
756
|
+
with ThreadPoolExecutor(max_workers=n_workers, thread_name_prefix='WKR') as executor:
|
|
754
757
|
# ITERATE OVER FREQUENCIES
|
|
755
758
|
for i_group, fgroup in enumerate(freq_groups):
|
|
756
759
|
logger.info(f'Precomputing group {i_group}.')
|
|
@@ -771,7 +774,7 @@ class Microwave3D:
|
|
|
771
774
|
jobs.append(job)
|
|
772
775
|
matset.append(mats)
|
|
773
776
|
|
|
774
|
-
logger.info(f'Starting distributed solve of {len(jobs)} jobs with {
|
|
777
|
+
logger.info(f'Starting distributed solve of {len(jobs)} jobs with {n_workers} threads.')
|
|
775
778
|
group_results = list(executor.map(run_job, jobs))
|
|
776
779
|
results.extend(group_results)
|
|
777
780
|
executor.shutdown()
|
|
@@ -784,7 +787,7 @@ class Microwave3D:
|
|
|
784
787
|
"if __name__ == '__main__' guard in the top-level script."
|
|
785
788
|
)
|
|
786
789
|
# Start parallel pool
|
|
787
|
-
with mp.Pool(processes=
|
|
790
|
+
with mp.Pool(processes=n_workers, initializer=_init_worker) as pool:
|
|
788
791
|
for i_group, fgroup in enumerate(freq_groups):
|
|
789
792
|
logger.debug(f'Precomputing group {i_group}.')
|
|
790
793
|
jobs = []
|
|
@@ -810,7 +813,7 @@ class Microwave3D:
|
|
|
810
813
|
|
|
811
814
|
logger.info(
|
|
812
815
|
f'Starting distributed solve of {len(jobs)} jobs '
|
|
813
|
-
f'with {
|
|
816
|
+
f'with {n_workers} processes in parallel'
|
|
814
817
|
)
|
|
815
818
|
# Distribute taks
|
|
816
819
|
group_results = pool.map(run_job_multi, jobs)
|
|
@@ -805,7 +805,7 @@ class LumpedPort(PortBC):
|
|
|
805
805
|
port_number: int,
|
|
806
806
|
width: float | None = None,
|
|
807
807
|
height: float | None = None,
|
|
808
|
-
direction: Axis | None = None,
|
|
808
|
+
direction: Axis | tuple[float, float, float] | None = None,
|
|
809
809
|
power: float = 1,
|
|
810
810
|
Z0: float = 50):
|
|
811
811
|
"""Generates a lumped power boundary condition.
|
|
@@ -843,7 +843,7 @@ class LumpedPort(PortBC):
|
|
|
843
843
|
|
|
844
844
|
self.width: float = width
|
|
845
845
|
self.height: float = height # type: ignore
|
|
846
|
-
self.Vdirection: Axis = direction # type: ignore
|
|
846
|
+
self.Vdirection: Axis = _parse_axis(direction) # type: ignore
|
|
847
847
|
self.type = 'TEM'
|
|
848
848
|
|
|
849
849
|
# logger.info('Constructing coordinate system from normal port')
|
|
@@ -732,6 +732,15 @@ class MWField:
|
|
|
732
732
|
self._z = zs
|
|
733
733
|
return EHField(xs, ys, zs, self.Ex, self.Ey, self.Ez, self.Hx, self.Hy, self.Hz, self.freq, self.er, self.ur)
|
|
734
734
|
|
|
735
|
+
def boundary(self,
|
|
736
|
+
selection: FaceSelection) -> EHField:
|
|
737
|
+
nodes = self.mesh.nodes
|
|
738
|
+
x = nodes[0,:]
|
|
739
|
+
y = nodes[1,:]
|
|
740
|
+
z = nodes[2,:]
|
|
741
|
+
field = self.interpolate(x, y, z, False)
|
|
742
|
+
return field
|
|
743
|
+
|
|
735
744
|
def cutplane(self,
|
|
736
745
|
ds: float,
|
|
737
746
|
x: float | None = None,
|
|
@@ -545,9 +545,16 @@ class PVDisplay(BaseDisplay):
|
|
|
545
545
|
X,Y,Z = XYZ
|
|
546
546
|
else:
|
|
547
547
|
tris = self._mesh.get_triangles(port.selection.tags)
|
|
548
|
+
ids = np.sort(np.unique(self._mesh.tris[:,tris].flatten()))
|
|
548
549
|
X = self._mesh.tri_centers[0,tris]
|
|
549
550
|
Y = self._mesh.tri_centers[1,tris]
|
|
550
551
|
Z = self._mesh.tri_centers[2,tris]
|
|
552
|
+
X2 = self._mesh.nodes[0,ids]
|
|
553
|
+
Y2 = self._mesh.nodes[1,ids]
|
|
554
|
+
Z2 = self._mesh.nodes[2,ids]
|
|
555
|
+
X = np.concatenate((X,X2))
|
|
556
|
+
Y = np.concatenate((Y,Y2))
|
|
557
|
+
Z = np.concatenate((Z,Z2))
|
|
551
558
|
|
|
552
559
|
X = X+dv[0]
|
|
553
560
|
Y = Y+dv[1]
|
|
@@ -680,6 +687,87 @@ class PVDisplay(BaseDisplay):
|
|
|
680
687
|
self._objs.append(_AnimObject(field_flat, T, grid, grid_no_nan, actor, on_update))
|
|
681
688
|
|
|
682
689
|
self._reset_cbar()
|
|
690
|
+
|
|
691
|
+
def add_boundary_field(self,
|
|
692
|
+
selection: FaceSelection,
|
|
693
|
+
field: tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray],
|
|
694
|
+
scale: Literal['lin','log','symlog'] = 'lin',
|
|
695
|
+
cmap: cmap_names | None = None,
|
|
696
|
+
clim: tuple[float, float] | None = None,
|
|
697
|
+
opacity: float = 1.0,
|
|
698
|
+
symmetrize: bool = False,
|
|
699
|
+
_fieldname: str | None = None,
|
|
700
|
+
**kwargs,):
|
|
701
|
+
"""Add a surface plot to the display based on a boundary surface
|
|
702
|
+
|
|
703
|
+
The X,Y,Z coordinates must be a 2D grid of data points. The field must be a real field with the same size.
|
|
704
|
+
|
|
705
|
+
Example:
|
|
706
|
+
>>> display.add_boundary_field(selection, field.boundary(selction).scalar('normE','real'))
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
selection (FaceSelection): The boundary to show the field on
|
|
710
|
+
field (tuple[np.ndarray]): The output of EMField().boundary().scalar()
|
|
711
|
+
scale (Literal["lin","log","symlog"], optional): The colormap scaling¹. Defaults to 'lin'.
|
|
712
|
+
cmap (cmap_names, optional): The colormap. Defaults to 'coolwarm'.
|
|
713
|
+
clim (tuple[float, float], optional): Specific color limits (min, max). Defaults to None.
|
|
714
|
+
opacity (float, optional): The opacity of the surface. Defaults to 1.0.
|
|
715
|
+
symmetrize (bool, optional): Wether to force a symmetrical color limit (-A,A). Defaults to True.
|
|
716
|
+
|
|
717
|
+
(¹): lin: f(x)=x, log: f(x)=log₁₀(|x|), symlog: f(x)=sgn(x)·log₁₀(1+|x·ln(10)|)
|
|
718
|
+
"""
|
|
719
|
+
|
|
720
|
+
grid = self.mesh_surface(selection)
|
|
721
|
+
|
|
722
|
+
field = field[3]
|
|
723
|
+
field_flat = field.flatten(order='F')
|
|
724
|
+
|
|
725
|
+
if scale=='log':
|
|
726
|
+
T = lambda x: np.log10(np.abs(x+1e-12))
|
|
727
|
+
elif scale=='symlog':
|
|
728
|
+
T = lambda x: np.sign(x) * np.log10(1 + np.abs(x*np.log(10)))
|
|
729
|
+
else:
|
|
730
|
+
T = lambda x: x
|
|
731
|
+
|
|
732
|
+
static_field = T(np.real(field_flat))
|
|
733
|
+
|
|
734
|
+
if _fieldname is None:
|
|
735
|
+
name = 'anim'+str(self._ctr)
|
|
736
|
+
else:
|
|
737
|
+
name = _fieldname
|
|
738
|
+
self._ctr += 1
|
|
739
|
+
|
|
740
|
+
grid[name] = static_field
|
|
741
|
+
|
|
742
|
+
default_cmap = EMERGE_AMP
|
|
743
|
+
# Determine color limits
|
|
744
|
+
if clim is None:
|
|
745
|
+
if self._cbar_lim is not None:
|
|
746
|
+
clim = self._cbar_lim
|
|
747
|
+
else:
|
|
748
|
+
fmin = np.nanmin(static_field)
|
|
749
|
+
fmax = np.nanmax(static_field)
|
|
750
|
+
clim = (fmin, fmax)
|
|
751
|
+
|
|
752
|
+
if symmetrize:
|
|
753
|
+
lim = max(abs(clim[0]), abs(clim[1]))
|
|
754
|
+
clim = (-lim, lim)
|
|
755
|
+
default_cmap = EMERGE_WAVE
|
|
756
|
+
|
|
757
|
+
if cmap is None:
|
|
758
|
+
cmap = default_cmap
|
|
759
|
+
|
|
760
|
+
kwargs = setdefault(kwargs, cmap=cmap, clim=clim, opacity=opacity, pickable=False, multi_colors=True)
|
|
761
|
+
actor = self._plot.add_mesh(grid, scalars=name, scalar_bar_args=self._cbar_args, **kwargs)
|
|
762
|
+
|
|
763
|
+
if self._do_animate:
|
|
764
|
+
def on_update(obj: _AnimObject, phi: complex):
|
|
765
|
+
field_anim = obj.T(np.real(obj.field * phi))
|
|
766
|
+
obj.grid[name] = field_anim
|
|
767
|
+
#obj.fgrid replace with thresholded scalar data.
|
|
768
|
+
self._objs.append(_AnimObject(field_flat, T, grid, grid, actor, on_update))
|
|
769
|
+
|
|
770
|
+
self._reset_cbar()
|
|
683
771
|
|
|
684
772
|
def add_title(self, title: str) -> None:
|
|
685
773
|
"""Adds a title
|
emerge/_emerge/simmodel.py
CHANGED
|
@@ -479,7 +479,7 @@ class Simulation:
|
|
|
479
479
|
for geo in _GEOMANAGER.all_geometries():
|
|
480
480
|
self.display.add_object(geo, mesh=plot_mesh, opacity=opacity, volume_mesh=volume_mesh, label=labels)
|
|
481
481
|
if selections:
|
|
482
|
-
[self.display.add_object(sel, color='red', opacity=0.
|
|
482
|
+
[self.display.add_object(sel, color='red', opacity=0.6, label=labels) for sel in selections]
|
|
483
483
|
self.display.show()
|
|
484
484
|
|
|
485
485
|
return None
|