emerge 0.6.7__py3-none-any.whl → 0.6.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of emerge might be problematic. Click here for more details.
- emerge/__init__.py +2 -2
- emerge/_emerge/_cache_check.py +1 -1
- emerge/_emerge/elements/femdata.py +3 -2
- emerge/_emerge/elements/index_interp.py +1 -2
- emerge/_emerge/elements/ned2_interp.py +16 -16
- emerge/_emerge/elements/nedelec2.py +17 -6
- emerge/_emerge/elements/nedleg2.py +21 -9
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/horn.py +0 -1
- emerge/_emerge/geo/modeler.py +1 -1
- emerge/_emerge/geo/operations.py +13 -0
- emerge/_emerge/geo/pcb_tools/calculator.py +2 -3
- emerge/_emerge/geo/pmlbox.py +35 -11
- emerge/_emerge/geometry.py +4 -4
- emerge/_emerge/material.py +326 -81
- emerge/_emerge/mesh3d.py +14 -8
- emerge/_emerge/physics/microwave/assembly/assembler.py +43 -20
- emerge/_emerge/physics/microwave/microwave_3d.py +57 -44
- emerge/_emerge/physics/microwave/microwave_bc.py +10 -4
- emerge/_emerge/physics/microwave/microwave_data.py +61 -5
- emerge/_emerge/plot/pyvista/display.py +21 -11
- emerge/_emerge/plot/simple_plots.py +1 -1
- emerge/_emerge/simmodel.py +12 -5
- emerge/_emerge/solver.py +45 -18
- {emerge-0.6.7.dist-info → emerge-0.6.8.dist-info}/METADATA +1 -1
- {emerge-0.6.7.dist-info → emerge-0.6.8.dist-info}/RECORD +29 -29
- {emerge-0.6.7.dist-info → emerge-0.6.8.dist-info}/licenses/LICENSE +2 -2
- {emerge-0.6.7.dist-info → emerge-0.6.8.dist-info}/WHEEL +0 -0
- {emerge-0.6.7.dist-info → emerge-0.6.8.dist-info}/entry_points.txt +0 -0
|
@@ -269,7 +269,7 @@ class Microwave3D:
|
|
|
269
269
|
Callable: The discretizer function
|
|
270
270
|
"""
|
|
271
271
|
def disc(material: Material):
|
|
272
|
-
return 299792458/(max(self.frequencies) * np.real(material.neff))
|
|
272
|
+
return 299792458/(max(self.frequencies) * np.real(material.neff(max(self.frequencies))))
|
|
273
273
|
return disc
|
|
274
274
|
|
|
275
275
|
def _initialize_field(self):
|
|
@@ -467,10 +467,25 @@ class Microwave3D:
|
|
|
467
467
|
raise SimulationError('Cannot proceed, the current basis class is undefined.')
|
|
468
468
|
|
|
469
469
|
logger.debug('Retreiving material properties.')
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
470
|
+
|
|
471
|
+
if freq is None:
|
|
472
|
+
freq = self.frequencies[0]
|
|
473
|
+
|
|
474
|
+
materials = self.mesh.retreive(self.mesher.volumes)
|
|
473
475
|
|
|
476
|
+
ertet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
477
|
+
tandtet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
478
|
+
urtet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
479
|
+
condtet = np.zeros((3,3,self.mesh.n_tets), dtype=np.complex128)
|
|
480
|
+
|
|
481
|
+
for mat in materials:
|
|
482
|
+
ertet = mat.er(freq, ertet)
|
|
483
|
+
tandtet = mat.tand(freq, tandtet)
|
|
484
|
+
urtet = mat.ur(freq, urtet)
|
|
485
|
+
condtet = mat.cond(freq, condtet)
|
|
486
|
+
|
|
487
|
+
ertet = ertet * (1-1j*tandtet)
|
|
488
|
+
|
|
474
489
|
er = np.zeros((3,3,self.mesh.n_tris,), dtype=np.complex128)
|
|
475
490
|
ur = np.zeros((3,3,self.mesh.n_tris,), dtype=np.complex128)
|
|
476
491
|
cond = np.zeros((self.mesh.n_tris,), dtype=np.complex128)
|
|
@@ -479,7 +494,7 @@ class Microwave3D:
|
|
|
479
494
|
itet = self.mesh.tri_to_tet[0,itri]
|
|
480
495
|
er[:,:,itri] = ertet[:,:,itet]
|
|
481
496
|
ur[:,:,itri] = urtet[:,:,itet]
|
|
482
|
-
cond[itri] = condtet[itet]
|
|
497
|
+
cond[itri] = condtet[0,0,itet]
|
|
483
498
|
|
|
484
499
|
itri_port = self.mesh.get_triangles(port.tags)
|
|
485
500
|
|
|
@@ -488,11 +503,7 @@ class Microwave3D:
|
|
|
488
503
|
ermax = np.max(er[:,:,itri_port].flatten())
|
|
489
504
|
urmax = np.max(ur[:,:,itri_port].flatten())
|
|
490
505
|
|
|
491
|
-
if freq is None:
|
|
492
|
-
freq = self.frequencies[0]
|
|
493
|
-
|
|
494
506
|
k0 = 2*np.pi*freq/299792458
|
|
495
|
-
kmax = k0*np.sqrt(ermax.real*urmax.real)
|
|
496
507
|
|
|
497
508
|
Amatrix, Bmatrix, solve_ids, nlf = self.assembler.assemble_bma_matrices(self.basis, er, ur, cond, k0, port, self.bc)
|
|
498
509
|
|
|
@@ -511,8 +522,7 @@ class Microwave3D:
|
|
|
511
522
|
else:
|
|
512
523
|
|
|
513
524
|
target_kz = ermean*urmean*0.7*k0
|
|
514
|
-
|
|
515
|
-
|
|
525
|
+
|
|
516
526
|
logger.debug(f'Solving for {solve_ids.shape[0]} degrees of freedom.')
|
|
517
527
|
|
|
518
528
|
eigen_values, eigen_modes, report = self.solveroutine.eig_boundary(Amatrix, Bmatrix, solve_ids, nmodes, direct, target_kz, sign=-1)
|
|
@@ -550,12 +560,10 @@ class Microwave3D:
|
|
|
550
560
|
Ez = np.max(np.abs(Efz))
|
|
551
561
|
Exy = np.max(np.abs(Efxy))
|
|
552
562
|
|
|
553
|
-
|
|
554
|
-
# Ez = 0
|
|
555
|
-
if Ez/Exy < 1e-3 and not TEM:
|
|
563
|
+
if Ez/Exy < 1e-1 and not TEM:
|
|
556
564
|
logger.debug('Low Ez/Et ratio detected, assuming TE mode')
|
|
557
565
|
mode.modetype = 'TE'
|
|
558
|
-
elif Ez/Exy > 1e-
|
|
566
|
+
elif Ez/Exy > 1e-1 and not TEM:
|
|
559
567
|
logger.debug('High Ez/Et ratio detected, assuming TM mode')
|
|
560
568
|
mode.modetype = 'TM'
|
|
561
569
|
elif TEM:
|
|
@@ -621,9 +629,7 @@ class Microwave3D:
|
|
|
621
629
|
if self.basis is None:
|
|
622
630
|
raise SimulationError('Cannot proceed, the simulation basis class is undefined.')
|
|
623
631
|
|
|
624
|
-
|
|
625
|
-
ur = self.mesh.retreive(lambda mat,x,y,z: mat.fur3d_mat(x,y,z), self.mesher.volumes)
|
|
626
|
-
cond = self.mesh.retreive(lambda mat,x,y,z: mat.cond, self.mesher.volumes)[0,0,:]
|
|
632
|
+
materials = self.mesh.retreive(self.mesher.volumes)
|
|
627
633
|
|
|
628
634
|
### Does this move
|
|
629
635
|
logger.debug('Initializing frequency domain sweep.')
|
|
@@ -674,7 +680,8 @@ class Microwave3D:
|
|
|
674
680
|
freq_groups = [self.frequencies[i:i+n] for i in range(0, len(self.frequencies), n)]
|
|
675
681
|
|
|
676
682
|
results: list[SimJob] = []
|
|
677
|
-
|
|
683
|
+
matset: list[tuple[np.ndarray, np.ndarray, np.ndarray]] = []
|
|
684
|
+
|
|
678
685
|
## Single threaded
|
|
679
686
|
job_id = 1
|
|
680
687
|
|
|
@@ -691,7 +698,7 @@ class Microwave3D:
|
|
|
691
698
|
logger.debug(f'Simulation frequency = {freq/1e9:.3f} GHz')
|
|
692
699
|
if automatic_modal_analysis:
|
|
693
700
|
self._compute_modes(freq)
|
|
694
|
-
job = self.assembler.assemble_freq_matrix(self.basis,
|
|
701
|
+
job, mats = self.assembler.assemble_freq_matrix(self.basis, materials,
|
|
695
702
|
self.bc.boundary_conditions,
|
|
696
703
|
freq,
|
|
697
704
|
cache_matrices=self.cache_matrices)
|
|
@@ -700,6 +707,7 @@ class Microwave3D:
|
|
|
700
707
|
job.id = job_id
|
|
701
708
|
job_id += 1
|
|
702
709
|
jobs.append(job)
|
|
710
|
+
matset.append(mats)
|
|
703
711
|
|
|
704
712
|
logger.info(f'Starting single threaded solve of {len(jobs)} jobs.')
|
|
705
713
|
group_results = [run_job_single(job) for job in jobs]
|
|
@@ -716,7 +724,7 @@ class Microwave3D:
|
|
|
716
724
|
logger.debug(f'Simulation frequency = {freq/1e9:.3f} GHz')
|
|
717
725
|
if automatic_modal_analysis:
|
|
718
726
|
self._compute_modes(freq)
|
|
719
|
-
job = self.assembler.assemble_freq_matrix(self.basis,
|
|
727
|
+
job, mats = self.assembler.assemble_freq_matrix(self.basis, materials,
|
|
720
728
|
self.bc.boundary_conditions,
|
|
721
729
|
freq,
|
|
722
730
|
cache_matrices=self.cache_matrices)
|
|
@@ -725,6 +733,7 @@ class Microwave3D:
|
|
|
725
733
|
job.id = job_id
|
|
726
734
|
job_id += 1
|
|
727
735
|
jobs.append(job)
|
|
736
|
+
matset.append(mats)
|
|
728
737
|
|
|
729
738
|
logger.info(f'Starting distributed solve of {len(jobs)} jobs with {njobs} threads.')
|
|
730
739
|
group_results = list(executor.map(run_job, jobs))
|
|
@@ -749,8 +758,8 @@ class Microwave3D:
|
|
|
749
758
|
if automatic_modal_analysis:
|
|
750
759
|
self._compute_modes(freq)
|
|
751
760
|
|
|
752
|
-
job = self.assembler.assemble_freq_matrix(
|
|
753
|
-
self.basis,
|
|
761
|
+
job, mats = self.assembler.assemble_freq_matrix(
|
|
762
|
+
self.basis, materials,
|
|
754
763
|
self.bc.boundary_conditions,
|
|
755
764
|
freq,
|
|
756
765
|
cache_matrices=self.cache_matrices
|
|
@@ -761,6 +770,7 @@ class Microwave3D:
|
|
|
761
770
|
job.id = job_id
|
|
762
771
|
job_id += 1
|
|
763
772
|
jobs.append(job)
|
|
773
|
+
matset.append(mats)
|
|
764
774
|
|
|
765
775
|
logger.info(
|
|
766
776
|
f'Starting distributed solve of {len(jobs)} jobs '
|
|
@@ -783,7 +793,7 @@ class Microwave3D:
|
|
|
783
793
|
|
|
784
794
|
self.solveroutine.reset()
|
|
785
795
|
### Compute S-parameters and return
|
|
786
|
-
self._post_process(results,
|
|
796
|
+
self._post_process(results, matset)
|
|
787
797
|
return self.data
|
|
788
798
|
|
|
789
799
|
def eigenmode(self, search_frequency: float,
|
|
@@ -817,19 +827,21 @@ class Microwave3D:
|
|
|
817
827
|
if self.basis is None:
|
|
818
828
|
raise SimulationError('Cannot proceed. The simulation basis class is undefined.')
|
|
819
829
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
830
|
+
materials = self.mesh.retreive(self.mesher.volumes)
|
|
831
|
+
|
|
832
|
+
# er = self.mesh.retreive(lambda mat,x,y,z: mat.fer3d_mat(x,y,z), self.mesher.volumes)
|
|
833
|
+
# ur = self.mesh.retreive(lambda mat,x,y,z: mat.fur3d_mat(x,y,z), self.mesher.volumes)
|
|
834
|
+
# cond = self.mesh.retreive(lambda mat,x,y,z: mat.cond, self.mesher.volumes)[0,0,:]
|
|
823
835
|
|
|
824
836
|
### Does this move
|
|
825
837
|
logger.debug('Initializing frequency domain sweep.')
|
|
826
838
|
|
|
827
839
|
logger.info(f'Pre-assembling matrices of {len(self.frequencies)} frequency points.')
|
|
828
840
|
|
|
829
|
-
job = self.assembler.assemble_eig_matrix(self.basis,
|
|
841
|
+
job, matset = self.assembler.assemble_eig_matrix(self.basis, materials,
|
|
830
842
|
self.bc.boundary_conditions, search_frequency)
|
|
831
843
|
|
|
832
|
-
|
|
844
|
+
er, ur, cond = matset
|
|
833
845
|
logger.info('Solving complete')
|
|
834
846
|
|
|
835
847
|
A, C, solve_ids = job.yield_AC()
|
|
@@ -846,7 +858,7 @@ class Microwave3D:
|
|
|
846
858
|
|
|
847
859
|
for i in range(nmodes_found):
|
|
848
860
|
|
|
849
|
-
|
|
861
|
+
|
|
850
862
|
eig_k0 = np.sqrt(eigen_values[i])
|
|
851
863
|
if eig_k0 < k0_limit:
|
|
852
864
|
logger.debug(f'Ignoring mode due to low k0: {eig_k0} < {k0_limit}')
|
|
@@ -856,11 +868,11 @@ class Microwave3D:
|
|
|
856
868
|
logger.debug(f'Found k0={eig_k0:.2f}, f0={eig_freq/1e9:.2f} GHz')
|
|
857
869
|
Emode = eigen_modes[:,i]
|
|
858
870
|
|
|
859
|
-
scalardata = self.data.scalar.new(
|
|
871
|
+
scalardata = self.data.scalar.new(**self._params)
|
|
860
872
|
scalardata.k0 = eig_k0
|
|
861
873
|
scalardata.freq = eig_freq
|
|
862
874
|
|
|
863
|
-
fielddata = self.data.field.new(
|
|
875
|
+
fielddata = self.data.field.new(**self._params)
|
|
864
876
|
fielddata.freq = eig_freq
|
|
865
877
|
fielddata._der = np.squeeze(er[0,0,:])
|
|
866
878
|
fielddata._dur = np.squeeze(ur[0,0,:])
|
|
@@ -870,7 +882,7 @@ class Microwave3D:
|
|
|
870
882
|
|
|
871
883
|
return self.data
|
|
872
884
|
|
|
873
|
-
def _post_process(self, results: list[SimJob],
|
|
885
|
+
def _post_process(self, results: list[SimJob], materials: list[tuple[np.ndarray, np.ndarray, np.ndarray]]):
|
|
874
886
|
"""Compute the S-parameters after Frequency sweep
|
|
875
887
|
|
|
876
888
|
Args:
|
|
@@ -888,18 +900,19 @@ class Microwave3D:
|
|
|
888
900
|
|
|
889
901
|
logger.info('Computing S-parameters')
|
|
890
902
|
|
|
891
|
-
ertri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
|
|
892
|
-
urtri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
|
|
893
|
-
condtri = np.zeros((self.mesh.n_tris,), dtype=np.complex128)
|
|
894
|
-
|
|
895
|
-
for itri in range(self.mesh.n_tris):
|
|
896
|
-
itet = self.mesh.tri_to_tet[0,itri]
|
|
897
|
-
ertri[:,:,itri] = er[:,:,itet]
|
|
898
|
-
urtri[:,:,itri] = ur[:,:,itet]
|
|
899
|
-
condtri[itri] = cond[itet]
|
|
900
|
-
|
|
901
|
-
for freq, job in zip(self.frequencies, results):
|
|
902
903
|
|
|
904
|
+
for freq, job, mats in zip(self.frequencies, results, materials):
|
|
905
|
+
er, ur, cond = mats
|
|
906
|
+
ertri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
|
|
907
|
+
urtri = np.zeros((3,3,self.mesh.n_tris), dtype=np.complex128)
|
|
908
|
+
condtri = np.zeros((self.mesh.n_tris,), dtype=np.complex128)
|
|
909
|
+
|
|
910
|
+
for itri in range(self.mesh.n_tris):
|
|
911
|
+
itet = self.mesh.tri_to_tet[0,itri]
|
|
912
|
+
ertri[:,:,itri] = er[:,:,itet]
|
|
913
|
+
urtri[:,:,itri] = ur[:,:,itet]
|
|
914
|
+
condtri[itri] = cond[0,0,itet]
|
|
915
|
+
|
|
903
916
|
k0 = 2*np.pi*freq/299792458
|
|
904
917
|
|
|
905
918
|
scalardata = self.data.scalar.new(freq=freq, **self._params)
|
|
@@ -73,7 +73,7 @@ class MWBoundaryConditionSet(BoundaryConditionSet):
|
|
|
73
73
|
"""
|
|
74
74
|
bcs = self.oftype(PEC)
|
|
75
75
|
for bc in self.oftype(SurfaceImpedance):
|
|
76
|
-
if bc.
|
|
76
|
+
if bc.sigma > 1e3:
|
|
77
77
|
bcs.append(bc)
|
|
78
78
|
|
|
79
79
|
return bcs
|
|
@@ -1034,12 +1034,13 @@ class SurfaceImpedance(RobinBC):
|
|
|
1034
1034
|
self._material: Material | None = material
|
|
1035
1035
|
self._mur: float | complex = 1.0
|
|
1036
1036
|
self._epsr: float | complex = 1.0
|
|
1037
|
-
|
|
1038
1037
|
self.sigma: float = 0.0
|
|
1038
|
+
|
|
1039
1039
|
if material is not None:
|
|
1040
1040
|
self.sigma = material.cond
|
|
1041
1041
|
self._mur = material.ur
|
|
1042
1042
|
self._epsr = material.er
|
|
1043
|
+
|
|
1043
1044
|
if surface_conductance is not None:
|
|
1044
1045
|
self.sigma = surface_conductance
|
|
1045
1046
|
|
|
@@ -1066,10 +1067,15 @@ class SurfaceImpedance(RobinBC):
|
|
|
1066
1067
|
Returns:
|
|
1067
1068
|
complex: The γ-constant
|
|
1068
1069
|
"""
|
|
1070
|
+
|
|
1069
1071
|
w0 = k0*C0
|
|
1070
|
-
|
|
1072
|
+
f0 = w0/(2*np.pi)
|
|
1073
|
+
sigma = self.sigma.scalar(f0)
|
|
1074
|
+
mur = self._material.ur.scalar(f0)
|
|
1075
|
+
er = self._material.er.scalar(f0)
|
|
1076
|
+
|
|
1071
1077
|
rho = 1/sigma
|
|
1072
|
-
d_skin = (2*rho/(w0*MU0*
|
|
1078
|
+
d_skin = (2*rho/(w0*MU0*mur) * ((1+(w0*EPS0*er*rho)**2)**0.5 + rho*w0*EPS0*er))**0.5
|
|
1073
1079
|
R = rho/d_skin
|
|
1074
1080
|
if self._sr_model=='Hammerstad-Jensen' and self._sr > 0.0:
|
|
1075
1081
|
R = R * (1 + 2/np.pi * np.arctan(1.4*(self._sr/d_skin)**2))
|
|
@@ -282,9 +282,9 @@ class FarFieldData:
|
|
|
282
282
|
|
|
283
283
|
@property
|
|
284
284
|
def Etheta(self) -> np.ndarray:
|
|
285
|
-
thx =
|
|
286
|
-
thy =
|
|
287
|
-
thz = np.sin(self.theta)
|
|
285
|
+
thx = np.cos(self.theta)*np.cos(self.phi)
|
|
286
|
+
thy = np.cos(self.theta)*np.sin(self.phi)
|
|
287
|
+
thz = -np.sin(self.theta)
|
|
288
288
|
return thx*self.E[0,:] + thy*self.E[1,:] + thz*self.E[2,:]
|
|
289
289
|
|
|
290
290
|
@property
|
|
@@ -296,11 +296,11 @@ class FarFieldData:
|
|
|
296
296
|
|
|
297
297
|
@property
|
|
298
298
|
def Erhcp(self) -> np.ndarray:
|
|
299
|
-
return (self.Etheta
|
|
299
|
+
return (self.Etheta + 1j*self.Ephi)/np.sqrt(2)
|
|
300
300
|
|
|
301
301
|
@property
|
|
302
302
|
def Elhcp(self) -> np.ndarray:
|
|
303
|
-
return (self.Etheta
|
|
303
|
+
return (self.Etheta - 1j*self.Ephi)/np.sqrt(2)
|
|
304
304
|
|
|
305
305
|
@property
|
|
306
306
|
def AR(self) -> np.ndarray:
|
|
@@ -702,6 +702,11 @@ class MWField:
|
|
|
702
702
|
|
|
703
703
|
def interpolate(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> EHField:
|
|
704
704
|
''' Interpolate the dataset in the provided xs, ys, zs values'''
|
|
705
|
+
if isinstance(xs, (float, int, complex)):
|
|
706
|
+
xs = np.array([xs,])
|
|
707
|
+
ys = np.array([ys,])
|
|
708
|
+
zs = np.array([zs,])
|
|
709
|
+
|
|
705
710
|
shp = xs.shape
|
|
706
711
|
xf = xs.flatten()
|
|
707
712
|
yf = ys.flatten()
|
|
@@ -748,6 +753,7 @@ class MWField:
|
|
|
748
753
|
xs = np.linspace(xb[0], xb[1], int((xb[1]-xb[0])/ds))
|
|
749
754
|
ys = np.linspace(yb[0], yb[1], int((yb[1]-yb[0])/ds))
|
|
750
755
|
zs = np.linspace(zb[0], zb[1], int((zb[1]-zb[0])/ds))
|
|
756
|
+
|
|
751
757
|
if x is not None:
|
|
752
758
|
Y,Z = np.meshgrid(ys, zs)
|
|
753
759
|
X = x*np.ones_like(Y)
|
|
@@ -759,6 +765,56 @@ class MWField:
|
|
|
759
765
|
Z = z*np.ones_like(Y)
|
|
760
766
|
return self.interpolate(X,Y,Z)
|
|
761
767
|
|
|
768
|
+
def cutplane_normal(self,
|
|
769
|
+
point=(0,0,0),
|
|
770
|
+
normal=(0,0,1),
|
|
771
|
+
npoints: int = 300) -> EHField:
|
|
772
|
+
"""
|
|
773
|
+
Take a 2D slice of the field along an arbitrary plane.
|
|
774
|
+
Args:
|
|
775
|
+
point: (x0,y0,z0), a point on the plane
|
|
776
|
+
normal: (nx,ny,nz), plane normal vector
|
|
777
|
+
npoints: number of grid points per axis
|
|
778
|
+
"""
|
|
779
|
+
|
|
780
|
+
n = np.array(normal, dtype=float)
|
|
781
|
+
n /= np.linalg.norm(n)
|
|
782
|
+
point = np.array(point)
|
|
783
|
+
|
|
784
|
+
tmp = np.array([1,0,0]) if abs(n[0]) < 0.9 else np.array([0,1,0])
|
|
785
|
+
u = np.cross(n, tmp)
|
|
786
|
+
u /= np.linalg.norm(u)
|
|
787
|
+
v = np.cross(n, u)
|
|
788
|
+
|
|
789
|
+
xb, yb, zb = self.basis.bounds
|
|
790
|
+
nx, ny, nz = 5, 5, 5
|
|
791
|
+
Xg = np.linspace(xb[0], xb[1], nx)
|
|
792
|
+
Yg = np.linspace(yb[0], yb[1], ny)
|
|
793
|
+
Zg = np.linspace(zb[0], zb[1], nz)
|
|
794
|
+
Xg, Yg, Zg = np.meshgrid(Xg, Yg, Zg, indexing='ij')
|
|
795
|
+
geometry = np.vstack([Xg.ravel(), Yg.ravel(), Zg.ravel()]).T # Nx3
|
|
796
|
+
|
|
797
|
+
rel_pts = geometry - point
|
|
798
|
+
S = rel_pts @ u
|
|
799
|
+
T = rel_pts @ v
|
|
800
|
+
|
|
801
|
+
margin = 0.01
|
|
802
|
+
s_min, s_max = S.min(), S.max()
|
|
803
|
+
t_min, t_max = T.min(), T.max()
|
|
804
|
+
s_bounds = (s_min - margin*(s_max-s_min), s_max + margin*(s_max-s_min))
|
|
805
|
+
t_bounds = (t_min - margin*(t_max-t_min), t_max + margin*(t_max-t_min))
|
|
806
|
+
|
|
807
|
+
S_grid = np.linspace(s_bounds[0], s_bounds[1], npoints)
|
|
808
|
+
T_grid = np.linspace(t_bounds[0], t_bounds[1], npoints)
|
|
809
|
+
S_mesh, T_mesh = np.meshgrid(S_grid, T_grid)
|
|
810
|
+
|
|
811
|
+
X = point[0] + S_mesh*u[0] + T_mesh*v[0]
|
|
812
|
+
Y = point[1] + S_mesh*u[1] + T_mesh*v[1]
|
|
813
|
+
Z = point[2] + S_mesh*u[2] + T_mesh*v[2]
|
|
814
|
+
|
|
815
|
+
return self.interpolate(X, Y, Z)
|
|
816
|
+
|
|
817
|
+
|
|
762
818
|
def grid(self, ds: float) -> EHField:
|
|
763
819
|
"""Interpolate a uniform grid sampled at ds
|
|
764
820
|
|
|
@@ -315,7 +315,6 @@ class PVDisplay(BaseDisplay):
|
|
|
315
315
|
"""The private callback function that stops the animation.
|
|
316
316
|
"""
|
|
317
317
|
self._stop = True
|
|
318
|
-
print('CLOSE!')
|
|
319
318
|
|
|
320
319
|
def _animate(self) -> None:
|
|
321
320
|
"""Private function that starts the animation loop.
|
|
@@ -567,6 +566,7 @@ class PVDisplay(BaseDisplay):
|
|
|
567
566
|
grid = pv.StructuredGrid(x,y,z)
|
|
568
567
|
field_flat = field.flatten(order='F')
|
|
569
568
|
|
|
569
|
+
|
|
570
570
|
if scale=='log':
|
|
571
571
|
T = lambda x: np.log10(np.abs(x))
|
|
572
572
|
elif scale=='symlog':
|
|
@@ -582,25 +582,28 @@ class PVDisplay(BaseDisplay):
|
|
|
582
582
|
self._ctr += 1
|
|
583
583
|
grid[name] = static_field
|
|
584
584
|
|
|
585
|
+
grid_no_nan = grid.threshold(scalars=name)
|
|
586
|
+
|
|
587
|
+
# Determine color limits
|
|
585
588
|
if clim is None:
|
|
586
|
-
fmin = np.
|
|
587
|
-
fmax = np.
|
|
589
|
+
fmin = np.nanmin(static_field)
|
|
590
|
+
fmax = np.nanmax(static_field)
|
|
588
591
|
clim = (fmin, fmax)
|
|
589
|
-
|
|
590
592
|
if symmetrize:
|
|
591
|
-
lim = max(abs(clim[0]),abs(clim[1]))
|
|
593
|
+
lim = max(abs(clim[0]), abs(clim[1]))
|
|
592
594
|
clim = (-lim, lim)
|
|
593
595
|
|
|
594
596
|
kwargs = setdefault(kwargs, cmap=cmap, clim=clim, opacity=opacity, pickable=False, multi_colors=True)
|
|
595
|
-
actor = self._plot.add_mesh(
|
|
597
|
+
actor = self._plot.add_mesh(grid_no_nan, scalars=name, **kwargs)
|
|
598
|
+
|
|
596
599
|
|
|
597
600
|
if self._do_animate:
|
|
598
601
|
def on_update(obj: _AnimObject, phi: complex):
|
|
599
|
-
|
|
600
|
-
obj.grid[name] =
|
|
601
|
-
self._objs.append(_AnimObject(field_flat, T,
|
|
602
|
-
|
|
603
|
-
|
|
602
|
+
field_anim = obj.T(np.real(obj.field * phi))
|
|
603
|
+
obj.grid[name] = field_anim
|
|
604
|
+
self._objs.append(_AnimObject(field_flat, T, grid_no_nan, actor, on_update))
|
|
605
|
+
|
|
606
|
+
|
|
604
607
|
def add_title(self, title: str) -> None:
|
|
605
608
|
"""Adds a title
|
|
606
609
|
|
|
@@ -652,6 +655,11 @@ class PVDisplay(BaseDisplay):
|
|
|
652
655
|
dx = dx.flatten().real
|
|
653
656
|
dy = dy.flatten().real
|
|
654
657
|
dz = dz.flatten().real
|
|
658
|
+
|
|
659
|
+
ids = np.invert(np.isnan(dx))
|
|
660
|
+
|
|
661
|
+
x, y, z, dx, dy, dz = x[ids], y[ids], z[ids], dx[ids], dy[ids], dz[ids]
|
|
662
|
+
|
|
655
663
|
dmin = _min_distance(x,y,z)
|
|
656
664
|
|
|
657
665
|
dmax = np.max(_norm(dx,dy,dz))
|
|
@@ -667,8 +675,10 @@ class PVDisplay(BaseDisplay):
|
|
|
667
675
|
kwargs = dict()
|
|
668
676
|
if color is not None:
|
|
669
677
|
kwargs['color'] = color
|
|
678
|
+
|
|
670
679
|
pl = self._plot.add_arrows(Coo, Vec, scalars=None, clim=None, cmap=None, **kwargs)
|
|
671
680
|
|
|
681
|
+
|
|
672
682
|
def add_contour(self,
|
|
673
683
|
X: np.ndarray,
|
|
674
684
|
Y: np.ndarray,
|
|
@@ -533,7 +533,7 @@ def plot_ff(
|
|
|
533
533
|
dB: bool = False,
|
|
534
534
|
labels: Optional[List[str]] = None,
|
|
535
535
|
xlabel: str = "Theta (rad)",
|
|
536
|
-
ylabel: str = "
|
|
536
|
+
ylabel: str = "",
|
|
537
537
|
linestyles: Union[str, List[str]] = "-",
|
|
538
538
|
linewidth: float = 2.0,
|
|
539
539
|
markers: Optional[Union[str, List[Optional[str]]]] = None,
|
emerge/_emerge/simmodel.py
CHANGED
|
@@ -245,13 +245,20 @@ class Simulation:
|
|
|
245
245
|
vM, vm, vp = [float(x) for x in version.split('.')]
|
|
246
246
|
cM, cm, cp = [float(x) for x in __version__.split('.')]
|
|
247
247
|
if vM != cM:
|
|
248
|
-
raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}")
|
|
248
|
+
raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}. \n You can upgrade your version of emerge with: pip --upgrade emerge")
|
|
249
249
|
if vm != cm:
|
|
250
|
-
raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}")
|
|
250
|
+
raise VersionError(f"You are running a script designed for version {version} with a possibly incompatible version of EMerge {__version__}. \n You can upgrade your version of emerge with: pip --upgrade emerge")
|
|
251
251
|
if vp != cp:
|
|
252
|
-
logger.warning(
|
|
252
|
+
logger.warning("You are running a script designed for a different version of EMerge.")
|
|
253
|
+
logger.warning(f"The script version: {version}")
|
|
254
|
+
logger.warning(f"EMerge version: {__version__}")
|
|
255
|
+
logger.warning("Usually EMerge works without a problem but Errors may occur.")
|
|
256
|
+
logger.warning("You can upgrade your version of emerge with: pip --upgrade emerge")
|
|
253
257
|
logger.warning("You may suppress this error by removing the call to .check_version().")
|
|
254
|
-
|
|
258
|
+
logger.warning("Press Ctrl+C to abort.")
|
|
259
|
+
ans = input('Press enter to proceed or [Q] to quit:')
|
|
260
|
+
if ans.lower().strip()=='q':
|
|
261
|
+
quit()
|
|
255
262
|
|
|
256
263
|
def save(self) -> None:
|
|
257
264
|
"""Saves the current model in the provided project directory."""
|
|
@@ -458,7 +465,7 @@ class Simulation:
|
|
|
458
465
|
|
|
459
466
|
logger.info(f'Iterating: {params}')
|
|
460
467
|
if len(dims_flat)==1:
|
|
461
|
-
yield
|
|
468
|
+
yield dims_flat[0][i_iter]
|
|
462
469
|
else:
|
|
463
470
|
yield (dim[i_iter] for dim in dims_flat) # type: ignore
|
|
464
471
|
self.mw.cache_matrices = True
|
emerge/_emerge/solver.py
CHANGED
|
@@ -289,10 +289,14 @@ class Solver:
|
|
|
289
289
|
|
|
290
290
|
def __init__(self):
|
|
291
291
|
self.own_preconditioner: bool = False
|
|
292
|
+
self.initialized: bool = False
|
|
292
293
|
|
|
293
294
|
def __str__(self) -> str:
|
|
294
295
|
return f'{self.__class__.__name__}'
|
|
295
296
|
|
|
297
|
+
def initialize(self) -> None:
|
|
298
|
+
return None
|
|
299
|
+
|
|
296
300
|
def duplicate(self) -> Solver:
|
|
297
301
|
return self.__class__()
|
|
298
302
|
|
|
@@ -324,6 +328,9 @@ class EigSolver:
|
|
|
324
328
|
def __init__(self):
|
|
325
329
|
self.own_preconditioner: bool = False
|
|
326
330
|
|
|
331
|
+
def initialize(self) -> None:
|
|
332
|
+
return None
|
|
333
|
+
|
|
327
334
|
def __str__(self) -> str:
|
|
328
335
|
return f'{self.__class__.__name__}'
|
|
329
336
|
|
|
@@ -513,7 +520,19 @@ class SolverUMFPACK(Solver):
|
|
|
513
520
|
super().__init__()
|
|
514
521
|
self.A: np.ndarray = None
|
|
515
522
|
self.b: np.ndarray = None
|
|
516
|
-
|
|
523
|
+
|
|
524
|
+
self.umfpack: um.UmfpackContext | None = None
|
|
525
|
+
|
|
526
|
+
# SETTINGS
|
|
527
|
+
self._pivoting_threshold: float = 0.001
|
|
528
|
+
|
|
529
|
+
self.fact_symb: bool = False
|
|
530
|
+
self.initalized: bool = False
|
|
531
|
+
|
|
532
|
+
def initialize(self):
|
|
533
|
+
if self.initalized:
|
|
534
|
+
return
|
|
535
|
+
self.umfpack = um.UmfpackContext('zl')
|
|
517
536
|
self.umfpack.control[um.UMFPACK_PRL] = 0 # ty: ignore
|
|
518
537
|
self.umfpack.control[um.UMFPACK_IRSTEP] = 2 # ty: ignore
|
|
519
538
|
self.umfpack.control[um.UMFPACK_STRATEGY] = um.UMFPACK_STRATEGY_SYMMETRIC # ty: ignore
|
|
@@ -522,21 +541,16 @@ class SolverUMFPACK(Solver):
|
|
|
522
541
|
self.umfpack.control[um.UMFPACK_SYM_PIVOT_TOLERANCE] = 0.001 # ty: ignore
|
|
523
542
|
self.umfpack.control[um.UMFPACK_BLOCK_SIZE] = 64 # ty: ignore
|
|
524
543
|
self.umfpack.control[um.UMFPACK_FIXQ] = -1 # ty: ignore
|
|
525
|
-
|
|
526
|
-
# SETTINGS
|
|
527
|
-
self._pivoting_threshold: float = 0.001
|
|
528
|
-
|
|
529
|
-
self.fact_symb: bool = False
|
|
530
|
-
|
|
544
|
+
self.initalized = True
|
|
531
545
|
def reset(self) -> None:
|
|
532
546
|
self.fact_symb = False
|
|
533
547
|
|
|
534
|
-
def set_options(self,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
548
|
+
def set_options(self, pivoting_threshold: float | None = None) -> None:
|
|
549
|
+
self.initialize()
|
|
550
|
+
if pivoting_threshold is not None:
|
|
551
|
+
self.umfpack.control[um.UMFPACK_PIVOT_TOLERANCE] = pivoting_threshold # ty: ignore
|
|
552
|
+
self.umfpack.control[um.UMFPACK_SYM_PIVOT_TOLERANCE] = pivoting_threshold # ty: ignore
|
|
553
|
+
self._pivoting_threshold = pivoting_threshold
|
|
540
554
|
|
|
541
555
|
def duplicate(self) -> Solver:
|
|
542
556
|
new_solver = self.__class__()
|
|
@@ -568,11 +582,17 @@ class SolverPardiso(Solver):
|
|
|
568
582
|
|
|
569
583
|
def __init__(self):
|
|
570
584
|
super().__init__()
|
|
571
|
-
self.solver: PardisoInterface =
|
|
585
|
+
self.solver: PardisoInterface | None = None
|
|
572
586
|
self.fact_symb: bool = False
|
|
573
587
|
self.A: np.ndarray = None
|
|
574
588
|
self.b: np.ndarray = None
|
|
575
589
|
|
|
590
|
+
def initialize(self) -> None:
|
|
591
|
+
if self.initialized:
|
|
592
|
+
return
|
|
593
|
+
self.solver = PardisoInterface()
|
|
594
|
+
self.initialized = True
|
|
595
|
+
|
|
576
596
|
def solve(self, A, b, precon, reuse_factorization: bool = False, id: int = -1) -> tuple[np.ndarray, SolveReport]:
|
|
577
597
|
logger.info(f'[ID={id}] Calling Pardiso Solver')
|
|
578
598
|
if self.fact_symb is False:
|
|
@@ -594,11 +614,18 @@ class SolverPardiso(Solver):
|
|
|
594
614
|
class CuDSSSolver(Solver):
|
|
595
615
|
real_only = False
|
|
596
616
|
def __init__(self):
|
|
597
|
-
|
|
617
|
+
super().__init__()
|
|
618
|
+
self._cudss: CuDSSInterface | None = None
|
|
598
619
|
self.fact_symb: bool = False
|
|
599
620
|
self.fact_numb: bool = False
|
|
621
|
+
|
|
622
|
+
def initialize(self) -> None:
|
|
623
|
+
if self.initialized:
|
|
624
|
+
return
|
|
625
|
+
self._cudss = CuDSSInterface()
|
|
600
626
|
self._cudss._PRES = 2
|
|
601
|
-
|
|
627
|
+
self.initialized = True
|
|
628
|
+
|
|
602
629
|
def reset(self) -> None:
|
|
603
630
|
self.fact_symb = False
|
|
604
631
|
self.fact_numb = False
|
|
@@ -1085,7 +1112,7 @@ class SolveRoutine:
|
|
|
1085
1112
|
np.ndarray: The resultant solution.
|
|
1086
1113
|
"""
|
|
1087
1114
|
solver: Solver = self._get_solver(A, b)
|
|
1088
|
-
|
|
1115
|
+
solver.initialize()
|
|
1089
1116
|
NF = A.shape[0]
|
|
1090
1117
|
NS = solve_ids.shape[0]
|
|
1091
1118
|
|
|
@@ -1178,7 +1205,7 @@ class SolveRoutine:
|
|
|
1178
1205
|
SolveReport: The solution report
|
|
1179
1206
|
"""
|
|
1180
1207
|
solver = self._get_eig_solver_bma(A, B, direct=direct)
|
|
1181
|
-
|
|
1208
|
+
solver.initialize()
|
|
1182
1209
|
NF = A.shape[0]
|
|
1183
1210
|
NS = solve_ids.shape[0]
|
|
1184
1211
|
|