Creating a multilayer stack¶
A multilayer stack and by extension a layer is defined for given incident conditions, which are:
the wavelength of light , in nanometers
the angle of incidence upon the stack defined in the entry isotropic semi-infinite medium in the plane, in degrees or in radians
the associated wavevector :
- is the normalised wavevector ()
- is the -component of the normalised wavevector that stays constant throughout the stack ( where is the refractive index of the entry isotropic half-space)
- is its -component (that equals to 0 by construction)
- is its -component ( in the entry semi-infinite isotropic medium)
There are three ways of creating a multilayer stack with PyLlama
:
- creating a multilayer stack from scratch with the classes
Structure
andLayer
, and working directly with classes that handle the optical calculations - creating multilayer stacks with the abstract class
Model
and its children, and writing one’s own child class when a different kind of stack is needed - using the class
Spectrum
that provides a higher level of automation to automatically calculate reflection and transmission across a range of wavelengths
This section explains how to create a multilayer stack and the section Calculating the reflection and transmission spectra of a stack explains how to calculate their reflectance.
From scratch: the technical way¶
A multilayer stack consists in a series of layers sandwiched between an entry and an exit semi-infinite isotropic media. A layer is represented by the class Layer
and the semi-infinite isotropic media are represented by the class HalfSpace
.
The entry and exit semi-infinite isotropic media are instances of the class HalfSpace
and are created with:
k0 = 2 * numpy.pi / wl_nm
Kx = n_entry * numpy.sin(theta_in_rad)
Kz_entry = n_entry * numpy.cos(theta_in_rad)
theta_out_rad = numpy.arcsin((n_entry / n_exit) * numpy.sin(self.theta_in_rad))
Kz_exit = n_exit * numpy.cos(theta_out_rad)
epsilon_entry = numpy.array([[n_entry ** 2, 0, 0],
[0, n_entry ** 2, 0],
[0, 0, n_entry ** 2]])
epsilon_exit = numpy.array([[n_exit ** 2, 0, 0],
[0, n_exit ** 2, 0],
[0, 0, n_exit ** 2]])
entry = HalfSpace(epsilon_entry, Kx, Kz_entry, k0)
exit = HalfSpace(epsilon_exit, Kx, Kz_exit, k0)
where:
n_entry
is the refractive index of the entry isotropic mediumn_exit
is the refractive index of the exit isotropic mediumtheta_in_rad
is the angle of incidence in the entry isotropic medium in radianswl_nm
is the wavelength of light, in nanometersk0
is the wavevector magnitudeKx
is the -component of the normalised wavevector that stays constant through the stackKz_entry
andKz_exit
are the -components of the normalised wavevector in the entry and exit isotropic half-spacesepsilon_entry
andepsilon_exit
are the permittivity tensors of the entry and exit isotropic half-spaces
A multilayer stack whose layers are embedded between the semi-infinite isotropic media entry
and exit
is then created with:
Ky = 0
Kz = n_entry * numpy.cos(theta_in_rad)
my_stack_structure = Structure(entry, exit, Kx, Ky, Kz_entry, Kz_exit, k0)
where:
Ky
is the -component of the normalised wavevector that is equal to 0 throughout the stack
At this point, my_stack_structure
represents two semi-infinite isotropic half-space that sandwich no layer. Layers are instances of the class Layer
and are created with:
my_layer = Layer(epsilon, thickness_nm, Kx, k0)
where:
k0
is the normalised wavevectorKx
is the -component of the normalised wavevectorepsilon
is the permittivity tensor (3x3 Numpy array) of the layer, which can represent a material that is isotropic or anisotropic, absorbing or non-absorbingthickness_nm
is the thickness of the layer in nanometers
The -component of the normalised wavevector changes inside the stack and is not defined in the Layer
: the partial waves will be calculated instead.
Then, the layer can be added to the stack with:
my_stack_structure.add_layer(my_layer)
The content of a stack can then be accessed with:
my_stack_structure.entry # access the entry HalfSpace
my_stack_structure.exit # access the exit HalfSpace
my_stack_structure.layers # access the list of Layers in the stack
The functions add_layers()
, remove_layer()
and replace_layer()
also enable the user to construct the stacks that they want.
Note
and stay constant throughout the stack and checks are carried out in the function Structure.add_layer()
to ensure that the user only adds Layers
that are compatible with the stack. The user should not add Layers
with my_stack.layers.append(my_layer)
as this may lead to impossible situations.
Lastly, the periodicity of the stack can be changed with:
my_stack_structure.N_periods = number_of_periods
The Layers
in the list my_stack.layers
represent one periodic pattern that is repeated number_of_periods
times in the stack. A multilayer stack made of N
repetitions of a periodic unit consisting in layer_a
with a permittivity eps_a
and a thickness thick_a
(in nanometers) and layer_b
with a permittivity eps_b
and a thickness thick_b
(in nanometers) is defined with:
my_stack_structure = Structure(entry, exit, Kx, Ky, Kz_entry, Kz_exit, k0)
layer_a = Layer(eps_a, thick_a, Kx, k0)
layer_b = Layer(eps_b, thick_b, Kx, k0)
my_stack_structure.add_layers([layer_a, layer_b])
my_stack_structure.N_per = N
The leftmost Layer
in the list is located after the entry half-space and the rightmost Layer
in the list is located before the exit half-space. The periodic pattern can include an arbitrary number of layers and we used two layers as an example.
With the Model
class: the flexible way¶
Creating Layers
from scratch to build up a multilayer stack can become constraining. Instead, pre-defined routines (Models
) allow the user to create particular multilayer stacks such as single slabs, periodic Bragg stacks, isotropic stacks where each layer has the same optical thickness for a given wavelength, and cholesteric stacks more easily.
The class Model
is a general class that gives a blueprint for all its specific children classes: SlabModel
, StackModel
, StackOpticalThicknessModel
, CholestericModel
, etc. “Giving the blueprint” means that parameters and functions that are common to all model classes are defined in the class Model
, and then its children classes inherit them, in addition to having their own specific parameters and functions. The user should directly use the children classes.
Note
Classes that give the blueprint for their children classes are usually called “abstract classes” and cannot be instanciated but it is here possible to instanciate Model
: it creates a stack with no layer.
A multilayer stack made of N
repetitions of a periodic unit consisting in a first layer with a permittivity eps_a
and a thickness thick_a
(in nanometers) and a second layer with a permittivity eps_b
and a thickness thick_b
(in nanometers) can be represented with StackModel
and is defined with:
my_stack_model = StackModel([eps_a, eps_b], [thick_a, thick_b], n_entry, n_exit, wl_nm, theta_in_rad, N)
where the following parameters are required by all Models
:
n_entry
is the refractive index of the entry isotropic mediumn_exit
is the refractive index of the exit isotropic mediumtheta_in_rad
is the angle of incidence in the entry isotropic medium in radianswl_nm
is the wavelength of light, in nanometers
while the lists [eps_a, eps_b]
and [thick_a, thick_b]
are required specifically by StackModel
.
The documentation of the classes SlabModel
, StackOpticalThicknessModel
and CholestericModel
provide information on which parameters are required to create stacks with these specific classes.
With the Spectrum
class: the automated way¶
The Spectrum
class provides a further level of automation for the user. It is meant for experimentalists who measure spectra from multilayered samples and want to quickly model their sample. The class Spectrum
enables to get a full spectrum in one go and to export it for MATLAB or Python processing. The creation of a Spectrum
is not more convenient than the creation of a Model
but they will differ in their utilisation.
A Spectrum
can be created the following way:
my_stack_spec = Spectrum(wl_nm_list, model_type, model_parameters)
where:
wl_nm_list
is a list of wavelengthsmodel_type
is a string that describes the type of model to usemodel_parameters
is a dictionary that contains all parameters needed for the model
For example, to create a Spectrum
in the visible for the periodic multilayer stack described in the previous examples, the input parameters are:
wl_nm_list = range(400, 800)
model_type = "StackModel"
model_parameters = {"eps_list": [eps_a, eps_b],
"thickness_nm_list": [thick_a, thick_b],
"n_entry": n_entry,
"n_exit": n_exit,
"theta_in_rad": theta_in_rad,
"N_per": N}
The documentation of the classes SlabModel
, StackOpticalThicknessModel
and CholestericModel
provide information on which parameters are required to create the appropriate dictionaries for their associated Spectra
.
Note
Models
may have default parameters (for example, when the user does not specify a number of periods for StackModel
, the value is set to 1 automatically), which is specified in their respective documentation. This is maintained in their associated Spectra
: the dictionary of parameters created by the user is merged with a dictionary of default parameters.