Skip to content

Commit d280045

Browse files
committed
Project 1 hint/solutions complete
1 parent 9f91482 commit d280045

11 files changed

+926
-38
lines changed

Project#01/README.md

+38-38
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# TODO:
22
- [ ] Add embedded equations
3-
- [ ] Add hints and solutions for each step
3+
- [x] Add hints and solutions for each step
44
- [x] Add input/output links for test cases
55

66
# Molecular-Geometry-Analysis
7-
The purpose of this project is to introduce you to fundamental C-language (or C++) programming techniques in the context of a scientific problem, viz. the calculation of the internal coordinates (bond lengths, bond angles, dihedral angles), moments of inertia, and rotational constants of a polyatomic molecule. A concise set of instructions for this project may be found [here](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/project1-instructions.pdf).
7+
The purpose of this project is to introduce you to fundamental C-language (or C++) programming techniques in the context of a scientific problem, viz. the calculation of the internal coordinates (bond lengths, bond angles, dihedral angles), moments of inertia, and rotational constants of a polyatomic molecule. A concise set of instructions for this project may be found [here](./project1-instructions.pdf).
88

99
We thank Dr. Yukio Yamaguchi of the University of Georgia for the original version of this project.
1010

@@ -20,14 +20,14 @@ The input to the program is the set of Cartesian coordinates of the atoms (in bo
2020
1 -1.007295466862 -1.669971842687 -0.705916966833
2121
1 -1.007295466862 1.669971842687 -0.705916966833
2222

23-
The first line above is the number of atoms (an integer), while the remaining lines contain the z-values and x-, y-, and z-coordinates of each atom (one integer followed by three double-precision floating-point numbers). This [input file](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/input/acetaldehyde.dat) ("acetaldehyde.dat") along with a few other test cases can be found in this repository in the [input directory](https://github.com/CrawfordGroup/ProgrammingProjects/tree/master/Project%2301/input).
23+
The first line above is the number of atoms (an integer), while the remaining lines contain the z-values and x-, y-, and z-coordinates of each atom (one integer followed by three double-precision floating-point numbers). This [input file](./input/acetaldehyde.dat) ("acetaldehyde.dat") along with a few other test cases can be found in this repository in the [input directory](./input).
2424

2525
After downloading the file to your computer (to a file called “geom.dat”, for example), you must open the file, read the data from each line into appropriate variables, and finally close the file.
2626

27-
- Hint #1: Opening and closing the file stream
28-
- Hint #2: Reading the number of atoms
29-
- Hint #3: Storing the z-values and the coordinates
30-
- Solution
27+
- [Hint #1](./hints/hint1-1.md): Opening and closing the file stream
28+
- [Hint #2](./hints/hint1-2.md): Reading the number of atoms
29+
- [Hint #3](./hints/hint1-3.md): Storing the z-values and the coordinates
30+
- [Solution](./hints/step1-solution.md)
3131

3232
## Step 2: Bond Lengths
3333
Calculate the interatomic distances using the expression:
@@ -38,11 +38,11 @@ EQUATION
3838

3939
where x, y, and z are Cartesian coordinates and i and j denote atomic indices.
4040

41-
- Hint 1: Memory allocation
42-
- Hint 2: Loop structure
43-
- Hint 3: Printing the results
44-
- Hint 4: Extending the Molecule class
45-
- Solution
41+
- [Hint 1](./hints/hint2-1.md): Memory allocation
42+
- [Hint 2](./hints/hint2-2.md): Loop structure
43+
- [Hint 3](./hints/hint2-3.md): Printing the results
44+
- [Hint 4](./hints/hint2-4.md): Extending the Molecule class
45+
- [Solution](./hints/step2-solution.md)
4646

4747
## Step 3: Bond Angles
4848
Calculate all possible bond angles. For example, the angle, &phi;<sub>ijk</sub>, between atoms i-j-k, where j is the central atom is given by:
@@ -57,12 +57,12 @@ where the e&#8407;<sub>ij</sub> are unit vectors between the atoms, e.g.,
5757
EQUATION
5858
```
5959

60-
- Hint 1: Memory allocation for the unit vectors
61-
- Hint 2: Avoiding a divide-by-zero
62-
- Hint 3: Memory allocation for the bond angles
63-
- Hint 4: Smart printing
64-
- Hint 5: Trigonometric functions
65-
- Solution
60+
- [Hint 1](./hints/hint3-1.md): Memory allocation for the unit vectors
61+
- [Hint 2](./hints/hint3-2.md): Avoiding a divide-by-zero
62+
- [Hint 3](./hints/hint3-3.md): Memory allocation for the bond angles
63+
- [Hint 4](./hints/hint3-4.md): Smart printing
64+
- [Hint 5](./hints/hint3-5.md): Trigonometric functions
65+
- [Solution](./hints/step3-solution.md)
6666

6767
## Step 4: Out-of-Plane Angles
6868
Calculate all possible out-of-plane angles. For example, the angle &theta;<sub>ijkl</sub> for atom i out of the plane containing atoms j-k-l (with k as the central atom, connected to i) is given by:
@@ -71,11 +71,11 @@ Calculate all possible out-of-plane angles. For example, the angle &theta;<sub>i
7171
EQUATION
7272
```
7373

74-
- Hint 1: Memory allocation?
75-
- Hint 2: Cross products
76-
- Hint 3: Numerical precision
77-
- Hint 4: Smarter printing
78-
- Solution
74+
- [Hint 1](./hints/hint4-1.md): Memory allocation?
75+
- [Hint 2](./hints/hint4-2.md): Cross products
76+
- [Hint 3](./hints/hint4-3.md): Numerical precision
77+
- [Hint 4](./hints/hint4-4.md): Smarter printing
78+
- [Solution](./hints/step4-solution.md)
7979

8080
## Step 5: Torsion/Dihedral Angles
8181
Calculate all possible torsional angles. For example, the torsional angle &tau;<sub>ijkl</sub> for the atom connectivity i-j-k-l is given by:
@@ -86,11 +86,11 @@ EQUATION
8686

8787
Can you also determine the sign of the torsional angle?
8888

89-
- Hint 1: Memory allocation?
90-
- Hint 2: Numerical precision
91-
- Hint 3: Smart printing
92-
- Hint 4: Sign
93-
- Solution
89+
- [Hint 1](./hints/hint5-1.md): Memory allocation?
90+
- [Hint 2](./hints/hint5-2.md): Numerical precision
91+
- [Hint 3](./hints/hint5-3.md): Smart printing
92+
- [Hint 4](./hints/hint5-4.md): Sign
93+
- [Solution](./hints/step5-solution.md)
9494

9595
## Step 6: Center-of-Mass Translation
9696
Find the center of mass of the molecule:
@@ -103,9 +103,9 @@ where m<sub>i</sub> is the mass of atom i and the summation runs over all atoms
103103

104104
Translate the input coordinates of the molecule to the center-of-mass.
105105

106-
- Hint 1: Atomic masses
107-
- Hint 2: Translating between atomic number and atomic mass
108-
- Solution
106+
- [Hint 1](./hints/hint6-1.md): Atomic masses
107+
- [Hint 2](./hints/hint6-2.md): Translating between atomic number and atomic mass
108+
- [Solution](./hints/step6-solution.md)
109109

110110
## Step 7: Principal Moments of Inertia
111111
Calculate elements of the [moment of inertia tensor](http://en.wikipedia.org/wiki/Moment_of_inertia_tensor).
@@ -129,9 +129,9 @@ Report the moments of inertia in amu bohr<sup>2</sup>, amu &#8491;<sup>2</sup>,
129129

130130
Based on the relative values of the principal moments, determine the [molecular rotor type](http://en.wikipedia.org/wiki/Rotational_spectroscopy): linear, oblate, prolate, asymmetric.
131131

132-
- Hint 1: Diagonalization of a 3×3 matrix
133-
- Hint 2: Physical constants
134-
- Solution
132+
- [Hint 1](./hints/hint7-1.md): Diagonalization of a 3×3 matrix
133+
- [Hint 2](./hints/hint7-2.md): Physical constants
134+
- [Solution](./hints/step7-solution.md)
135135

136136
## Step 8: Rotational Constants
137137
Compute the rotational constants in cm<sup>-1</sup> and MHz:
@@ -140,13 +140,13 @@ Compute the rotational constants in cm<sup>-1</sup> and MHz:
140140
EQUATION
141141
```
142142

143-
- Solution
143+
- [Solution](./hints/step8-solution.md)
144144

145145

146146
## Test Cases
147-
- Acetaldehyde: [input coordinates](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/input/acetaldehyde.dat) | [output](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/output/acetaldehyde_out.txt)
148-
- Benzene: [input coordinates](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/input/benzene.dat) | [output](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/output/benzene_out.txt)
149-
- Allene: [input coordinates](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/input/allene.dat) | [output](https://github.com/CrawfordGroup/ProgrammingProjects/blob/master/Project%2301/output/allene_out.txt)
147+
- Acetaldehyde: [input coordinates](./input/acetaldehyde.dat) | [output](./output/acetaldehyde_out.txt)
148+
- Benzene: [input coordinates](./input/benzene.dat) | [output](./output/benzene_out.txt)
149+
- Allene: [input coordinates](./input/allene.dat) | [output](./output/allene_out.txt)
150150

151151
## References
152152
E.B. Wilson, J.C. Decius, and P.C. Cross, __Molecular Vibrations__, McGraw-Hill, 1955.

Project#01/eigen.tar.gz

549 KB
Binary file not shown.

Project#01/hints/hint5-4.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The sign of a torsional/dihedral angle among atoms **_i-j-k-l_** is positive (negative) if the vector along **_k-l_** lies to the right (left) of the plane formed by **_i-j-k_** when the plane is viewed along the **_j-k_** vector.

Project#01/hints/hint6-1.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
An excellent source for atomic masses and other physical constants is the [National Institute of Standard and Technology (NIST) website](http://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=&ascii=html&isotype=some).

Project#01/hints/hint6-2.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
An elegant way to translate between the atomic number (z-value) of a given atom and its mass is to prepare a static array of the masses of the most abundant isotope of each element. I suggest preparing a header file containing a [global array](https://github.com/CrawfordGroup/ProgrammingProjects/wiki/Variable-Scope-and-Reference-Types#global-variables), e.g.:
2+
```c++
3+
double masses[] = {
4+
0.0000000,
5+
1.007825,
6+
4.002603,
7+
6.015123,
8+
...};
9+
```
10+
Note that, for example, `masses[1] = 1.007825`, which is the correct atomic mass for a hydrogen atom.

Project#01/hints/hint7-1.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
Here are two approaches for the [diagonalization](http://en.wikipedia.org/wiki/Diagonalizable_matrix) of the moment of inertia tensor:
2+
3+
## Secular Determinant
4+
Since the moment of inertia tensor is only a 3x3 matrix, a brute-force approach via the secular determinant is feasible:
5+
6+
```latex
7+
\left|\begin{array}{ccc}
8+
(I_{11}-\lambda) & I_{12} & I_{13} \\
9+
I_{21} & (I_{22}-\lambda) & I_{23} \\
10+
I_{31} & I_{32} & (I_{33}-\lambda) \\
11+
\end{array}\right| = 0
12+
```
13+
14+
This leads to a cubic equation in &lambda;, which one can solve directly. Have fun with that.
15+
16+
## More General Algorithms
17+
"Canned" algorithms are definitely the way to go for general matrix diagonalization. Most such algorithms are based on a two-step procedure:
18+
- Reduction of the matrix to a tridiagonal form using the [Householder](http://en.wikipedia.org/wiki/Householder's_method) or Givens approaches.
19+
- Diagonalization of the tridiagonal structure, either by solving its secular determinant or by other methods, e.g. [QR or QL decompositions](http://en.wikipedia.org/wiki/QR_decomposition).
20+
21+
A convenient canned library for a wide range of linear algebraic operations is the [Eigen package](http://eigen.tuxfamily.org). This is a template-only library that provides a very clean interface for manipulating a large number of matrix types. You can either download and install the library from the [official website](http://eigen.tuxfamily.org) or just grab the [gzipped tarfile from here](../eigen.tar.gz). Unpack the library in the same directory as your source code, and you're ready to get started.
22+
23+
To use the library to diagonalize your moment inertia tensor, follow these steps:
24+
25+
- Add the following lines to your main source file below the inclusion of other headers:
26+
```c++
27+
#include "Eigen/Dense"
28+
#include "Eigen/Eigenvalues"
29+
#include "Eigen/Core"
30+
31+
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Matrix;
32+
```
33+
This code defines a new type called a `Matrix` that may be dynamically allocated and contains only doubles.
34+
- Allocate your moment of inertia tensor by a line of code like:
35+
```c++
36+
Matrix I(3,3);
37+
```
38+
- Access or assign individual elements of the Matrix using parenthetical notation rather than brackets, e.g.:
39+
```c++
40+
I(0,0) = 2.0;
41+
```
42+
- The Eigen package makes it easy to examine your matrix using cout:
43+
```c++
44+
cout << I << endl;
45+
```
46+
- After you have built the moment of inertia tensor, you may compute its eigenvalues and eigenvectors as follows:
47+
```c++
48+
Eigen::SelfAdjointEigenSolver<Matrix> solver(I);
49+
Matrix evecs = solver.eigenvectors();
50+
Matrix evals = solver.eigenvalues();
51+
```

Project#01/hints/hint7-2.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lots of useful and precise physical constants are available at the [National Institute of Standards and Technology website](http://physics.nist.gov/cuu/Constants/index.html?/codata86.html).

Project#01/hints/step5-solution.md

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
Now we've added a new member function to the Molecule class:
2+
```c++
3+
// Computes the angle between planes a-b-c and b-c-d
4+
double Molecule::torsion(int a, int b, int c, int d)
5+
{
6+
double eabc_x = (unit(1,b,a)*unit(2,b,c) - unit(2,b,a)*unit(1,b,c));
7+
double eabc_y = (unit(2,b,a)*unit(0,b,c) - unit(0,b,a)*unit(2,b,c));
8+
double eabc_z = (unit(0,b,a)*unit(1,b,c) - unit(1,b,a)*unit(0,b,c));
9+
10+
double ebcd_x = (unit(1,c,b)*unit(2,c,d) - unit(2,c,b)*unit(1,c,d));
11+
double ebcd_y = (unit(2,c,b)*unit(0,c,d) - unit(0,c,b)*unit(2,c,d));
12+
double ebcd_z = (unit(0,c,b)*unit(1,c,d) - unit(1,c,b)*unit(0,c,d));
13+
14+
double exx = eabc_x * ebcd_x;
15+
double eyy = eabc_y * ebcd_y;
16+
double ezz = eabc_z * ebcd_z;
17+
18+
double tau = (exx + eyy + ezz)/(sin(angle(a,b,c)) * sin(angle(b,c,d)));
19+
20+
if(tau < -1.0) tau = acos(-1.0);
21+
else if(tau > 1.0) tau = acos(1.0);
22+
else tau = acos(tau);
23+
24+
// Compute the sign of the torsion
25+
double cross_x = eabc_y * ebcd_z - eabc_z * ebcd_y;
26+
double cross_y = eabc_z * ebcd_x - eabc_x * ebcd_z;
27+
double cross_z = eabc_x * ebcd_y - eabc_y * ebcd_x;
28+
double norm = cross_x*cross_x + cross_y*cross_y + cross_z*cross_z;
29+
cross_x /= norm;
30+
cross_y /= norm;
31+
cross_z /= norm;
32+
double sign = 1.0;
33+
double dot = cross_x*unit(0,b,c)+cross_y*unit(1,b,c)+cross_z*unit(2,b,c);
34+
if(dot < 0.0) sign = -1.0;
35+
36+
return tau*sign;
37+
}
38+
```
39+
40+
And we use the new function in the code as follows:
41+
42+
```c++
43+
#include <iostream>
44+
#include <fstream>
45+
#include <iomanip>
46+
#include <cstdio>
47+
#include <cmath>
48+
#include "molecule.h"
49+
50+
using namespace std;
51+
52+
int main()
53+
{
54+
Molecule mol("geom.dat", 0);
55+
56+
cout << "Number of atoms: " << mol.natom << endl;
57+
cout << "Input Cartesian coordinates:\n";
58+
mol.print_geom();
59+
60+
cout << "Interatomic distances (bohr):\n";
61+
for(int i=0; i < mol.natom; i++)
62+
for(int j=0; j < i; j++)
63+
printf("%d %d %8.5f\n", i, j, mol.bond(i,j));
64+
65+
cout << "\nBond angles:\n";
66+
for(int i=0; i < mol.natom; i++) {
67+
for(int j=0; j < i; j++) {
68+
for(int k=0; k < j; k++) {
69+
if(mol.bond(i,j) < 4.0 && mol.bond(j,k) < 4.0)
70+
printf("%2d-%2d-%2d %10.6f\n", i, j, k, mol.angle(i,j,k)*(180.0/acos(-1.0)));
71+
}
72+
}
73+
}
74+
75+
cout << "\nOut-of-Plane angles:\n";
76+
for(int i=0; i < mol.natom; i++) {
77+
for(int k=0; k < mol.natom; k++) {
78+
for(int j=0; j < mol.natom; j++) {
79+
for(int l=0; l < j; l++) {
80+
if(i!=j && i!=k && i!=l && j!=k && k!=l && mol.bond(i,k) < 4.0 && mol.bond(k,j) < 4.0 && mol.bond(k,l) < 4.0)
81+
printf("%2d-%2d-%2d-%2d %10.6f\n", i, j, k, l, mol.oop(i,j,k,l)*(180.0/acos(-1.0)));
82+
}
83+
}
84+
}
85+
}
86+
87+
cout << "\nTorsional angles:\n\n";
88+
for(int i=0; i < mol.natom; i++) {
89+
for(int j=0; j < i; j++) {
90+
for(int k=0; k < j; k++) {
91+
for(int l=0; l < k; l++) {
92+
if(mol.bond(i,j) < 4.0 && mol.bond(j,k) < 4.0 && mol.bond(k,l) < 4.0)
93+
printf("%2d-%2d-%2d-%2d %10.6f\n", i, j, k, l, mol.torsion(i,j,k,l)*(180.0/acos(-1.0)));
94+
}
95+
}
96+
}
97+
}
98+
99+
return 0;
100+
}
101+
```
102+
103+
The above code produces the following output when applied to the acetaldehyde test case:
104+
105+
```
106+
Number of atoms: 7
107+
Input Cartesian coordinates:
108+
6 0.000000000000 0.000000000000 0.000000000000
109+
6 0.000000000000 0.000000000000 2.845112131228
110+
8 1.899115961744 0.000000000000 4.139062527233
111+
1 -1.894048308506 0.000000000000 3.747688672216
112+
1 1.942500819960 0.000000000000 -0.701145981971
113+
1 -1.007295466862 -1.669971842687 -0.705916966833
114+
1 -1.007295466862 1.669971842687 -0.705916966833
115+
Interatomic distances (bohr):
116+
1 0 2.84511
117+
2 0 4.55395
118+
2 1 2.29803
119+
3 0 4.19912
120+
3 1 2.09811
121+
3 2 3.81330
122+
4 0 2.06517
123+
4 1 4.04342
124+
4 2 4.84040
125+
4 3 5.87463
126+
5 0 2.07407
127+
5 1 4.05133
128+
5 2 5.89151
129+
5 3 4.83836
130+
5 4 3.38971
131+
6 0 2.07407
132+
6 1 4.05133
133+
6 2 5.89151
134+
6 3 4.83836
135+
6 4 3.38971
136+
6 5 3.33994
137+
138+
Bond angles:
139+
0- 1- 2 124.268308
140+
0- 1- 3 115.479341
141+
0- 4- 5 35.109529
142+
0- 4- 6 35.109529
143+
0- 5- 6 36.373677
144+
1- 2- 3 28.377448
145+
4- 5- 6 60.484476
146+
147+
Out-of-plane angles:
148+
0- 3- 1- 2 -0.000000
149+
0- 6- 4- 5 19.939726
150+
0- 6- 5- 4 -19.850523
151+
0- 5- 6- 4 19.850523
152+
1- 5- 0- 4 53.678778
153+
1- 6- 0- 4 -53.678778
154+
1- 6- 0- 5 54.977064
155+
2- 3- 1- 0 0.000000
156+
3- 2- 1- 0 -0.000000
157+
4- 5- 0- 1 -53.651534
158+
4- 6- 0- 1 53.651534
159+
4- 6- 0- 5 -54.869992
160+
4- 6- 5- 0 29.885677
161+
4- 5- 6- 0 -29.885677
162+
5- 4- 0- 1 53.626323
163+
5- 6- 0- 1 -56.277112
164+
5- 6- 0- 4 56.194621
165+
5- 6- 4- 0 -30.558964
166+
5- 4- 6- 0 31.064344
167+
6- 4- 0- 1 -53.626323
168+
6- 5- 0- 1 56.277112
169+
6- 5- 0- 4 -56.194621
170+
6- 5- 4- 0 30.558964
171+
6- 4- 5- 0 -31.064344
172+
173+
Torsional angles:
174+
175+
3- 2- 1- 0 180.000000
176+
6- 5- 4- 0 36.366799
177+
```

0 commit comments

Comments
 (0)