Polarization in the circular basis: Poincare sphere

4.2. Polarization in the circular basis: Poincare sphere#

In this notebook we plot the spatial pattern traced out by the tip of the electric field vector for left and right circularly polarized light, plus also a superposition of left and right. The relative amplitude and phase of this left/right superposition is characterised by two angles, \(\theta\) and \(\phi\), which can be represented by a point on the Poincare sphere.

We shall plot the Poincare vector to the point \((\theta,\phi)\) in an inset.

There are many ways in python to make visualisations in 3D. Here we have used pyvista. https://docs.pyvista.org/

As per usual the first cell imports all the packages we need.

CLICK HERE TO ACTIVATE CODE CELLS
import numpy as np
import matplotlib.pyplot as plt
import pyvista as pv
from io import BytesIO

import matplotlib as mpl
mpl.rcParams['font.family'] = 'serif'
mpl.rc('text', usetex = True)

from mpl_toolkits.axes_grid1.inset_locator import inset_axes

Now we define some functions to plot the electric fields. We use pyvista tube and arrow.

For left circular our \(x\) and \(y\) components are \({\cal E}_0\cos(kz-\omega t)\) and \({\cal E}_0\sin(kz-\omega t)\), respectively. For right circular we change the sign of the \(y\) component.

def left_tube(el, afreq, phi):
    num_pi=32
    zmax = num_pi * np.pi
    t = 0
    npts=200
    z = np.linspace(0, zmax, npts)    
    E0 = -50
    Ex =  E0*el*np.cos(afreq*(z-t)+0*np.pi/4)
    Ey =  E0*el*np.sin(afreq*(z-t)+0*np.pi/4)
    points = np.column_stack((Ex, 1.5*z, Ey))
    spline = pv.Spline(points, 1000)
    spline["scalars"] = np.arange(spline.n_points)
    tube_data = spline.tube(radius=1)
    return tube_data

def right_tube(er,afreq, phi):
    num_pi=32
    zmax = num_pi * np.pi
    t = 0
    npts=200
    z = np.linspace(0, zmax, npts)    
    E0 = -50
    Ex =  E0*er*np.cos(afreq*(z-t)+0*np.pi/4+phi)
    Ey =  -E0*er*np.sin(afreq*(z-t)+0*np.pi/4+phi)
    points = np.column_stack((Ex, 1.5*z, Ey))
    spline = pv.Spline(points, 1000)
    spline["scalars"] = np.arange(spline.n_points)
    tube_data = spline.tube(radius=1)
    return tube_data

def sum_tube(el, er, afreq, phi):
    num_pi=32
    zmax = num_pi * np.pi
    t = 0
    npts=200
    z = np.linspace(0, zmax, npts)    
    envelope=np.exp(-(z-zmax/2-t)**2/600)
    E0 = -50
    Exl =  E0*el*np.cos(afreq*(z-t))
    Eyl =  E0*el*np.sin(afreq*(z-t))   
    Exr =  E0*er*np.cos(afreq*(z-t)+0*np.pi/4+phi)
    Eyr =  -E0*er*np.sin(afreq*(z-t)+0*np.pi/4+phi)

    norm = np.sqrt(el**2+er**2)
    Ex = (Exl + Exr)/norm
    Ey = (Eyl + Eyr)/norm
    points = np.column_stack((Ex, 1.5 * z , Ey))
    spline = pv.Spline(points, 1000)
    spline["scalars"] = np.arange(spline.n_points)
    tube_data = spline.tube(radius=2)
    return tube_data

def E_left_arrow(el,afreq, phi):
    num_pi=32
    zmax = num_pi * np.pi
    t = 0
    E_arrow_size=50*el
    E_arrow_data = pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([-np.cos(-afreq*t),0,-np.sin(-afreq*t)]),# * self.r,
                       tip_length=20/E_arrow_size, tip_radius=4/E_arrow_size, tip_resolution=20,
                       shaft_radius=2/E_arrow_size, shaft_resolution=20, scale=E_arrow_size)
    return E_arrow_data

def E_right_arrow(er,afreq, phi):
    num_pi=32
    zmax = num_pi * np.pi
    t = 0
    E_arrow_size=50*er
    E_arrow_data = pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([-np.cos(-afreq*t+phi+0*np.pi/4),0,np.sin(-afreq*t+phi+0*np.pi/4)]),# * self.r,
                       tip_length=20/E_arrow_size, tip_radius=4/E_arrow_size, tip_resolution=20,
                       shaft_radius=2/E_arrow_size, shaft_resolution=20, scale=E_arrow_size)
    return E_arrow_data

These two functions create all the data for the polarization plot and the Poincare sphere.

