Polarization¶
In this notebook we'll be explaining the basic ideas behind the implementation of polarization in Chromatix. Currently, we implement polarization through Jones calculus, so we can only deal with fully polarized light. We're looking at adding Mueller calculus and more general birefringence.
In Chromatix, we have two types of Fields: Scalar and Vector. You can use the former for all your standard scalar diffraction, and the latter for all simulations requiring vectorial calculations. Putting this succintly:
- If you don't care about polarization, use
ScalarFieldorChromaticScalarField. - If you need polarization, use
VectorFieldorChromaticVectorField.
There's two main differences between these fields:
- The
ScalarFieldhas shape(... height width), whereas theVectorFieldhas shape(... height width 3). Correspondingly, theChromaticScalarFieldhas shape(... height width wavelengths)and theChromaticVectorFieldhas shape(... height width wavelengths 3). As you might guess from this, the 3 components of the electric field are thus placed at the last dimension. This makes the last dimension of theScalarFieldrather useless, but having it significantly simplifies implementation, as every field type has the same rank. - The
Vectorfields have thejones_vectorproperty implemented, which returns the Jones vector at every point in the field.
We've implemented all basic retarders and attenuators such as linear (circular) polarizers, and quarter (half) waveplates. For a full list, please see the API, but we'll go through a few below.
Ordering of the components Note that most Jones vector and matrix implementations have rank 2, but we use rank 3! As we implement propagation through 3D birefringent samples, we actually need the third component. The order of the components is [z, y, x], where z is the propagation direction, so for now this will always be zero. The API we expose for all the Jones calculus automatically takes care of most of this, though.
Working with polarized fields¶
All sources can be used to create a polarized field by setting scalar=False and specifying a vector of length 3 as the amplitude in the z, y, and x components respectively:
import jax.numpy as jnp
import chromatix.functional as cf
field = cf.plane_wave(
(512, 512),
1.0,
0.532,
amplitude=cf.linear(1 / 2 * jnp.pi),
scalar=False,
)
Next to cf.linear, we've implemented other commonly used initialisers, but you can always give in your own vector - cf.linear() just returns a 3-vector (the Jones vector!):
print(cf.linear(1 / 2 * jnp.pi))
[ 0.000000e+00+0.j 1.000000e+00+0.j -4.371139e-08+0.j]
We can easily pass this field through a linear polarizer:
field = cf.linear_polarizer(field, angle=1 / 4 * jnp.pi)
And we can check our results using Malus law - the power in the field should be: $$ \cos(1/2\cdot \pi - 1/4\cdot\pi)^2 =\cos(1/2 \pi)^2 = 1/2 $$
print(field.power.squeeze())
0.5
As stated before, we implement all basic and often-used elements (circular, linear, half-wave plates, etc), but all these are wrappers to a single function: polarizer. You can simply call polarizer directly with the Jones matrix of your choice:
field = cf.polarizer(
field, J00=1, J01=0, J10=-1j, J11=1j
) # note that J00 follows standard Jones calculus ordering.