Copyright 2016, Vinothan N. Manoharan, Victoria Hwang, Annie Stephenson
This file is part of the structural-color python package.
This package is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this package. If not, see http://www.gnu.org/licenses/.
Polarization plays a role in several calculations throughout the structural-color package:
**FAQ
Q: If polarization is always calculated in Mie theory, what do we do with it in the calculations where we assume the light is unpolarized?
A: We average the parallel and perpendicular components. This is derived on pg 73 of Bohren and Huffman's $\textit{Absorption and Scattering of Light by Small Particles}$
Q: Is polarization implemented in both the single scattering model and the Monte Carlo model?
A: Currently, polarization is only fully implemented in the Monte Carlo model. However, it is certainly possible to add it to the single scattering model in the future
In [1]:
import numpy as np
import matplotlib.pyplot as plt
import structcol as sc
from structcol import refractive_index as ri
from structcol import montecarlo as mc
from structcol import detector as det
import pymie as pm
from pymie import size_parameter, index_ratio
import seaborn as sns
import time
# For Jupyter notebooks only:
%matplotlib inline
set system parameters
In [2]:
# incident light wavelength
wavelength = sc.Quantity('600 nm')
# sample parameters
radius = sc.Quantity('0.140 um')
volume_fraction = sc.Quantity(0.55, '')
n_imag = 2.1e-4
n_particle = ri.n('polystyrene', wavelength) + n_imag # refractive indices can be specified as pint quantities or
n_matrix = ri.n('vacuum', wavelength) # called from the refractive_index module. n_matrix is the
n_medium = ri.n('vacuum', wavelength) # space within sample. n_medium is outside the sample
n_sample = ri.n_eff(n_particle, # refractive index of sample, calculated using Bruggeman approximation
n_matrix,
volume_fraction)
thickness = sc.Quantity('80 um')
boundary = 'film'
# Monte Carlo parameters
ntrajectories = 300 # number of trajectories
nevents = 300 # number of scattering events in each trajectory
initialize and run trajectories
In [3]:
# Calculate scattering quantities
p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, n_sample,
volume_fraction, wavelength, polarization= True)
# Initialize trajectories
r0, k0, W0, p0 = mc.initialize(nevents, ntrajectories, n_medium, n_sample, boundary, polarization=True)
r0 = sc.Quantity(r0, 'um')
k0 = sc.Quantity(k0, '')
W0 = sc.Quantity(W0, '')
p0 = sc.Quantity(p0,'')
trajectories = mc.Trajectory(r0, k0, W0, p0)
# Sample trajectory angles
sintheta, costheta, sinphi, cosphi, theta, phi= mc.sample_angles(nevents,
ntrajectories,p)
# Sample step sizes
step = mc.sample_step(nevents, ntrajectories, mu_scat)
# Update trajectories based on sampled values
trajectories.scatter(sintheta, costheta, sinphi, cosphi)
trajectories.polarize(theta, phi, sintheta, costheta, sinphi,cosphi,
n_particle, n_sample, radius, wavelength, volume_fraction)
trajectories.move(step)
trajectories.absorb(mu_abs, step)
calculate reflectance
In [4]:
reflectance, transmittance = det.calc_refl_trans(trajectories, thickness, n_medium, n_sample, boundary)
print('Reflectance: ' + str(reflectance))
print('Transmittance: ' + str(transmittance))
set system parameters
In [5]:
# incident light wavelength
wavelengths = sc.Quantity(np.arange(400, 800, 20), 'nm')
# sample parameters
radius = sc.Quantity('0.140 um')
volume_fraction = sc.Quantity(0.55, '')
n_imag = 2.1e-4
n_particle = ri.n('polystyrene', wavelengths) + n_imag*1j # refractive indices can be specified as pint quantities or
n_matrix = ri.n('vacuum', wavelengths) # called from the refractive_index module. n_matrix is the
n_medium = ri.n('vacuum', wavelengths) # space within sample. n_medium is outside the sample
thickness = sc.Quantity('80 um')
z_low = sc.Quantity('0 um')
# Monte Carlo parameters
ntrajectories = 300 # number of trajectories
nevents = 300 # number of scattering events in each trajectory
initialize trajectories, run trajectories, and calculate reflectance for each wavelength
In [6]:
reflectance = np.zeros(wavelengths.size)
pol_refl_x = np.zeros(wavelengths.size)
pol_refl_y = np.zeros(wavelengths.size)
pol_refl_z = np.zeros(wavelengths.size)
for i in range(wavelengths.size):
# print wavelength
print('wavelength: ' + str(wavelengths[i]))
# calculate n_sample
n_sample = ri.n_eff(n_particle[i], n_matrix[i], volume_fraction)
# Calculate scattering quantities
p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle[i], n_sample,
volume_fraction, wavelengths[i], polarization= True)
# Initialize trajectories
r0, k0, W0, p0 = mc.initialize(nevents, ntrajectories, n_medium[i], n_sample, boundary, polarization=True)
r0 = sc.Quantity(r0, 'um')
k0 = sc.Quantity(k0, '')
W0 = sc.Quantity(W0, '')
p0 = sc.Quantity(p0,'')
trajectories = mc.Trajectory(r0, k0, W0, p0)
# Sample trajectory angles
sintheta, costheta, sinphi, cosphi, theta, phi= mc.sample_angles(nevents,
ntrajectories,p)
# Sample step sizes
step = mc.sample_step(nevents, ntrajectories, mu_scat)
# Update trajectories based on sampled values
trajectories.scatter(sintheta, costheta, sinphi, cosphi)
trajectories.polarize(theta, phi, sintheta, costheta, sinphi,cosphi,
n_particle[i], n_sample, radius, wavelengths[i], volume_fraction)
trajectories.move(step)
trajectories.absorb(mu_abs, step)
# calculate reflectance and other values of interest
refl_indices, trans_indices,_,_,_,_,_,_,_,_,_,reflectance[i],_,_,_,_ = det.calc_refl_trans(trajectories,
thickness, n_medium[i], n_sample,
boundary, return_extra = True)
# calculate reflectance contribution from each polarization component
pol_refl_x[i], pol_refl_y[i], pol_refl_z[i] = det.calc_pol_frac(trajectories, refl_indices)
Plot reflectance and polarization spectra
In [7]:
sns.set_style('white')
plt.figure()
plt.plot(wavelengths, reflectance , label = 'total', linewidth = 3.5)
plt.plot(wavelengths, pol_refl_x, label = 'x-polarized', linewidth = 3)
plt.plot(wavelengths, pol_refl_y, label = 'y-polarized', linewidth = 3)
plt.plot(wavelengths, pol_refl_z, label = 'z-polarized', linewidth = 3)
plt.plot(wavelengths, pol_refl_x + pol_refl_y + pol_refl_z, label = 'total polarized', linewidth = 3)
plt.xlim([400,800])
plt.ylim([0,1])
plt.ylabel('Reflectance')
plt.xlabel('Wavelength (nm)')
plt.legend()
Out[7]:
This plot shows the contribution to reflectance from light which each of three possible polarization vectors in the global lab coordinate system. The total reflectance nearly overlaps with the sum of the three polarization compontents. The lack of a perfect overlap is due to any extra reflectance which is calculated outside of the trajectories themselves. This includes fresnel reflections at the initial medium-sample interface and the distribution of trajectories that were stuck inside the sample at the end of the Monte Carlo calculation.
In [ ]: