Hi,
I am trying to check some optical plots in the following paper:
https://www.nature.com/articles/s41598-020-68704-w
Here is a smaller/quicker-to-simulate version of the problem. I am
interested in silicon at long wavelength (up to 1.2 μm), and have fit
Lorentzians to Schinke et al data to get the following:
eps01=1.000000000000757e+00
cSi_sig1=1.742452232381217e+00
cSi_frq1=2.268393842625499e-03
cSi_gam1=1.000000000000000e-06
cSi_sig2=4.413478307846660e+00
cSi_frq2=5.563486517688901e-03
cSi_gam2=1.039533704134854e-05
cSi_sig3=4.346562778556655e-04
cSi_frq3=1.015803616840379e-03
cSi_gam3=4.436686697711304e-05
cSi_sig4=2.667407380976183e+00
cSi_frq4=3.019419346733225e-03
cSi_gam4=7.381034557205803e-06
cSi_sig5=1.874745406362289e+00
cSi_frq5=2.999546106465388e-03
cSi_gam5=2.153817716389790e-06
cSi_sig6=-4.082635925775152e-02
cSi_frq6=2.261035517057193e-03
cSi_gam6=5.061060071803692e-04
cSi_susc = [mp.LorentzianSusceptibility(frequency=cSi_frq1*1000,
gamma=cSi_gam1*2000, sigma=cSi_sig1),
mp.LorentzianSusceptibility(frequency=cSi_frq2*1000,
gamma=cSi_gam2*2000, sigma=cSi_sig2),
mp.LorentzianSusceptibility(frequency=cSi_frq3*1000,
gamma=cSi_gam3*2000, sigma=cSi_sig3),
mp.LorentzianSusceptibility(frequency=cSi_frq4*1000,
gamma=cSi_gam4*2000, sigma=cSi_sig4),
mp.LorentzianSusceptibility(frequency=cSi_frq5*1000,
gamma=cSi_gam5*2000, sigma=cSi_sig5),
mp.LorentzianSusceptibility(frequency=cSi_frq6*1000,
gamma=cSi_gam6*2000, sigma=cSi_sig6)]
cSi = mp.Medium(epsilon=eps01, E_susceptibilities=cSi_susc,
valid_freq_range=cSi_range)
I am interested in the absorption of an inverted pyramid photonic
crystal structure in the 1-1.2 μm wavelength range. If I start with just
a silicon slab:
zSi = dpml+2*monpad+dsi/2
siFilm = mp.Block(size=mp.Vector3(sx,sy,dsi),
center=mp.Vector3(0,0,zSi),
material=cSi)
geometry = [siFilm]
I get that my code converges and the absorption looks basically how I
expect it to. If I add a pyramid of air using the sidewall_angle argument:
zPyrAir = zSi + dsi/2
vertices = [mp.Vector3(sx*0.5,sy*0.5,zPyrAir),
mp.Vector3(sx*0.5,sy*-0.5,zPyrAir),
mp.Vector3(sx*-0.5,sy*-0.5,zPyrAir),
mp.Vector3(sx*-0.5,sy*0.5,zPyrAir)]
pyr_air = mp.Prism(vertices,
# remove small number so that vertices at top
don't overlap (otherwise crashes)
height=pyr_height_h-1e-10,
axis=mp.Vector3(0,0,-1),
sidewall_angle=-t_angle*pi/180,
material=mp.Medium(epsilon=1))
geometry = [siFilm, pyr_air]
I get that the fields diverge (albeit very slowly), at least so it
seems. Boundary conditions are k_point=Vector3(0,0,0) with PML in the Z
direction. My guess has been that the pyramid reflects light such that
it hits the PML at a glancing angle and this gives reflective artefacts.
I've hence played with PML parameters (thickness, profile, R_asymptotic)
and the distance between sources, monitors and structure, but so far
haven't gotten anything to work. Is perhaps the absorption too small for
MEEP to handle (i.e. leading to significant roundoff errors)? Is there a
workaround? What are some typical values to try if the defaults do not work?
I've attached code as well which contains all the details.
For what it's worth, I also find that with this structure I can only add
one symmetry (as discussed earlier on this mailing list), although my
results are slightly different (<1%) and the run time improvement is not
substantial.
Philip
"""
..moduleauthor:: jph
..date:: 2020-07-28
One minimal example to test MEEP long wavelength absorption and symmetry:
1 micron silicon slab. I am mostly checking for convergence, not necessarily
correctness
Sent version
"""
import sys
import meep as mp
from math import pi, tan, sin
import numpy as np
from matplotlib import pyplot as plt
cSi_range = mp.FreqRange(min=1/1.2, max=1/1.)
eps01=1.000000000000757e+00
cSi_sig1=1.742452232381217e+00
cSi_frq1=2.268393842625499e-03
cSi_gam1=1.000000000000000e-06
cSi_sig2=4.413478307846660e+00
cSi_frq2=5.563486517688901e-03
cSi_gam2=1.039533704134854e-05
cSi_sig3=4.346562778556655e-04
cSi_frq3=1.015803616840379e-03
cSi_gam3=4.436686697711304e-05
cSi_sig4=2.667407380976183e+00
cSi_frq4=3.019419346733225e-03
cSi_gam4=7.381034557205803e-06
cSi_sig5=1.874745406362289e+00
cSi_frq5=2.999546106465388e-03
cSi_gam5=2.153817716389790e-06
cSi_sig6=-4.082635925775152e-02
cSi_frq6=2.261035517057193e-03
cSi_gam6=5.061060071803692e-04
cSi_susc = [mp.LorentzianSusceptibility(frequency=cSi_frq1*1000, gamma=cSi_gam1*2000, sigma=cSi_sig1),
mp.LorentzianSusceptibility(frequency=cSi_frq2*1000, gamma=cSi_gam2*2000, sigma=cSi_sig2),
mp.LorentzianSusceptibility(frequency=cSi_frq3*1000, gamma=cSi_gam3*2000, sigma=cSi_sig3),
mp.LorentzianSusceptibility(frequency=cSi_frq4*1000, gamma=cSi_gam4*2000, sigma=cSi_sig4),
mp.LorentzianSusceptibility(frequency=cSi_frq5*1000, gamma=cSi_gam5*2000, sigma=cSi_sig5),
mp.LorentzianSusceptibility(frequency=cSi_frq6*1000, gamma=cSi_gam6*2000, sigma=cSi_sig6)]
cSi = mp.Medium(epsilon=eps01, E_susceptibilities=cSi_susc, valid_freq_range=cSi_range)
if __name__ == '__main__':
### INPUT PARAMETERS ###
is_pyr = int(sys.argv[1])
wvl_min = 1.0
wvl_max = 1.2
max_time = 2000
decay_factor = 1e-5
if is_pyr:
resolution = 50
outdir = 'test_simple_pyr'
else:
resolution = 50
outdir = 'test_slab'
dpml = 2.0
# PYRAMID #
pyr_base_L = 1
# pyr_height_h = 1
t_angle = 35.3
tilt_angle = 90-t_angle
pyr_height_h = tan(tilt_angle*pi/180)*pyr_base_L/2
dsi = 1 # 1 micron thick silicon
### DERIVED PARAMETERS ###
nfreq = int(1000*(wvl_max-wvl_min)+1) # number of frequency samples
sourcepad = wvl_max
monpad = wvl_max
freq_min = 1/wvl_max
freq_max = 1/wvl_min
freq_centre = (freq_max+freq_min)/2
freq_width = freq_max-freq_min
### DOMAIN ###
sx = pyr_base_L
sy = pyr_base_L
sz = 2*dpml + 2*sourcepad + monpad + dsi + 2*monpad
domain = mp.Vector3(sx, sy, sz)
### SOURCE DEFINITION ###
sources = [mp.Source(mp.GaussianSource(frequency=freq_centre, width=freq_width),
component=mp.Ex,
center=mp.Vector3(0,0,sz-dpml-sourcepad),
size=mp.Vector3(sx,sy,0))]
### SIMULATION PARAMETERS ###
k = mp.Vector3(0,0,0)
# it seems symmetry works only to an approximation
# and can only have one of them with PBCs
symm = [] # [mp.Mirror(mp.Y, phase=1)] #, mp.Mirror(mp.X, phase=-1)] # right now, no symmetries used
pml_layers = [mp.PML(dpml, direction=mp.Z, side=mp.ALL)]
### NORMALISATION SIM ###
sim = mp.Simulation(cell_size=domain,
boundary_layers=pml_layers,
geometry=[],
resolution=resolution,
k_point=k,
geometry_center=mp.Vector3(0,0,+0.5*sz),
symmetries=symm,
sources=sources)
### DEFINE FLUX REGIONS ###
refl_flux_region = mp.FluxRegion(center=mp.Vector3(0,0,sz-dpml-sourcepad-monpad),
size=mp.Vector3(sx,sy,0))
refl = sim.add_flux(freq_centre, freq_width, nfreq, refl_flux_region)
tran_flux_region = mp.FluxRegion(center=mp.Vector3(0,0,dpml+monpad),
size=mp.Vector3(sx,sy,0))
tran = sim.add_flux(freq_centre, freq_width, nfreq, tran_flux_region)
### RUN NORM SIMULATION ###
checkpt = mp.Vector3(0,0,dpml+monpad) # check at centre of tran flux
sim.run(until_after_sources=mp.stop_when_fields_decayed(25, mp.Ex, checkpt, 1e-10))
### GET FLUXES FOR SIMULATION ###
refl_flux = mp.get_fluxes(refl)
tran_flux = mp.get_fluxes(tran)
flux_freq = mp.get_flux_freqs(refl)
refl_norm_data = sim.get_flux_data(refl)
tran_norm_flux = mp.get_fluxes(tran)
### DEFINE GEOMETRY FOR SIMULATION ###
sim.reset_meep() # first must reset
zSi = dpml+2*monpad+dsi/2
siFilm = mp.Block(size=mp.Vector3(sx,sy,dsi),
center=mp.Vector3(0,0,zSi),
material=cSi)
if is_pyr:
zPyrAir = zSi + dsi/2
vertices = [mp.Vector3(sx*0.5,sy*0.5,zPyrAir),
mp.Vector3(sx*0.5,sy*-0.5,zPyrAir),
mp.Vector3(sx*-0.5,sy*-0.5,zPyrAir),
mp.Vector3(sx*-0.5,sy*0.5,zPyrAir)]
pyr_air = mp.Prism(vertices,
# remove small number so that vertices at top don't overlap (otherwise crashes!!)
height=pyr_height_h-1e-10,
axis=mp.Vector3(0,0,-1),
sidewall_angle=-t_angle*pi/180,
material=mp.Medium(epsilon=1))
geometry = [siFilm, pyr_air]
else:
geometry = [siFilm]
### DEFINE SIMULATION ###
sim = mp.Simulation(cell_size=domain,
boundary_layers=pml_layers,
geometry=geometry,
resolution=resolution,
k_point=k,
geometry_center=mp.Vector3(0,0,+0.5*sz),
symmetries=symm,
sources=sources)
### DEFINE FLUXES ###
refl_flux_region = mp.FluxRegion(center=mp.Vector3(0,0,sz-dpml-sourcepad-monpad),
size=mp.Vector3(sx,sy,0))
refl = sim.add_flux(freq_centre, freq_width, nfreq, refl_flux_region)
tran_flux_region = mp.FluxRegion(center=mp.Vector3(0,0,dpml+monpad),
size=mp.Vector3(sx,sy,0))
tran = sim.add_flux(freq_centre, freq_width, nfreq, tran_flux_region)
sim.load_minus_flux_data(refl, refl_norm_data)
### NAIVE VISUALISATION ###
plt.figure(dpi=150)
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,+0.5*sz), size=mp.Vector3(sx,sy,0)), eps_parameters={'cmap':'Set2','interpolation':'none'})
plt.savefig(outdir+'/pyrxy.png')
plt.close()
plt.figure(dpi=150)
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,+0.5*sz), size=mp.Vector3(sx,0,sz)), eps_parameters={'cmap':'Set2','interpolation':'none'})
plt.savefig(outdir+'/pyrxz.png')
plt.close()
plt.figure(dpi=150)
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,+0.5*sz), size=mp.Vector3(0,sy,sz)), eps_parameters={'cmap':'Set2','interpolation':'none'})
plt.savefig(outdir+'/pyryz.png')
plt.close()
### RUN SIMULATION ###
# for now, checkpt is same
sim.run(until_after_sources=[max_time, mp.stop_when_fields_decayed(25, mp.Ex, checkpt, decay_factor)])
### SAVE DATA ###
refl_flux = mp.get_fluxes(refl)
tran_flux = mp.get_fluxes(tran)
flux_freq = mp.get_flux_freqs(refl)
wl=np.array([])
Rs=np.array([])
Ts=np.array([])
for i in range(nfreq):
wl = np.append(wl, 1/flux_freq[i])
Rs = np.append(Rs,-refl_flux[i]/tran_norm_flux[i])
Ts = np.append(Ts,tran_flux[i]/tran_norm_flux[i])
print('saving output csv files...')
print(outdir+"/wl_pyr.csv")
np.savetxt(outdir+"/wl_pyr.csv", wl)
np.savetxt(outdir+"/rs_pyr.csv", Rs)
np.savetxt(outdir+"/ts_pyr.csv", Ts)
print('files saved')
_______________________________________________
meep-discuss mailing list
[email protected]
http://ab-initio.mit.edu/cgi-bin/mailman/listinfo/meep-discuss