├── 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 | --------------------------------------------------------------------------------