Tutorial

This is an example of how an interactive IPython/Jupyter notebook works with this optics project.

It'll be split into different sections explaining individual components and building up towards a full use case.

Oscillators

We have implemented a few commonly used oscillators/lineshapes. These are located in the Oscillators module.


In [ ]:
import Oscillators as osc

Let's create a Lorentz oscillator of amplitude 1, center position 1.5 eV, and width 0.2 eV.


In [ ]:
l = osc.Lorentz(1,0.2,1.5)
print(l)

We can directly ask some properties to this oscillator, like its dielectric function in a particular energy/frequency window of interest.

First we define the window of interest though a set of points where the dielectric function will be evaluated. In our case we want the range from 0.01 to 3 eV in steps of 0.1. For this we will use numpy, but a standard Python list works too!


In [ ]:
import numpy as np
l.dielectricFunction(np.arange(0.01,3,0.1))

As it can be seen, a numpy array of complex values containing the dielectric function evaluated in the points given is returned. But just a list of numbers do not tell much, thus we want to be able to see it.

We will use the matplotlib package to manually plot the dielectric function of this oscillator, but later we will see an alternative that is provided allowing for a shortcut.

Again, we will define a window of interest but with a smaller step.


In [ ]:
import numpy as np
%pylab inline
window = np.arange(0.01,3,0.01)
plot(window, l.dielectricFunction(window))

We can see that we get a warning because the dielectric function is a complex value and only the real part is considered ($\varepsilon_1$). Let's plot the imaginary part of the dielectric function ($\varepsilon_2$).


In [ ]:
plot(window, np.imag(l.dielectricFunction(window)))

And of course we could show both parts of the dielectric function in one single plot.


In [ ]:
plot(window, np.real(l.dielectricFunction(window)))
plot(window, np.imag(l.dielectricFunction(window)))

Another interesting propertie of oscillators are their optical conductivity and spectral weight. These two can also be accessed at an oscillator level.

Optical Models

To combine more than one oscillator to model the optical response of a material, an optical model can be created. This model contains the collection of oscillators describing a system.

We will create an empty model and add to it the already created oscillator in the previous section.


In [ ]:
from OpticalModel import *
om = OpticalModel()
om.add(l)

Here we can see a text representation of the model indicating its name, which by default is Optical Model 1, and a list of the oscillators included. If you have run the previous cell more than once, then the number in the name of the model will change representing how many models have been created in a session. This can be changed by providing a name when creating the model of afterwards.

Let us change the name and then make use of a shortcut to plot the dielectric function of the model reusing the previously defined window of interest.


In [ ]:
om.name = "Demo Model"
om.plot(window)

This shortcut provides a way to visualize the dielectric function in a quick and convenient way, providing automatic labeling of axes and a title. Note that the title coincides with the name of the model!

For those with some Python experience, an Optical Model instance supports all operations of a list!

Now we would like to make something slightly more complex, and add a second oscillator to the model. In this case, we will add a Gaussian. The oscillator is added directly to the model and it is in its own cell to avoid adding it more than once in case we want to modify the plot creation that follows.


In [ ]:
om.add(osc.Gauss(1,0.4,3))

Now we can show the model and then plot it again.


In [ ]:
om.show()
om.plot(window)

We do not see the whole of the Gaussian! In this case, since the Gaussian is added at 3 eV, we have to extend the window of interest.


In [ ]:
extended_window = np.arange(0.01,4,0.01)
om.plot(extended_window)

Now, we can see that the plot is becoming a bit saturated. Optionally, we could ask for alternative plot. For example we want to see $\varepsilon_2$ alone.


In [ ]:
om.plot(extended_window, flag = "e2")

Alternatively, we can get other optical properties of the model. For instance the reflectivity by using the "R" flag.


In [ ]:
om.plot(extended_window, flag = "R")

Other available options are e1, e2, s1, s2, n, k, nk.

A model can be saved to disk as a json file to be reloaded in later sessions. When saved without a filename, it uses the name of the model as a filename.


In [ ]:
om.dump("Demo Model")

Datasets

Datasets of experimental data can be also managed with the intention of using them to fit a model to it.

Let us import a simple reflectivity dataset using the loadRaw function. Since the unit of the data in inverse centimeters ($cm^{-1}$), as encountered in datasets from Bruker FT equipement for example, we use the optional argument unit= "cm-1" to indicate that. Internally we use an eV representatin of energies. In this case we again have a shortcut to provide a simple graph of the dataset.


In [ ]:
from Datasets import *
data = Dataset()
data.loadRaw("Examples/Reflectivity-Simple-wavenumber.dat", unit = "cm-1")
data.plot()

A dataset can be transformed in different ways. For example we could scale the values for reflectivity. Let us try it up $10%$.


In [ ]:
data.scale(1.1)
data.plot()

The previous operation is an in place one, modifying the data. Alternatively, to preserve the original dataset one could clone and apply the scaling.


In [ ]:
scaled_data = data.clone(scale=0.9, name = "Scaled reflectivity")
scaled_data.plot()

Besides scale, the operations shift, and subset are supported.


In [ ]: