<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Fresnel Coefficients and Ellipsometric angles
This notebook explores how light gets reflected from an interface.
Fresnel Coefficients
Let's define the perpendicular and parallel Fresnel coefficients.
Step1:  And now the perpendicular and parallel reflectivities.
Step2:  Now, some imports to be able to plot and handle data
Step3:  To plot the reflectivities as a function of the angle of incidence we need to do some coding!
The starting point will be an interface defined by vacuum and a material with $n=1.5$ and no absorption.
Step4:  There we have the reflectivity. But it would be very interesting if we were able to interact and explore the behavior of the reflectivity given different index of refraction for the two media defining the interface.
We add interaction capability in the following.
Step5:  Ellipsometric Angles
At this point, we can see how the ellipsometric angles behave.
First we define the ellipsometric angles by using the previously defined Fresnel coefficients and then some similar code for plotting and handling the interactivity as for the reflectivity before.
<ASSISTANT_TASK:>
Python Code:
import numpy as np
def perpendicularFresnelCoefficient(n1, n2, theta):
    ci = np.cos(theta*np.pi/180)
    si = np.sin(theta*np.pi/180)
    temp1 = n1*ci
    temp2 = n2*np.sqrt(1-(n1/n2*si)**2)
    num = temp1-temp2
    den = temp1+temp2
    return num/den
def parallelFresnelCoefficient(n1, n2, theta):
    ci = np.cos(theta*np.pi/180)
    si = np.sin(theta*np.pi/180)
    temp1 = n1*np.sqrt(1-(n1/n2*si)**2)
    temp2 = n2*ci
    num = temp1-temp2
    den = temp1+temp2
    return num/den
Explanation: Fresnel Coefficients and Ellipsometric angles
This notebook explores how light gets reflected from an interface.
Fresnel Coefficients
Let's define the perpendicular and parallel Fresnel coefficients.
End of explanation
def perpendicularReflectivity(n1,k1,n2,k2,theta):
    return np.abs(perpendicularFresnelCoefficient(n1+k1*1j, n2+k2*1j, theta))**2
def parallelReflectivity(n1,k1,n2,k2,theta):
    return np.abs(parallelFresnelCoefficient(n1+k1*1j, n2+k2*1j, theta))**2
Explanation: And now the perpendicular and parallel reflectivities.
End of explanation
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
from bokeh.io import push_notebook
from bokeh.models import ColumnDataSource
from ipywidgets import interact
Explanation: Now, some imports to be able to plot and handle data
End of explanation
angle = np.arange(0, 90.5, 0.5)
Rs = perpendicularReflectivity(1, 0, 1.5, 0, angle)
Rp = parallelReflectivity(1, 0, 1.5, 0, angle)
source1 = ColumnDataSource(data=dict(x=angle, y=Rs))
source2 = ColumnDataSource(data=dict(x=angle, y=Rp))
reflectivityPlot = figure(title="Reflectivity", plot_height=300, plot_width=600, y_range=(0, 1), x_range=(0,90))
reflectivityPlot.line(angle, Rs, color="#2222aa", alpha=0.5, line_width=2, source=source1, name="1")
reflectivityPlot.line(angle, Rp, color="#222222", alpha=0.5, line_width=2, source=source2, name="2")
show(reflectivityPlot, notebook_handle=True)
Explanation: To plot the reflectivities as a function of the angle of incidence we need to do some coding!
The starting point will be an interface defined by vacuum and a material with $n=1.5$ and no absorption.
End of explanation
def updateReflectivityPlot(n1=1, k1=0, n2=1.5, k2=0):
    source1.data['y'] = perpendicularReflectivity(n1, k1, n2, k2, angle)
    source2.data['y'] = parallelReflectivity(n1, k1, n2, k2, angle)
    push_notebook()
    
interact(updateReflectivityPlot, n1=(1, 5, 0.05), k1=(0, 2, 0.01), n2=(1, 5, 0.05), k2=(0, 2, 0.01))
Explanation: There we have the reflectivity. But it would be very interesting if we were able to interact and explore the behavior of the reflectivity given different index of refraction for the two media defining the interface.
We add interaction capability in the following.
End of explanation
def ellipsometricAngles(n1, n2, angle):
    rp = parallelFresnelCoefficient(n1, n2, angle)
    rs = perpendicularFresnelCoefficient(n1, n2, angle)
    psi = np.arctan(np.abs(rp)/np.abs(rs))*180/np.pi
    delta = np.angle(rp, deg=True)-np.angle(rs, deg=True)
    
    return psi, delta
psi, delta = ellipsometricAngles(1, 1.5, angle)
source1 = ColumnDataSource(data=dict(x=angle, y=psi))
#source2 = ColumnDataSource(data=dict(x=angle, y=delta))
ellipsometryPlot = figure(title="Psi and Delta", plot_height=300, plot_width=600, y_range=(0, 50), x_range=(0,90))
ellipsometryPlot.line(angle, psi, color="#2222aa", alpha=0.5, line_width=2, source=source1, name="1")
#ellipsometryPlot.line(angle, delta, color="#222222", alpha=0.5, line_width=2, source=source1, name="2")
def updateEllipsometryPlot(n1=1, k1=0, n2=1.5, k2=0):
    source1.data['y'] = ellipsometricAngles(n1+1j*k1, n2+1j*k2, angle)[0]
    #source2.data['y'] = ellipsometricAngles(n1+1j*k1, n2+1j*k2, angle)[1]
    push_notebook()
    
show(ellipsometryPlot, notebook_handle=True)
interact(updateEllipsometryPlot, n1=(1,5, 0.05), k1=(0, 2, 0.01), n2=(1,5, 0.05), k2=(0, 2, 0.01))
Explanation: Ellipsometric Angles
At this point, we can see how the ellipsometric angles behave.
First we define the ellipsometric angles by using the previously defined Fresnel coefficients and then some similar code for plotting and handling the interactivity as for the reflectivity before.
End of explanation
<END_TASK>