Synoptic exercises

1. Consider the following blocks of code. Each one of them will cause an error when run. Taking each block one-by-one:

Block 1


You will need this file.

import *NumPy* as np

wavenumber, transmittance = np.loadtxt("ir_data.csv", delimiter=",")
print(wavenumber, transmittance)

Block 2


Tip

This block also contains a problem which, whilst it will not cause an error, will lead to the wrong result. See if you can spot and fix this problem in addition to removing the error.

You will need this file.

with open('elements.txt', 'a') as f:
  lines = f.readlines()

for line in lines:
  num_h += line.count('h')

print(f'There are {num_h:d} occurences of the letter H in the names of the 118 elements.')

2. The atomic coordinates of molecules are often stored xyz files. These have the following format:

NUMBER_OF_ATOMS
COMMENT
ATOM1_LABEL ATOM1_X ATOM1_Y ATOM1_Z
ATOM2_LABEL ATOM2_X ATOM2_Y ATOM2_Z
...
ATOMN_LABEL ATOMN_X ATOMN_Y ATOMN_Z

The number of atoms is an int, the comment line is a str which can optionally be blank, the atom labels are str (e.g. 'H', or 'H2'), and the \(x\), \(y\), and \(z\) coordinates are all float with units of Angstrom.

a. Using np.loadtxt, read this xyz file for hydrogen peroxide (\(\mathrm{H_2O_2}\)) and store its atom labels and coordinates in two separate NumPy arrays.

Tip

You can tell np.loadtxt() to ignore a given number of lines using the skiprows keyword argument.

You can tell np.loadtxt() to extract specific columns of data using the usecols keyword argument, e.g. usecols=(0, 1, 3) extracts the 1st, 2nd, and 4th columns.

By default, np.loadtxt() will try to read the file’s contents as float, to read str you’ll need to set the dtype (short for datatype) keyword argument equal to str.

These are summarised in the NumPy documentation page for np.loadtxt.

The labels used in this xyz file are shown in the figure below.

b. The distance between two atoms \(\mathrm{A}\) and \(\mathrm{B}\) is given by

\[\begin{equation*} d_\mathrm{AB} = \sqrt{\left(x_\mathrm{B}-x_\mathrm{A}\right)^2 + \left(y_\mathrm{B}-y_\mathrm{A}\right)^2 + \left(z_\mathrm{B}-z_\mathrm{A}\right)^2} \end{equation*}\]

where, for example, \(x_\mathrm{B}\) is the \(x\) coordinate of atom \(\mathrm{B}\).

i. Write a function that takes two NumPy arrays as arguments - each containing the \(x\), \(y\), and \(z\) coordinates of an atom - and returns the distance between them.

ii. Using your function, calculate the \(\mathrm{O}_1-\mathrm{O}_2\) bond length, and the average \(\mathrm{O}-\mathrm{H}\) bond length.

c. We can define the vector \(\overrightarrow{\mathrm{AB}}\) between atoms \(\mathrm{A}\) and \(\mathrm{B}\) as

\[\begin{equation*} \overrightarrow{\mathrm{AB}} = \begin{bmatrix} x_\mathrm{B}-x_\mathrm{A}\\ y_\mathrm{B}-y_\mathrm{A}\\ z_\mathrm{B}-z_\mathrm{A} \end{bmatrix} \end{equation*}\]

A vector is a mathematical quantity which has both direction and magnitude.

We denote vectors using an arrow over the top of their mathematical symbol, and write them in full as either a column or a row containg a set of coordinates which describe the vector’s magnitude (length) and direction.

Calculate the vector \(\overrightarrow{\mathrm{O_1O_2}}\) which connects the two oxygen atoms \(\mathrm{O_1}\) and \(\mathrm{O_2}\), and store it in a NumPy array.

d. Using np.linalg.norm, compute the vector norm of \(\overrightarrow{\mathrm{O_1O_2}}\) from part c. Compare this to your answer in part b, what do you think the vector norm is?

e. The angle \(\theta\) between two bonds can be obtained by constructing vectors between the three atoms involved and then calculating the angle between those vectors.

\[\begin{equation*} \theta = \cos^{-1}\left(\frac{\overrightarrow{\mathrm{AB}}\cdot\overrightarrow{\mathrm{BC}}}{d_\mathrm{AB}\, d_\mathrm{BC}}\right) \end{equation*}\]

where the symbol \(\cdot\) implies the dot product of the two vectors is taken. This is defined as

\[\begin{equation*} \overrightarrow{\mathrm{D}}\cdot\overrightarrow{\mathrm{E}} = x_\mathrm{D} x_\mathrm{E} + y_\mathrm{D} y_\mathrm{E} + z_\mathrm{D} z_\mathrm{E} \end{equation*}\]

i. Write a function that takes two NumPy arrays as arguments - each containing a vector - and returns the angle between them.

Tip

You can calculate the dot product between two NumPy arrays using np.dot(array_1, array_2).

NumPy provides an inverse cosine (\(\cos^{-1}\)) function as np.arccos.

ii. Using your function, calculate the average \(\mathrm{O\hat{O}H}\) bond angle in hydrogen peroxide.