├── Image-0.png
├── Image-1.png
├── Image-2.png
├── README.md
└── poscar2pdf.cpp
/Image-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-niu/poscar2pdf/HEAD/Image-0.png
--------------------------------------------------------------------------------
/Image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-niu/poscar2pdf/HEAD/Image-1.png
--------------------------------------------------------------------------------
/Image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-niu/poscar2pdf/HEAD/Image-2.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # poscar2pdf
2 | This code calculates the pair distribution function (PDF) from a POSCAR.
3 |
4 | *This code works fine on my Mac. It seems something is wrong if it's run on Linux.*
5 |
6 | ## Installation and Usage
7 | 1. Compile the c++ code: `c++ poscar2pdf.cpp -o poscar2pdf`
8 | 2. Run the code: `poscar2pdf POSCAR 2.5 0.1 6.0` (where 2.5 and 6.0 set the range of radius, and 0.1 is the size of steps).
9 | 3. Plot PDF using whatever visualization software you like with the data file `PDF_POSCAR.txt`. The first row has the titles of each column, which from left to right are the radius, PDFs of pair of elements, and total PDF.
10 |
11 | Example of `PDF_POSCAR.txt`:
12 |
13 |
14 | ## Maths In The Code
15 | ### How to calculate the PDF
16 | To calculate the PDF, or g(r), the following is done by the code:
17 |
18 | 1. Read the input for *r*: *min*, *dr*, *max*.
19 | 2. Expand the POSCAR so that the sphere defined by *max* is entirely covered by the expanded supercell.
20 | 3. There are PDF for any two elements and PDF of the whole structure, which are calculated in different ways.
21 | 1. For the total PDF, consider every atom in the POSCAR, one at a time. Find all atoms in the supercell that are at a distance between *min* and *max* from the considered atom. Count the number of atoms that are between *r* and *r+dr*, where *r = min + n dr*. Repeat for every atom in the POSCAR. Then for each step *n* divide this number by the volume of that thin layer of shell, *4 pi r^2 dr*, and by the number of atoms in the POSCAR. This is g(r) for the entire POSCAR.
22 | 2. For the PDF of two elements, consider every atom of one element in the POSCAR, one at a time. Find all atoms of the other element in the supercell that are at a distance between *min* and *max* from the considered atom. Count the number of atoms that are between *r* and *r+dr*, where *r = min + n dr*. Repeat for every atom of the first element in the POSCAR. Then for each step *n* divide this number by the volume of that thin layer of shell, *4 pi r^2 dr*, and by the number of atoms of the first element in the POSCAR. This is g(r) for the two elements. Repeat for other pair of elements.
23 |
24 | ### How to determine the size of supercell
25 |
26 | POSCAR only contains a single periodic unit that represents the entire bulk material. We must expand this periodic unit cell to a supercell which is big enough to calculate the PDF within the desired range. So here comes the question: how big is the expanded supercell?
27 |
28 | A 3-3-3 supercell has the original periodic unit cell in the center and eight cells surrounding it. The maximum range of the PDF is the radius of the largest sphere inside the supercell whose spherical center is anywhere in the periodic unit cell in the center. Therefore, this radius equals the smallest distance between any two opposite sides of the periodic unit cell. This is only true for the 3-3-3 supercell. If the desired maximum range of PDF is larger than this radius, a supercell larger than 3-3-3 is needed. Let’s put it this way:
29 |
30 |
31 |
32 | where *r\_max* is the maximum range of the desired PDF, *n* is the thickness of periodic unit cell surrounding the one in the center, and *d\_min* is the smallest distance between any two opposite sides of the periodic unit cell.
33 | Now, we first need to calculate *d\_min*. The periodic unit cell is defined by three vectors, or three points plus the origin. The distance between two opposite sides equals the distance from one point to the plane defined by the other two points and the origin. This is a well-defined math problem. Let’s skip to the answers:
34 |
35 |
36 |
37 | where *n\_1* is the normal vector to the plane defined by vector 2 and 3, *d\_1* is the distance from point 1 to the plane defined by vector 2 and 3, etc.
38 |
39 | The size of the supercell is then (2n+1) by (2n+1) by (2n+1).
40 |
--------------------------------------------------------------------------------
/poscar2pdf.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // poscar2pdf.cpp
3 | //
4 | // Created by Changning Niu on 2/9/16.
5 | // Copyright © 2016 Changning Niu. All rights reserved.
6 | // For more information, go to https://github.com/changning/poscar2pdf
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | using namespace std;
19 |
20 | // read_poscar:
21 | // return 1 if reading successful
22 | // return 2 if POSCAR has no line of elements
23 | // return 0 if reading failed
24 | int read_poscar (char *file, string& head, double& dLatConst,
25 | double dArrVec[3][3], string strArrElem[10], int nArrElem[10],
26 | char& cCoord, double dArrAtom[][3], int& nElem, int& nAtom) {
27 | istringstream iss;
28 | ifstream poscar;
29 | string line;
30 | double x, y, z;
31 | int num;
32 |
33 | poscar.open(file);
34 | if (!poscar) return 0;
35 | getline(poscar,head); // line 1
36 | getline(poscar,line);
37 | iss.str(line); // line 2
38 | if (iss >> dLatConst) iss.clear(); // read lattice constant
39 | else return 0;
40 | for (int i = 0; i < 3; i++) { // line 3-5: lattice vectors
41 | getline(poscar,line);
42 | iss.str(line);
43 | if (iss >> x >> y >> z) {
44 | iss.clear();
45 | dArrVec[i][0] = x;
46 | dArrVec[i][1] = y;
47 | dArrVec[i][2] = z;
48 | }
49 | else return 0;
50 | }
51 | getline(poscar,line); // line 6
52 | iss.str(line);
53 | nAtom = 0;
54 | nElem = 0;
55 | if (iss >> num) { // if line 6 has integers
56 | nAtom += num;
57 | while (iss >> num) { // count numberf in line 6
58 | nElem++;
59 | nAtom += num;
60 | }
61 | string strArrMark[10] = {"A","B","C","D","E","F","G","H","I","J"};
62 | for (int i = 0; i < nElem; i++) // Mark artificial element types
63 | strArrElem[i] = strArrMark[i];
64 | }
65 | else { // if line 6 has element types
66 | iss.clear();
67 | iss.str(line);
68 | num = 0;
69 | while (iss >> strArrElem[num]) { // read elements
70 | num++;
71 | nElem++;
72 | }
73 | if (nElem == 0) return 0; // if no element read, return 0
74 | getline(poscar,line);
75 | iss.clear();
76 | iss.str(line);
77 | for (int i = 0; i < nElem; i++) { // read line 7: number for elements
78 | if (iss >> num) {
79 | nArrElem[i] = num;
80 | nAtom += num;
81 | }
82 | else return 0;
83 | }
84 | }
85 | getline(poscar,line);
86 | cCoord = line[0]; // read line 8 first character
87 | if (cCoord == 'S' || cCoord == 's') { // if it's selective relaxation
88 | getline(poscar,line); // ignore this line
89 | cCoord = line[0];
90 | }
91 | for (int i = 0; i < nAtom; i++) {
92 | iss.clear();
93 | getline(poscar,line);
94 | iss.str(line);
95 | if (iss >> x >> y >> z) {
96 | dArrAtom[i][0] = x;
97 | dArrAtom[i][1] = y;
98 | dArrAtom[i][2] = z;
99 | }
100 | else return 0;
101 | }
102 | return 1;
103 | }
104 |
105 | // cart_to_direct:
106 | // Converts atomic coordinates in Cartesian format to Directional format.
107 | // return 1 if successful, 0 if failed.
108 | int cart_to_direct(double dArrVec[3][3], double dArrAtom[][3], int nAtom) {
109 | double det;
110 | double newAtom[nAtom][3];
111 | double rev[3][3];
112 | det = dArrVec[0][0] * dArrVec[1][1] * dArrVec[2][2]
113 | - dArrVec[0][0] * dArrVec[1][2] * dArrVec[2][1]
114 | - dArrVec[0][1] * dArrVec[1][0] * dArrVec[2][2]
115 | + dArrVec[0][1] * dArrVec[1][2] * dArrVec[2][0]
116 | + dArrVec[0][2] * dArrVec[1][0] * dArrVec[2][1]
117 | - dArrVec[0][2] * dArrVec[1][1] * dArrVec[2][0];
118 | rev[0][0] = (dArrVec[1][1] * dArrVec[2][2] - dArrVec[1][2] * dArrVec[2][1]);
119 | rev[0][1] = (dArrVec[0][2] * dArrVec[2][1] - dArrVec[0][1] * dArrVec[2][2]);
120 | rev[0][2] = (dArrVec[0][1] * dArrVec[1][2] - dArrVec[0][2] * dArrVec[1][1]);
121 | rev[1][0] = (dArrVec[1][2] * dArrVec[2][0] - dArrVec[1][0] * dArrVec[2][2]);
122 | rev[1][1] = (dArrVec[0][0] * dArrVec[2][2] - dArrVec[0][2] * dArrVec[2][0]);
123 | rev[1][2] = (dArrVec[0][2] * dArrVec[1][0] - dArrVec[0][0] * dArrVec[1][2]);
124 | rev[2][0] = (dArrVec[1][0] * dArrVec[2][1] - dArrVec[1][1] * dArrVec[2][0]);
125 | rev[2][1] = (dArrVec[0][1] * dArrVec[2][0] - dArrVec[0][0] * dArrVec[2][1]);
126 | rev[2][2] = (dArrVec[0][0] * dArrVec[1][1] - dArrVec[0][1] * dArrVec[1][0]);
127 | for (int i = 0; i < nAtom; i++) {
128 | newAtom[i][0] = dArrAtom[i][0] * rev[0][0] + dArrAtom[i][1] * rev[1][0] + dArrAtom[i][2] * rev[2][0];
129 | newAtom[i][1] = dArrAtom[i][0] * rev[0][1] + dArrAtom[i][1] * rev[1][1] + dArrAtom[i][2] * rev[2][1];
130 | newAtom[i][2] = dArrAtom[i][0] * rev[0][2] + dArrAtom[i][1] * rev[1][2] + dArrAtom[i][2] * rev[2][2];
131 | }
132 | for (int i = 0; i < nAtom; i++) {
133 | dArrAtom[i][0] = newAtom[i][0] / det;
134 | dArrAtom[i][1] = newAtom[i][1] / det;
135 | dArrAtom[i][2] = newAtom[i][2] / det;
136 | }
137 | return 1;
138 | }
139 |
140 | // Bubble sort
141 | // Based on codes from http://www.algolist.net/Algorithms/Sorting/Bubble_sort
142 | void bubbleSort(double arr[], int n) {
143 | bool swapped = true;
144 | int j = 0;
145 | double tmp;
146 | while (swapped) {
147 | swapped = false;
148 | j++;
149 | for (int i = 0; i < n - j; i++) {
150 | if (arr[i] > arr[i + 1]) {
151 | tmp = arr[i];
152 | arr[i] = arr[i + 1];
153 | arr[i + 1] = tmp;
154 | swapped = true;
155 | }
156 | }
157 | }
158 | }
159 |
160 | int main(int argc, char* argv[]) {
161 | double dMax, dMin, dStep; // min/max and step of desired PDF radius
162 | double dMax2, dMin2; // squared min/max for acceleration
163 | char *file; // pointer to POSCAR
164 | int nElem; // number of elements
165 | int nAtom; // number of atoms
166 | string head; // line 1 of POSCAR
167 | double dLatConst; // line 2 of POSCAR
168 | double dArrVec[3][3]; // line 3-5: lattice vectors
169 | string strArrElem[10]; // element names
170 | int nArrElem[10]; // number of atoms of each element
171 | char cCoord; // line 8: directional or cartesian
172 | double dArrAtom[500][3]; // atomic coordinates
173 | double dArrAtom2[500][3]; // atomic coordinates in Cartesian
174 | string strArrAtom[500]; // element of each atom
175 | int count;
176 | double A1, B1, C1, A2, B2, C2, A3, B3, C3;// coefficients A_i, B_i, C_i for finding min d
177 | double d1, d2, d3, dMinCell;// distances between two opposite sides of cell
178 | int nCell; // number of cells outside each side to build supercell
179 | int nPairs; // number of pairs of elements
180 | int nData; // number of data points of PDF
181 | int nSCatom; // number of atoms in the supercell
182 | double dTemp; // temperary usage
183 |
184 | // Check input
185 | if (argc != 5) {
186 | cout << "Usage: poscar2pdf POSCAR 0.1 0.1 4.0\n";
187 | exit(1);
188 | }
189 | file = argv[1];
190 | dMin = atof(argv[2]);
191 | dStep = atof(argv[3]);
192 | dMax = atof(argv[4]);
193 | if (dMin == 0.0) {
194 | cout << "Min value must be larger than 0.\n";
195 | exit(1);
196 | }
197 | // Read POSCAR
198 | if (read_poscar(file, head, dLatConst, dArrVec, strArrElem, nArrElem, cCoord, dArrAtom, nElem, nAtom) == 0) {
199 | cout << "Errors occurred when reading POSCAR.\n";
200 | exit(1);
201 | }
202 | else if (cCoord == 'C' && cCoord == 'c') {
203 | cart_to_direct(dArrVec, dArrAtom, nAtom);
204 | }
205 |
206 | // Get atomic species for each atom
207 | count = 0;
208 | for (int i = 0; i < nElem; i++) {
209 | for (int j = 0; j < nArrElem[i]; j++) {
210 | strArrAtom[count++] = strArrElem[i];
211 | }
212 | }
213 |
214 | // If (dMax - dMin) / dStep is not integer, decrease dMax slightly
215 | nData = (int)((dMax - dMin) / dStep) + 1;
216 | dMax = (nData - 1) * dStep + dMin;
217 | dMin2 = dMin * dMin / dLatConst / dLatConst;
218 | dMax2 = dMax * dMax / dLatConst / dLatConst;
219 | if (nData > 10000) {
220 | cout << "Number of PDF data points exceeds 10000. Change the code.\n";
221 | exit(1);
222 | }
223 |
224 | // Determine the size of supercell and expand by Cartesian
225 | A1 = dArrVec[1][1] * dArrVec[2][2] - dArrVec[2][1] * dArrVec[1][2];
226 | B1 = dArrVec[2][1] * dArrVec[0][2] - dArrVec[0][1] * dArrVec[2][2];
227 | C1 = dArrVec[0][1] * dArrVec[1][2] - dArrVec[1][1] * dArrVec[0][2];
228 | A2 = dArrVec[1][0] * dArrVec[2][2] - dArrVec[2][0] * dArrVec[1][2];
229 | B2 = dArrVec[2][0] * dArrVec[0][2] - dArrVec[0][0] * dArrVec[2][2];
230 | C2 = dArrVec[0][0] * dArrVec[1][2] - dArrVec[1][0] * dArrVec[0][2];
231 | A3 = dArrVec[1][0] * dArrVec[2][1] - dArrVec[2][0] * dArrVec[1][1];
232 | B3 = dArrVec[2][0] * dArrVec[0][1] - dArrVec[0][0] * dArrVec[2][1];
233 | C3 = dArrVec[0][0] * dArrVec[1][1] - dArrVec[1][0] * dArrVec[0][1];
234 | d1 = abs(A1 * dArrVec[0][0] + B1 * dArrVec[0][1] + C1 * dArrVec[0][2]);
235 | d2 = abs(A2 * dArrVec[1][0] + B2 * dArrVec[1][1] + C2 * dArrVec[1][2]);
236 | d3 = abs(A3 * dArrVec[2][0] + B3 * dArrVec[2][1] + C3 * dArrVec[2][2]);
237 | dMinCell = fmin(d1, fmin(d2, d3)) * dLatConst;
238 | nCell = (int)(dMax / dMinCell + 1);
239 | nSCatom = nAtom * (int)(pow((nCell * 2 + 1), 3));
240 | if (nSCatom > 100000) {
241 | cout << "Supercell needs over 100000 atoms. Change the code to suit your calculation.\n";
242 | exit(1);
243 | }
244 | double dArrSC[100000][3]; // declare array containing supercell coordinates
245 | double dArrSCr[100000]; // declare array to hold temp radii from one center atom
246 | string strArrSC[100000]; // declare array containing elements in supercell
247 | count = 0;
248 | for (int i = -nCell; i <= nCell; i++) {
249 | for (int j = -nCell; j <= nCell; j++) {
250 | for (int k = -nCell; k <= nCell; k++) {
251 | for (int m = 0; m < nAtom; m++) {
252 | dArrSC[count][0] = (dArrAtom[m][0] + i) * dArrVec[0][0]
253 | + (dArrAtom[m][1] + j) * dArrVec[1][0]
254 | + (dArrAtom[m][2] + k) * dArrVec[2][0];
255 | dArrSC[count][1] = (dArrAtom[m][0] + i) * dArrVec[0][1]
256 | + (dArrAtom[m][1] + j) * dArrVec[1][1]
257 | + (dArrAtom[m][2] + k) * dArrVec[2][1];
258 | dArrSC[count][2] = (dArrAtom[m][0] + i) * dArrVec[0][2]
259 | + (dArrAtom[m][1] + j) * dArrVec[1][2]
260 | + (dArrAtom[m][2] + k) * dArrVec[2][2];
261 | strArrSC[count] = strArrAtom[m];
262 | count++;
263 | }
264 | }
265 | }
266 | }
267 | if (count != nSCatom) {
268 | cout << "Error when generating supercell.\n";
269 | exit(1);
270 | }
271 |
272 | // Convert atomic coordinates to Cartesian
273 | for (int i = 0; i < nAtom; i++) {
274 | dArrAtom2[i][0] = dArrAtom[i][0] * dArrVec[0][0]
275 | + dArrAtom[i][1] * dArrVec[1][0]
276 | + dArrAtom[i][2] * dArrVec[2][0];
277 | dArrAtom2[i][1] = dArrAtom[i][0] * dArrVec[0][1]
278 | + dArrAtom[i][1] * dArrVec[1][1]
279 | + dArrAtom[i][2] * dArrVec[2][1];
280 | dArrAtom2[i][2] = dArrAtom[i][0] * dArrVec[0][2]
281 | + dArrAtom[i][1] * dArrVec[1][2]
282 | + dArrAtom[i][2] * dArrVec[2][2];
283 | }
284 |
285 | // Calculate pair distribution function
286 | nPairs = (nElem + 1) * nElem / 2;
287 | if (nPairs > 20) {
288 | cout << "Pair of elements exceeds 20. Change the code.\n";
289 | exit(1);
290 | }
291 | double dArrPDF[10000][20]; // declare the array containing PDF data
292 | for (int i = 0; i < nData; i++) {
293 | dArrPDF[i][0] = dMin + i * dStep; // first column is r
294 | for (int j = 1; j <= nPairs + 1; j++) {
295 | dArrPDF[i][j] = 0.0; // initialize PDF data
296 | }
297 | }
298 | int count2 = 0; // used to count pairs
299 | for (int i = 0; i < nElem; i++) { // This and next loop scan all pairs
300 | for (int j = 0; j <= i; j++) {
301 | count2++;
302 | for (int atom = 0; atom < nAtom; atom++) { // This loop scan all atoms as center atom
303 | if (strArrAtom[atom] == strArrElem[i]) {
304 | count = 0; // used to count number of atoms of supercell in the range
305 | for (int SCatom = 0; SCatom < nSCatom; SCatom++) {// This loop scan all supercell atoms
306 | if (strArrSC[SCatom] == strArrElem[j]) { // if this is the pair of elements we want
307 | dTemp = pow((dArrSC[SCatom][0]-dArrAtom2[atom][0]), 2)
308 | + pow((dArrSC[SCatom][1]-dArrAtom2[atom][1]), 2)
309 | + pow((dArrSC[SCatom][2]-dArrAtom2[atom][2]), 2);
310 | if (dTemp <= dMax2 && dTemp >= dMin2) {
311 | dArrSCr[count] = sqrt(dTemp) * dLatConst;
312 | count++;
313 | }
314 | }
315 | }
316 | bubbleSort(dArrSCr, count);
317 | int num = 0;
318 | for (int m = 0; m < count; m++) { // check each recorded radius
319 | for (int n = num; n < nData; n++) {
320 | if (dArrSCr[m] >= dArrPDF[n][0] && dArrSCr[m] < dArrPDF[n][0] + dStep) {
321 | dArrPDF[n][count2] += 1.0;
322 | num = n;
323 | break;
324 | }
325 | }
326 | }
327 | }
328 | }
329 | for (int k = 0; k < nData; k++) { // divide counted integers by volume and number of center atom
330 | dArrPDF[k][count2] /= (4*3.14159*pow(dArrPDF[k][0],2)*dStep*nArrElem[i]);
331 | }
332 | }
333 | }
334 |
335 | // Calculate total PDF
336 | for (int i = 0; i < nAtom; i++) {
337 | count = 0;
338 | for (int j = 0; j < nSCatom; j++) {
339 | dTemp = pow((dArrSC[j][0]-dArrAtom2[i][0]), 2)
340 | + pow((dArrSC[j][1]-dArrAtom2[i][1]), 2)
341 | + pow((dArrSC[j][2]-dArrAtom2[i][2]), 2);
342 | if (dTemp <= dMax2 && dTemp >= dMin2) {
343 | dArrSCr[count] = sqrt(dTemp) * dLatConst;
344 | count++;
345 | }
346 | }
347 | bubbleSort(dArrSCr, count);
348 | int num = 0;
349 | for (int m = 0; m < count; m++) {
350 | for (int n = num; n < nData; n++) {
351 | if (dArrSCr[m] >= dArrPDF[n][0] && dArrSCr[m] < dArrPDF[n][0] + dStep) {
352 | dArrPDF[n][nPairs+1] += 1.0;
353 | num = n;
354 | break;
355 | }
356 | }
357 | }
358 | }
359 | for (int k = 0; k < nData; k++) {
360 | dArrPDF[k][nPairs+1] /= (4*3.14159*pow(dArrPDF[k][0],2)*dStep*nAtom);
361 | }
362 |
363 | // Output
364 | ofstream os;
365 | char output[] = "PDF_";
366 | strcat(output, argv[1]);
367 | strcat(output, ".txt");
368 | os.open(output);
369 | os << " r ";
370 | for (int i = 0; i < nElem; i++) {
371 | for (int j = 0; j <= i; j++) {
372 | os << setw(5) << strArrElem[i];
373 | os << "-";
374 | os << setw(2) << strArrElem[j];
375 | }
376 | }
377 | os << " Total "<< "\n";
378 | for (int i = 0; i < nData; i++) {
379 | os << setw(5) << fixed << setprecision(3) << dArrPDF[i][0];
380 | for (int j = 1; j <= nPairs+1; j++) {
381 | os << setw(8) << fixed << setprecision(4) << dArrPDF[i][j];
382 | }
383 | os << "\n";
384 | }
385 | os.close();
386 | return 0;
387 | }
388 |
--------------------------------------------------------------------------------