MSE 104L laboratory 2 exercises
Contents
MSE 104L laboratory 2 exercises¶
Authors: Enze Chen (University of California, Berkeley)
Note
This is an interactive exercise, so you will want to click the and open the notebook in DataHub (or Colab for non-UCB students).
This notebook contains a series of exercises to help you process your data from Lab 2. It doesn’t answer all of the discussion questions in the lab procedures, but it will help you create some figures that can supplement the narrative of your lab report.
Contents¶
The exercises in this notebook include:
Import Python packages¶
It can be pretty helpful to import all required packages at the top of the notebook. We’ve imported a few for you, and we’ll let you figure out what else you might need. Don’t forget the plot styling! ⭐
import numpy as np
import pandas as pd
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
Plot a spectra as a sanity check¶
Since you’re working with more powder XRD spectra in this lab, it’s a good idea to just visualize the spectra of each sample as a reference. This is also good practice to remind yourself of the commands from the previous lab. You should be quite proficient at this by now!
Remember the general steps are:
Upload your data to JupyterHub, this time in the
lab2
folder.Write code below to process and plot the data.
Save the figure and download it to your computer.
Recall this is
fig.savefig('my_figure.png', dpi=300, bbox_inches='tight')
.
If you don’t remember what you did in lab 1, you can always reference your completed notebooks (assuming you did them) by going to JupyterHub (https://datahub.berkeley.edu) and opening them 📁 > mse104l
> lab1
.
Copy+paste previous code is definitely a thing!
It’s also a huge advantage of programming over manual manipulation.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
Lattice constant calculations¶
In this course, you can expect to be given an XRD spectrum of a cubic material and deduce the crystal structure from the peak positions. This is a tedious exercise by hand because we have to compute each peak individually, so we’ll try to speed things up and automate the calculations using Python. This will take some time initially, but save you time in the long run. 😉
Arguably the most important equation is Bragg’s law, given by
where \(n\) is the order (typically \(1\)), \(\lambda\) is the wavelength, \(d\) is the interplanar spacing, and \(\theta\) is the Bragg angle. We’ll be manipulating this equation to calculate lattice constants.
1. Inputs / experimental data¶
Start by typing in your known values collected from the experiment:
Wavelength: A
float
corresponding to \(\lambda\) of your X-ray source, likely Cu-K\(\alpha\).Angles: Values are \(2\theta\) in degrees. The angles should be stored in a
list
in the form
angles = [1.23, 4.56, 7.89]
for however many \(2\theta\) peak values you’ve measured (maybe 3 to 5, or so).
Note: Unlike the spectra plots, we only need the angles corresponding to the peak positions, not all angles collected by the diffractometer. 😅
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
wavelength = None
angles = []
2. Creating our DataFrame¶
Now it’s time to construct our pandas DataFrame using the dict
version of the constructor.
Recall that a dictionary takes the form:
my_dict = {'key1':val1, 'key2':val2, ...}
We’ll need one column (key:value
pair) for the wavelength
and another for angles
.
Once you’ve constructed the DataFrame, the df
variable on the last line will display it.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
df # you should see two columns with your data! Always good to show the df
3. Numerical calculations¶
Note that if we assume \(n=1\) in Bragg’s law, then we have three unknowns. In our case, we know the wavelength and angle, so we can find the interplanar spacing. We will try to do this in a very principled fashion. Modular solutions are very important when programming.
3.1 Find \(\theta\)¶
First, we need to get the \(\theta\) values.
We can do this by creating a new column in the DataFrame whose entries are computed by dividing the existing column of \(2\theta\) values by 2.
Also, we strongly recommend that you convert the angles to radians using the np.deg2rad()
function on the result. ⭐
So, the syntax will look something like this:
df['new_column_name'] = np.deg2rad(df['column_of_2theta_angles'] / 2)
Of course, please use better names. After you’ve done this, display the DataFrame like we did above to confirm you did it correctly.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
3.2 Take the sine¶
Next, we’ll compute the sine of the \(\theta\) values using np.sin()
, whose argument inside the parentheses must be in radians—good thing we converted it earlier!
Add another column to your DataFrame with the sine of the angles.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
3.3 Calculate the interplanar spacing, \(d\)¶
Now you have all the pieces in place for calculating \(d\). What’s awesome about pandas is that you can perform element-wise division of two columns by use of the division operator, /
.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
3.4 Take the ratio of \(d^2\) values¶
At this point, you know to take the ratio between the first distance and each distance measured; however, there’s a small “hack” to this. 😎
If you do it naively, you’ll get numbers like 1.000
, 1.414
, 1.732
, and the like.
Unless you know your square root approximations, this can be tricky to decipher.
We prefer to square the distances first, and then take the ratio.
We’ve started it below, so you have to fill in the second half.
The resulting column should have values that are close to integers or multiples of a simple fraction like \(\frac{1}{3}\).
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
# square the distances
df['d^2'] = df['d'] ** 2
# take the ratio - finish this one-liner
# show the df
df
3.5 Figure out the crystal structure and index the peaks¶
OK! At this point, the ratios should be enough for you to deduce which crystal structure you have on your hands. This will allow you to index the peaks accordingly. You can probably do this part by hand.
Nelson-Riley plots¶
Recall that the Nelson-Riley function is used for cubic systems to get a more accurate measurement of the lattice constant. The Nelson-Riley function is
You can probably sense where this is going. If you’ve done the above calculations with the pandas DataFrame, it’s quite easy to add another column of Nelson-Riley values! Only one line of number crunching with Equation (2). If you already did the previous parts and come back to this section later, you can easily rerun everything (Menu bar: Cell > Run All) and pick up where you left off.
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
Recall that for a cubic system the interplanar spacing between \((hkl)\) planes is given by (Table 4.1.2 in Krishnan):
In order to get the cubic lattice constants for our Nelson-Riley plots, we need to first include \(s\), which we can hard code below.
Based on the structure (SC, BCC, FCC), index the planes accordingly to get \(hkl\) values to calculate \(s\).
To clarify what we mean, we’ve left in the code structure below, which you’ll have to again modify and fill in the calculation to get \(a\).
In the JupyterHub, we’ve also included a PNG of our final data table (xrd_dataframe.png
, using our fake data) so you have a sense of what we were intending—and if you did something else, that’s OK!
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
s = np.sqrt([3, 4, 8, 11, 12, 16, 19]) # example only—you may want to change this!
df['s'] = s # create a column
df # add in one line of multiplication above to calculate a!
Now you’re all set to make your NR plot! Note how by doing calculations in the DataFrame, we obtained all NR and \(a\) values at once.
⭐ First plot the individual data points using a method like
ax.scatter()
. Thes
parameter changes the marker size.Then compute a line of best fit using
np.polyfit()
and add it to the plot.⭐ You may want to
ax.set()
the \(x\)-axis limits to include \(x=0\) as a guide to the eye. 👀Of course, with
np.polyfit()
, you can directly obtain the value of interest andprint()
it out!
# ------------- WRITE YOUR CODE IN THE SPACE BELOW ---------- #
Conclusion¶
This concludes the programming exercises for Lab 2. Congratulations! We hope you’re proud of the plots that you generated and we wish you luck with the lab writeup. 📝