Skip to content

Commit f1539d0

Browse files
committed
First set of hints from Project#1
1 parent cd717a9 commit f1539d0

File tree

10 files changed

+273
-5
lines changed

10 files changed

+273
-5
lines changed

Project#01/hints/hint2-2.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ To build the distance matrix, we need a loop for each index:
66

77
for(int i=0; i < mol.natom; i++) {
88
for(int j=0; j < mol.natom; j++) {
9-
R[i][j] = sqrt(
10-
(mol.geom[i][0]-mol.geom[j][0])*(mol.geom[i][0]-mol.geom[j][0])
11-
+ (mol.geom[i][1]-mol.geom[j][1])*(mol.geom[i][1]-mol.geom[j][1])
12-
+ (mol.geom[i][2]-mol.geom[j][2])*(mol.geom[i][2]-mol.geom[j][2])
13-
);
9+
R[i][j] = sqrt( (mol.geom[i][0]-mol.geom[j][0])*(mol.geom[i][0]-mol.geom[j][0])
10+
+ (mol.geom[i][1]-mol.geom[j][1])*(mol.geom[i][1]-mol.geom[j][1])
11+
+ (mol.geom[i][2]-mol.geom[j][2])*(mol.geom[i][2]-mol.geom[j][2]) );
1412
}
1513
}
1614
```

Project#01/hints/hint2-3.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
To print the interatomic distance matrix, we could just run through its unique values:
2+
```c++
3+
for(int i=0; i < mol.natom; i++)
4+
for(int j=0; j < i; j++)
5+
printf("%d %d %8.5f\n", i, j, R[i][j]);
6+
```
7+
Note the conditional on the index `j` which keeps the code form printing redundant information.

Project#01/hints/hint2-4.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Now that we've written some code to compute bond distances, we can extend the Molecule class a bit more to define our `bond()` function:
2+
```c++
3+
double Molecule::bond(int a, int b)
4+
{
5+
return sqrt( (geom[a][0]-geom[b][0])*(geom[a][0]-geom[b][0])
6+
+ (geom[a][1]-geom[b][1])*(geom[a][1]-geom[b][1])
7+
+ (geom[a][2]-geom[b][2])*(geom[a][2]-geom[b][2]) );
8+
}
9+
```
10+
11+
Note that since `bond()` is a member function of the Molecule class, we can access the geom array with just `geom` rather than `mol.geom`

Project#01/hints/hint3-1.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Each unit vector points from one atom to another, hence each Cartesian component should be treated as a matrix, much like the interatomic distance matrix. For now, let's store the unit vectors in memory, but later we'll write a function to recompute them as needed:
2+
```c++
3+
double **ex = new double* [mol.natom];
4+
double **ey = new double* [mol.natom];
5+
double **ez = new double* [mol.natom];
6+
for(int i=0; i < mol.natom; i++) {
7+
ex[i] = new double[mol.natom];
8+
ey[i] = new double[mol.natom];
9+
ez[i] = new double[mol.natom];
10+
}
11+
```
12+
And don't forget to delete[] them at the end:
13+
```c++
14+
for(int i=0; i < mol.natom; i++) {
15+
delete[] ex[i]; delete[] ey[i]; delete[] ez[i];
16+
}
17+
delete[] ex; delete[] ey; delete[] ez;
18+
```

Project#01/hints/hint3-2.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Be careful about the diagonal elements of the unit vector matrices because of the division by the bond distance between atoms i and j. You should restrict the loops to skip those elements involving a divide-by-zero condition!
2+
```c++
3+
for(i=0; i < mol.natom; i++) {
4+
for(j=0; j < i; j++) {
5+
ex[i][j] = ex[j][i] = -(x[i] - x[j])/R[i][j];
6+
ey[i][j] = ey[j][i] = -(y[i] - y[j])/R[i][j];
7+
ez[i][j] = ez[j][i] = -(z[i] - z[j])/R[i][j];
8+
}
9+
}
10+
```

Project#01/hints/hint3-3.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
If you choose to store the bond angles for later use (not absolutely necessary, as we'll see), you need a three-dimensional array:
2+
```c++
3+
double ***phi = new double** [mol.natom];
4+
for(int i=0; i < mol.natom; i++) {
5+
phi[i] = new double* [mol.natom];
6+
for(int j=0; j < mol.natom; j++) {
7+
phi[i][j] = new double[mol.natom];
8+
}
9+
}
10+
```

Project#01/hints/hint3-4.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
If you print the angle between every possible combination of three atoms, you'll get lots of zeroes and angles between atoms that are far apart. To print mainly interesting angles, use if-else blocks to enforce these restrictions:
2+
```c++
3+
if(i!=j && i!=k && j!=k) { } /* Skip coincidences */
4+
```
5+
6+
and
7+
```c++
8+
if(i < j && j < k && R[i][j] < 4.0 && R[j][k] < 4.0) { } /* Skip atoms far apart (specifically with bond distances > 4.0 bohr) */
9+
```
10+
11+
Alternatively, you can limit the loop structure and just filter out the bond distances:
12+
```c++
13+
for(int i=0; i < mol.natom; i++) {
14+
for(int j=0; j < i; j++) {
15+
for(int k=0; k < j; k++) {
16+
if(R[i][j] < 4.0 && R[j][k] < 4.0) {
17+
...
18+
}
19+
}
20+
}
21+
}
22+
```

Project#01/hints/hint3-5.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
To compute the final bond angle, you must use the `acos()` function (arccos), which, like the `sqrt()` function, is part of the [C math library](http://en.wikipedia.org/wiki/Math.h).

Project#01/hints/step2-solution.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Here we use the Molecule class we've been working on so far, and thanks to the new "bond()" function, we don't need to worry about storing the distances in a matrix:
2+
3+
```c++
4+
#include "molecule.h"
5+
#include <iostream>
6+
#include <fstream>
7+
#include <iomanip>
8+
#include <cstdio>
9+
#include <cmath>
10+
11+
using namespace std;
12+
13+
int main()
14+
{
15+
Molecule mol("geom.dat", 0);
16+
17+
cout << "Number of atoms: " << mol.natom << endl;
18+
cout << "Input Cartesian coordinates:\n";
19+
mol.print_geom();
20+
21+
cout << "Interatomic distances (bohr):\n";
22+
for(int i=0; i < mol.natom; i++)
23+
for(int j=0; j < i; j++)
24+
printf("%d %d %8.5f\n", i, j, mol.bond(i,j));
25+
26+
return 0;
27+
}
28+
```
29+
30+
The output from the above program for the acetaldehyde test case is:
31+
```
32+
Number of atoms: 7
33+
Input Cartesian coordinates:
34+
6 0.000000000000 0.000000000000 0.000000000000
35+
6 0.000000000000 0.000000000000 2.845112131228
36+
8 1.899115961744 0.000000000000 4.139062527233
37+
1 -1.894048308506 0.000000000000 3.747688672216
38+
1 1.942500819960 0.000000000000 -0.701145981971
39+
1 -1.007295466862 -1.669971842687 -0.705916966833
40+
1 -1.007295466862 1.669971842687 -0.705916966833
41+
Interatomic distances (bohr):
42+
1 0 2.84511
43+
2 0 4.55395
44+
2 1 2.29803
45+
3 0 4.19912
46+
3 1 2.09811
47+
3 2 3.81330
48+
4 0 2.06517
49+
4 1 4.04342
50+
4 2 4.84040
51+
4 3 5.87463
52+
5 0 2.07407
53+
5 1 4.05133
54+
5 2 5.89151
55+
5 3 4.83836
56+
5 4 3.38971
57+
6 0 2.07407
58+
6 1 4.05133
59+
6 2 5.89151
60+
6 3 4.83836
61+
6 4 3.38971
62+
6 5 3.33994
63+
```

Project#01/hints/step3-solution.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
Here again we've extended the Molecule class to compute the bond angles without storing them. This also required the addition of a new function to compute the Cartesian unit vectors on the fly, whose declaration we add to the member functions of molecule.h:
2+
```c++
3+
#include <string>
4+
5+
using namespace std;
6+
7+
class Molecule
8+
{
9+
public:
10+
int natom;
11+
int charge;
12+
int *zvals;
13+
double **geom;
14+
string point_group;
15+
16+
void print_geom();
17+
void rotate(double phi);
18+
void translate(double x, double y, double z);
19+
double bond(int atom1, int atom2);
20+
double angle(int atom1, int atom2, int atom3);
21+
double torsion(int atom1, int atom2, int atom3, int atom4);
22+
double unit(int cart, int atom1, int atom2);
23+
24+
Molecule(const char *filename, int q);
25+
~Molecule();
26+
};
27+
```
28+
29+
Now the two new functions added to molecule.cc:
30+
```c++
31+
// Returns the value of the unit vector between atoms a and b
32+
// in the cart direction (cart=0=x, cart=1=y, cart=2=z)
33+
double Molecule::unit(int cart, int a, int b)
34+
{
35+
return -(geom[a][cart]-geom[b][cart])/bond(a,b);
36+
}
37+
38+
// Returns the angle between atoms a, b, and c in radians
39+
double Molecule::angle(int a, int b, int c)
40+
{
41+
return acos(unit(0,b,a) * unit(0,b,c) + unit(1,b,a) * unit(1,b,c) + unit(2,b,a) * unit(2,b,c));
42+
}
43+
```
44+
45+
And finally, the code that makes use of the above:
46+
47+
```c++
48+
#include "molecule.h"
49+
#include <iostream>
50+
#include <fstream>
51+
#include <iomanip>
52+
#include <cstdio>
53+
#include <cmath>
54+
55+
using namespace std;
56+
57+
int main()
58+
{
59+
Molecule mol("geom.dat", 0);
60+
61+
cout << "Number of atoms: " << mol.natom << endl;
62+
cout << "Input Cartesian coordinates:\n";
63+
mol.print_geom();
64+
65+
cout << "Interatomic distances (bohr):\n";
66+
for(int i=0; i < mol.natom; i++)
67+
for(int j=0; j < i; j++)
68+
printf("%d %d %8.5f\n", i, j, mol.bond(i,j));
69+
70+
cout << "\nBond angles:\n";
71+
for(int i=0; i < mol.natom; i++) {
72+
for(int j=0; j < i; j++) {
73+
for(int k=0; k < j; k++) {
74+
if(mol.bond(i,j) < 4.0 && mol.bond(j,k) < 4.0)
75+
printf("%2d-%2d-%2d %10.6f\n", i, j, k, mol.angle(i,j,k)*(180.0/acos(-1.0)));
76+
}
77+
}
78+
}
79+
80+
return 0;
81+
}
82+
```
83+
Note that the value of &pi; is obtained with machine precision using `acos(-1.0)`.
84+
85+
The above code produces the following output for the acetaldehyde test case:
86+
87+
```
88+
Number of atoms: 7
89+
Input Cartesian coordinates:
90+
6 0.000000000000 0.000000000000 0.000000000000
91+
6 0.000000000000 0.000000000000 2.845112131228
92+
8 1.899115961744 0.000000000000 4.139062527233
93+
1 -1.894048308506 0.000000000000 3.747688672216
94+
1 1.942500819960 0.000000000000 -0.701145981971
95+
1 -1.007295466862 -1.669971842687 -0.705916966833
96+
1 -1.007295466862 1.669971842687 -0.705916966833
97+
Interatomic distances (bohr):
98+
1 0 2.84511
99+
2 0 4.55395
100+
2 1 2.29803
101+
3 0 4.19912
102+
3 1 2.09811
103+
3 2 3.81330
104+
4 0 2.06517
105+
4 1 4.04342
106+
4 2 4.84040
107+
4 3 5.87463
108+
5 0 2.07407
109+
5 1 4.05133
110+
5 2 5.89151
111+
5 3 4.83836
112+
5 4 3.38971
113+
6 0 2.07407
114+
6 1 4.05133
115+
6 2 5.89151
116+
6 3 4.83836
117+
6 4 3.38971
118+
6 5 3.33994
119+
120+
Bond angles:
121+
2- 1- 0 124.268308
122+
3- 1- 0 115.479341
123+
3- 2- 1 28.377448
124+
5- 4- 0 35.109529
125+
6- 4- 0 35.109529
126+
6- 5- 0 36.373677
127+
6- 5- 4 60.484476
128+
```

0 commit comments

Comments
 (0)