151 lines
6.0 KiB
Python
151 lines
6.0 KiB
Python
'''
|
|
Auxiliary functions to analyse results obtained using scripts in
|
|
the ../misc directory.
|
|
|
|
This needs to be kept in sync with the scripts and qpms/argproc.py.
|
|
'''
|
|
import math
|
|
import numpy as np
|
|
import qpms
|
|
from qpms.qpms_c import Particle
|
|
from qpms.cytmatrices import CTMatrix, TMatrixGenerator
|
|
from qpms.cybspec import BaseSpec
|
|
from qpms.cymaterials import EpsMu, EpsMuGenerator, LorentzDrudeModel, lorentz_drude
|
|
from qpms.symmetries import point_group_info
|
|
from qpms import FinitePointGroup, ScatteringSystem, eV, hbar
|
|
eh = eV / hbar
|
|
|
|
def cleanarray(a, atol=1e-8, copy=True, fullNaN=True):
|
|
a = np.array(a, copy=copy)
|
|
sieve = abs(a.real) < atol
|
|
a[sieve] = 1j * a[sieve].imag
|
|
sieve = abs(a.imag) < atol
|
|
a[sieve] = a[sieve].real
|
|
if fullNaN:
|
|
if np.all(a == 0):
|
|
a[...] = 0
|
|
return a
|
|
|
|
def nicerot(a, atol=1e-10, copy=True):
|
|
'''
|
|
Gives an array a "nice" phase.
|
|
'''
|
|
a = np.array(a, copy=copy)
|
|
i = np.argmax(abs(a))
|
|
a = a / a[i] * abs(a[i])
|
|
return a
|
|
|
|
def _meta_matspec2emg(matspec):
|
|
if isinstance(matspec, (float, complex)): # number is interpreted as refractive index
|
|
return EpsMuGenerator(EpsMu(matspec**2))
|
|
else:
|
|
return EpsMuGenerator(lorentz_drude[matspec])
|
|
|
|
|
|
class FiniteRectLatAnalysis:
|
|
'''Recreates the scattering system structure from finiterectlat-modes.py or
|
|
finiterectlat-constant-driving.py'''
|
|
def __init__(self, data):
|
|
meta = data['meta'][()]
|
|
scatter = 'symmetry_adapted' in meta.keys()# finiterectlat-constant-driving ; TODO unify
|
|
if scatter:
|
|
#thegroup = meta['symmetry_adapted'] # always D2h, this option does something else!
|
|
thegroup = 'D2h'
|
|
else: # finiterectlat-modes
|
|
thegroup = 'D2h' if meta['D2'] else 'D4h'
|
|
Nx, Ny = meta['size']
|
|
px, py = meta['period']
|
|
orig_x = (np.arange(Nx/2) + (0 if (Nx % 2) else .5)) * px
|
|
orig_y = (np.arange(Ny/2) + (0 if (Ny % 2) else .5)) * py
|
|
orig_xy = np.stack(np.meshgrid(orig_x, orig_y), axis = -1)
|
|
bspec = BaseSpec(lMax = meta['lMax'])
|
|
nelem = len(bspec)
|
|
bg = _meta_matspec2emg(meta['background'])
|
|
fg = _meta_matspec2emg(meta['material'])
|
|
if meta.get('height', None) is None:
|
|
tmgen = TMatrixGenerator.sphere(bg, fg, meta['radius'])
|
|
else:
|
|
tmgen = TMatrixGenerator.cylinder(bg, fg, meta['radius'], meta['height'], lMax_extend=meta['lMax_extend'])
|
|
particles = [Particle(orig_xy[i], tmgen, bspec) for i in np.ndindex(orig_xy.shape[:-1])]
|
|
sym = FinitePointGroup(point_group_info[thegroup])
|
|
omega = data['omega'][()] if scatter else meta['centre'] * eh
|
|
self.ss, self.ssw = ScatteringSystem.create(particles, bg, omega, sym=sym)
|
|
ss1, ssw1 = ScatteringSystem.create([Particle([0,0,0], tmgen, bspec)], bg, omega, sym=sym)
|
|
self.ss1, self.ssw1 = ss1, ssw1
|
|
|
|
# per-particle projectors/transformation to symmetry-adapted bases
|
|
fvcs1 = np.empty((nelem, nelem), dtype=complex)
|
|
y = 0
|
|
iris1 = []
|
|
for iri1 in range(ss1.nirreps):
|
|
for j in range(ss1.saecv_sizes[iri1]):
|
|
pvc1 = np.zeros((ss1.saecv_sizes[iri1],), dtype=complex)
|
|
pvc1[j] = 1
|
|
fvcs1[y] = ss1.unpack_vector(pvc1, iri1)
|
|
fvcs1[y] = cleanarray(nicerot(fvcs1[y], copy=False), copy=False)
|
|
y += 1
|
|
iris1.append(iri1)
|
|
|
|
# Mapping between ss particles and grid positions
|
|
positions = self.ss.positions.astype(np.float32)
|
|
# round to get rid of duplicities due to rounding errors
|
|
positions = positions.round(7-int(math.log(np.amax(abs(positions)),10)))
|
|
xpositions = np.unique(positions[:,0])
|
|
ypositions = np.unique(positions[:,1])
|
|
assert(len(xpositions) == Nx)
|
|
assert(len(ypositions == Ny))
|
|
# particle positions as integer indices
|
|
posmap = np.empty((positions.shape[0],2), dtype=int)
|
|
#invposmap = np.empty((Nx, Ny), dtype=int)
|
|
for i, pos in enumerate(positions):
|
|
posmap[i,0] = np.searchsorted(xpositions, positions[i,0])
|
|
posmap[i,1] = np.searchsorted(ypositions, positions[i,1])
|
|
#invposmap[posmap[i,0], posmap[i, 1]] = i
|
|
|
|
self.meta = meta
|
|
self.npzfile = data
|
|
self.fvcs1 = fvcs1
|
|
self.iris1 = np.array(iris1)
|
|
self.bspec = bspec
|
|
self.nelem = nelem
|
|
self.Nx, self.Ny, self.px, self.py = Nx, Ny, px, py
|
|
self.posmap = posmap
|
|
self.fullvec_poffsets = nelem * np.arange(Nx*Ny)
|
|
self.group = thegroup
|
|
if scatter:
|
|
self.typ = 'scatter'
|
|
self.fvcs1_fromdata = data['fvcs1']
|
|
self.iris1_fromdata = data['iris1']
|
|
self.positions_fromdata = data['positions']
|
|
self.driving_symmetry_adapted = data['symmetry_adapted']
|
|
else: # modes
|
|
self.typ = 'modes'
|
|
self.eigval = data['eigval']
|
|
self.neig = len(self.eigval)
|
|
self.eigvec = data['eigvec']
|
|
self.residuals = data['residuals']
|
|
self.ranktest_SV = data['ranktest_SV']
|
|
self.iri = data['iri'][()]
|
|
|
|
|
|
def fullvec2grid(self, fullvec, swapxy=False):
|
|
arr = np.empty((self.Nx, self.Ny, self.nelem), dtype=complex)
|
|
for pi, offset in enumerate(self.fullvec_poffsets):
|
|
ix, iy = self.posmap[pi]
|
|
arr[ix, iy] = fullvec[offset:offset+self.nelem]
|
|
return np.swapaxes(arr, 0, 1) if swapxy else arr
|
|
|
|
def saecv2grid(self, saecv, swapxy=False):
|
|
fullvec = self.ss.unpack_vector(saecv, iri=self.iri)
|
|
return self.fullvec2grid(fullvec, swapxy)
|
|
|
|
def eigvec_asgrid(self, i, swapxy = False):
|
|
'''
|
|
Returns i-th eigenmode as a (Nx, Ny, nelem)-shaped grid of excitation
|
|
coefficients.
|
|
'''
|
|
if self.iri is not None:
|
|
return self.saecv2grid(self.eigvec[i], swapxy)
|
|
else:
|
|
return self.fullvec2grid(self.eigvec[i], swapxy)
|