def PNG_data(Theta,Phi):
    #    p=pv.Plotter(shape=(1, 1),multi_samples=1,window_size=(800,800),off_screen=True,notebook=False)
    afreq = 0.2
    relative_phase = Phi - 0 *np.pi

    el= np.cos(Theta/2)
    er = np.sin(Theta/2)
    p.add_mesh(left_tube(el,afreq, relative_phase), smooth_shading=True, color='blue', opacity=0.25, show_scalar_bar=False)
    p.add_mesh(right_tube(er,afreq, relative_phase), smooth_shading=True, color='red', opacity=0.25, show_scalar_bar=False)
    p.add_mesh(sum_tube(el,er,afreq, relative_phase), smooth_shading=True, color='green', opacity=0.5, show_scalar_bar=False)

    z_arrow_size=175
    z_arrow=pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([0,1,0]),# * self.r,
                           tip_length=10/z_arrow_size, tip_radius=2/z_arrow_size, tip_resolution=40,
                           shaft_radius=1/z_arrow_size, shaft_resolution=40, scale=z_arrow_size)
    p.add_mesh(z_arrow, opacity=1.0, color='black', smooth_shading=True)

    x_arrow_size= 100
    x_arrow=pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([-1,0,0]),# * self.r,
                           tip_length=10/x_arrow_size, tip_radius=2/x_arrow_size, tip_resolution=40,
                           shaft_radius=1/x_arrow_size, shaft_resolution=20, scale=x_arrow_size)
    p.add_mesh(x_arrow, opacity=1.0, color='black', smooth_shading=True)

    y_arrow_size= 100
    y_arrow=pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([0,0,1]),# * self.r,
                           tip_length=10/y_arrow_size, tip_radius=2/y_arrow_size, tip_resolution=40,
                           shaft_radius=1/y_arrow_size, shaft_resolution=20, scale=y_arrow_size)
    p.add_mesh(y_arrow, opacity=1.0, color='black', smooth_shading=True)

    p.add_mesh(E_left_arrow(el,afreq, relative_phase), opacity=1.0, color='blue', smooth_shading=True)
    p.add_mesh(E_right_arrow(er,afreq, relative_phase), opacity=1.0, color='red', smooth_shading=True)

#    p.enable_depth_peeling(10)
    p.camera_position = [(420, 300, 40),(35, 90, 30.0),(0.0, 0.0, 1.0)] 
    p.set_background("white", top="white")
    png_output = BytesIO()    
    return png_output

def BS_data(Theta,Phi):
    u = np.sin(Theta) * np.cos(Phi)
    v = np.sin(Theta) * np.sin(Phi)
    w = np.cos(Theta)
    
    num = 50
    theta = np.linspace(-1 * np.pi, 1 * np.pi, num)
    r = 3
    phi = 0 * np.pi / 60

    z = 0 * r * np.cos(theta)
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    rpts = np.column_stack((x, y, z))
    spline = pv.Spline(rpts, 1000)
    rxy_tube = spline.tube(radius=0.03)

    z = r * np.cos(theta)
    x = r * np.sin(theta) * np.cos(phi - np.pi / 2)
    y = r * np.sin(theta) * np.sin(phi - np.pi / 2)
    rpts = np.column_stack((x, y, z))
    spline = pv.Spline(rpts, 1000)
    rxz_tube = spline.tube(radius=0.03)

    z = r * np.cos(theta)
    x = r * np.sin(theta) * np.cos(phi)
    y = r * np.sin(theta) * np.sin(phi)
    rpts = np.column_stack((x, y, z))
    spline = pv.Spline(rpts, 1000)
    ryz_tube = spline.tube(radius=0.03)

    # add sphere
    big = pv.Sphere(center=(0, 0, 0), radius=r)
    p.add_mesh(big, opacity=0.4,
                    color="w", specular=0.85, smooth_shading=True)

    # add cross-sections
    p.add_mesh(rxy_tube, opacity=0.1, smooth_shading=True, color=cDUkk)
    p.add_mesh(rxz_tube, opacity=0.1, smooth_shading=True, color=cDUkk)
    p.add_mesh(ryz_tube, opacity=0.1, smooth_shading=True, color=cDUkk)
    
    p.set_background(cDUsky, top="white")
    p.camera_position = [(14, 4.0, 4.0),
                            (0.0, 0.0, 0.0),
                            (0.0, 0.0, 1)]
    
    addAxisArrow(1,0,0,cDUo)
    addAxisArrow(0,1,0,cDUy)
    addAxisArrow(0,0,1,cDUb)
    addStateArrow(u,v,w)
    png_output = BytesIO()
    return png_output

def addAxisArrow(x,y,z,color):
    r=3
    length = 1.25*np.sqrt(x*x + y*y + z*z)
    arrow=pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([x,y,z]) * r,
                   tip_length=0.15/length, tip_radius=0.05/length, tip_resolution=20,
                   shaft_radius=0.025/length, shaft_resolution=20, scale=length * r)
    p.add_mesh(arrow, opacity=1.0, color=color, smooth_shading=True)

def addStateArrow(x,y,z):
    r=3
    length = 1.0*np.sqrt(x*x + y*y + z*z)
    arrow=pv.Arrow(start=(0.0, 0.0, 0.0), direction=np.array([x,y,z]) * r,
                   tip_length=0.2, tip_radius=0.1, tip_resolution=20,
                   shaft_radius=0.05, shaft_resolution=20, scale=length * r)
    p.add_mesh(arrow, opacity=1.0, color='Green', smooth_shading=True)

We define some colours to use.

#: Durham colour scheme
cDUp = "#7E317B"  # Palatinate Purple
cDUpp =  "#D8ACF4"  # Light purple

cDUb = "#006388"  # Blue
cDUbb = "#91B8BD"  # Mid Blue
cDUbbb = "#C4E5FA"  # Light Blue
cDUbbbb = "#00AEEF"

cDUsky = "#A5C8D0"  # sky blue

cDUo = "#9FA161"  # Olive Green

cDUr = "#AA2B4A"  # Red
cDUrr = "#BE1E2D"
cDUy = "#E8E391" #  Yellow

cDUp = "#C43B8E" # Pink

cDUk = "#231F20"  # Black
cDUkk = "#002A41" # ink

cDUggg = "#CFDAD1"  # Near White/L. Grey
cDUgg = "#968E85"  # Warm Grey
cDUg = "#6E6464"  # midgrey

Finally we make a plot. The key input parameters are Theta and Phi. You should check that left (blue) and right (red) have the correct handedness, and play around with different values of Theta and Phi to generate interesting superpositions. What values are needed to create linearly polarized light aligned on the \(+45\) degree axis?

eps = 1e-8

Theta = 3.* np.pi/4 + eps
Phi = 1 * np.pi/4

fig, (ax1) = plt.subplots(1,1,figsize=(8, 8))

p=pv.Plotter(shape=(1, 1),multi_samples=1,window_size=(1600,1600),off_screen=True,notebook=False)

png_output1 = PNG_data(Theta,Phi)
p.show(screenshot=png_output1)
ax1.imshow(p.image)
ax1.text(470,440,'$y$', fontsize = 24)
ax1.text(1380,960,'$z$', fontsize = 24)
ax1.text(275,200,'$(i)$', fontsize = 24)
ax1.set_axis_off()
ax1ins = inset_axes(ax1,width="50%", height="75%",
                   bbox_to_anchor=(.525, .485, .725, .725),
                   bbox_transform=ax1.transAxes, loc=3)

p=pv.Plotter(shape=(1, 1),multi_samples=1,window_size=(600,600),off_screen=True,notebook=False)

num = 50
theta = np.linspace(0, Theta, num)
r = 3
z = r * np.cos(theta)
x = r * np.sin(theta) * np.cos(Phi)
y = r * np.sin(theta) * np.sin(Phi)
rpts = np.column_stack((x, y, z))
spline = pv.Spline(rpts, 1000)
theta_tube = spline.tube(radius=0.05)
p.add_mesh(theta_tube, opacity=1.0, smooth_shading=True, color=cDUb)

theta = np.linspace(Theta, np.pi/2, num)
r = 3
z = r * np.cos(theta)
x = r * np.sin(theta) * np.cos(Phi)
y = r * np.sin(theta) * np.sin(Phi)
rpts = np.column_stack((x, y, z))
spline = pv.Spline(rpts, 1000)
theta_tube = spline.tube(radius=0.05)
p.add_mesh(theta_tube, opacity=0.1, smooth_shading=True, color=cDUk)

r = np.linspace(0, 3, num)
theta = np.pi/2
z = r * np.cos(theta)
x = r * np.sin(theta) * np.cos(Phi)
y = r * np.sin(theta) * np.sin(Phi)
rpts = np.column_stack((x, y, z))
spline = pv.Spline(rpts, 1000)
theta_tube = spline.tube(radius=0.05)
p.add_mesh(theta_tube, opacity=0.1, smooth_shading=True, color=cDUk)


phi = np.linspace(0, Phi, num)
r = 3
theta = np.pi/2
z = 0 * r * np.cos(phi)
x = r * np.sin(theta) * np.cos(phi)
y = r * np.sin(theta) * np.sin(phi)
rpts = np.column_stack((x, y, z))
spline = pv.Spline(rpts, 1000)
phi_tube = spline.tube(radius=0.05)
p.add_mesh(phi_tube, opacity=1.0, smooth_shading=True, color=cDUo)

png_output2 = BS_data(Theta,Phi)
p.show(screenshot=png_output2)

ax1ins.imshow(p.image)
#ax1ins.text(500,400,'$y$', fontsize = 32)
#ax1ins.text(1400,960,'$z$', fontsize = 32)
#ax1ins.text(100,400,'$(ii)$', fontsize = 32)
ax1ins.set_axis_off()
_images/c603e917820e64349899f9f675f80a896582c18de5cde4ef55d649e7e16ecf2f.png
fig.savefig('Pol_Poincare_test.png', dpi=600, facecolor='white', edgecolor='white')