| 1 | /********************************************************************** |
|---|
| 2 | Xtal - Wrapper for Structure to ease work with crystals. |
|---|
| 3 | |
|---|
| 4 | Copyright (C) 2009-2011 by David C. Lonie |
|---|
| 5 | |
|---|
| 6 | This program is free software; you can redistribute it and/or modify |
|---|
| 7 | it under the terms of the GNU General Public License as published by |
|---|
| 8 | the Free Software Foundation version 2 of the License. |
|---|
| 9 | |
|---|
| 10 | This program is distributed in the hope that it will be useful, |
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | GNU General Public License for more details. |
|---|
| 14 | ***********************************************************************/ |
|---|
| 15 | |
|---|
| 16 | #include <xtalopt/structures/xtal.h> |
|---|
| 17 | |
|---|
| 18 | #include <xtalopt/xtalopt.h> |
|---|
| 19 | |
|---|
| 20 | #include <avogadro/primitive.h> |
|---|
| 21 | #include <avogadro/molecule.h> |
|---|
| 22 | #include <avogadro/atom.h> |
|---|
| 23 | |
|---|
| 24 | #include <globalsearch/macros.h> |
|---|
| 25 | #include <globalsearch/obeigenconv.h> |
|---|
| 26 | #include <globalsearch/stablecomparison.h> |
|---|
| 27 | |
|---|
| 28 | #include <openbabel/generic.h> |
|---|
| 29 | #include <openbabel/forcefield.h> |
|---|
| 30 | |
|---|
| 31 | extern "C" { |
|---|
| 32 | #include <spglib/spglib.h> |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | #include <xtalcomp/xtalcomp.h> |
|---|
| 36 | |
|---|
| 37 | #include <Eigen/LU> |
|---|
| 38 | |
|---|
| 39 | #include <QtAlgorithms> |
|---|
| 40 | |
|---|
| 41 | #include <QtCore/QFile> |
|---|
| 42 | #include <QtCore/QDebug> |
|---|
| 43 | #include <QtCore/QRegExp> |
|---|
| 44 | #include <QtCore/QStringList> |
|---|
| 45 | |
|---|
| 46 | #define DEBUG_MATRIX(m) printf("| %9.5f %9.5f %9.5f |\n" \ |
|---|
| 47 | "| %9.5f %9.5f %9.5f |\n" \ |
|---|
| 48 | "| %9.5f %9.5f %9.5f |\n", \ |
|---|
| 49 | (m)(0,0), (m)(0,1), (m)(0,2), \ |
|---|
| 50 | (m)(1,0), (m)(1,1), (m)(1,2), \ |
|---|
| 51 | (m)(2,0), (m)(2,1), (m)(2,2)) |
|---|
| 52 | |
|---|
| 53 | using namespace std; |
|---|
| 54 | using namespace OpenBabel; |
|---|
| 55 | using namespace Avogadro; |
|---|
| 56 | using namespace Eigen; |
|---|
| 57 | |
|---|
| 58 | namespace XtalOpt { |
|---|
| 59 | |
|---|
| 60 | Xtal::Xtal(QObject *parent) : |
|---|
| 61 | Structure(parent) |
|---|
| 62 | { |
|---|
| 63 | ctor(parent); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | Xtal::Xtal(double A, double B, double C, |
|---|
| 67 | double Alpha, double Beta, double Gamma, |
|---|
| 68 | QObject *parent) : |
|---|
| 69 | Structure(parent) |
|---|
| 70 | { |
|---|
| 71 | ctor(parent); |
|---|
| 72 | setCellInfo(A, B, C, Alpha, Beta, Gamma); |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | // Actual constructor: |
|---|
| 76 | void Xtal::ctor(QObject *parent) |
|---|
| 77 | { |
|---|
| 78 | if (!m_mixMatrices.size()) { |
|---|
| 79 | generateValidCOBs(); |
|---|
| 80 | } |
|---|
| 81 | m_spgNumber = 231; |
|---|
| 82 | this->setOBUnitCell(new OpenBabel::OBUnitCell); |
|---|
| 83 | // Openbabel seems to be fond of making unfounded assumptions that |
|---|
| 84 | // break things. This fixes one of them. |
|---|
| 85 | this->cell()->SetSpaceGroup(0); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | Xtal::~Xtal() { |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | void Xtal::setVolume(double Volume) { |
|---|
| 92 | |
|---|
| 93 | // Get scaling factor |
|---|
| 94 | double factor = pow(Volume/cell()->GetCellVolume(), 1.0/3.0); // Cube root |
|---|
| 95 | |
|---|
| 96 | // Store position of atoms in fractional units |
|---|
| 97 | QList<Atom*> atomList = atoms(); |
|---|
| 98 | QList<Vector3d*> fracCoordsList; |
|---|
| 99 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 100 | fracCoordsList.append(cartToFrac(atomList.at(i)->pos())); |
|---|
| 101 | |
|---|
| 102 | // Scale cell |
|---|
| 103 | setCellInfo(factor * cell()->GetA(), |
|---|
| 104 | factor * cell()->GetB(), |
|---|
| 105 | factor * cell()->GetC(), |
|---|
| 106 | cell()->GetAlpha(), |
|---|
| 107 | cell()->GetBeta(), |
|---|
| 108 | cell()->GetGamma()); |
|---|
| 109 | |
|---|
| 110 | // Recalculate coordinates: |
|---|
| 111 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 112 | atomList.at(i)->setPos(fracToCart(*(fracCoordsList.at(i)))); |
|---|
| 113 | |
|---|
| 114 | // Free memory |
|---|
| 115 | qDeleteAll(fracCoordsList); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | void Xtal::rescaleCell(double a, double b, double c, |
|---|
| 119 | double alpha, double beta, double gamma) |
|---|
| 120 | { |
|---|
| 121 | if (!a && !b && !c && !alpha && !beta && !gamma) { |
|---|
| 122 | return; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | this->rotateCellAndCoordsToStandardOrientation(); |
|---|
| 126 | |
|---|
| 127 | // Store position of atoms in fractional units |
|---|
| 128 | QList<Atom*> atomList = atoms(); |
|---|
| 129 | QList<Vector3d> fracCoordsList; |
|---|
| 130 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 131 | fracCoordsList.append(cartToFrac(*atomList.at(i)->pos())); |
|---|
| 132 | |
|---|
| 133 | double nA = getA(); |
|---|
| 134 | double nB = getB(); |
|---|
| 135 | double nC = getC(); |
|---|
| 136 | double nAlpha = getAlpha(); |
|---|
| 137 | double nBeta = getBeta(); |
|---|
| 138 | double nGamma = getGamma(); |
|---|
| 139 | |
|---|
| 140 | // Set angles and reset volume |
|---|
| 141 | if (alpha || beta || gamma) { |
|---|
| 142 | double volume = getVolume(); |
|---|
| 143 | if (alpha) nAlpha = alpha; |
|---|
| 144 | if (beta) nBeta = beta; |
|---|
| 145 | if (gamma) nGamma = gamma; |
|---|
| 146 | setCellInfo(nA, |
|---|
| 147 | nB, |
|---|
| 148 | nC, |
|---|
| 149 | nAlpha, |
|---|
| 150 | nBeta, |
|---|
| 151 | nGamma); |
|---|
| 152 | setVolume(volume); |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | if (a || b || c) { |
|---|
| 156 | // Initialize variables |
|---|
| 157 | double scale_primary, scale_secondary, scale_tertiary; |
|---|
| 158 | |
|---|
| 159 | // Set lengths while preserving volume |
|---|
| 160 | // Case one length is static |
|---|
| 161 | if (a && !b && !c) { // A |
|---|
| 162 | scale_primary = a / nA; |
|---|
| 163 | scale_secondary = pow(scale_primary, 0.5); |
|---|
| 164 | nA = a; |
|---|
| 165 | nB *= scale_secondary; |
|---|
| 166 | nC *= scale_secondary; |
|---|
| 167 | } |
|---|
| 168 | else if (!a && b && !c) { // B |
|---|
| 169 | scale_primary = b / nB; |
|---|
| 170 | scale_secondary = pow(scale_primary, 0.5); |
|---|
| 171 | nB = b; |
|---|
| 172 | nA *= scale_secondary; |
|---|
| 173 | nC *= scale_secondary; |
|---|
| 174 | } |
|---|
| 175 | else if (!a && !b && c) { // C |
|---|
| 176 | scale_primary = c / nC; |
|---|
| 177 | scale_secondary = pow(scale_primary, 0.5); |
|---|
| 178 | nC = c; |
|---|
| 179 | nA *= scale_secondary; |
|---|
| 180 | nB *= scale_secondary; |
|---|
| 181 | } |
|---|
| 182 | // Case two lengths are static |
|---|
| 183 | else if (a && b && !c) { // AB |
|---|
| 184 | scale_primary = a / nA; |
|---|
| 185 | scale_secondary = b / nB; |
|---|
| 186 | scale_tertiary = scale_primary * scale_secondary; |
|---|
| 187 | nA = a; |
|---|
| 188 | nB = b; |
|---|
| 189 | nC *= scale_tertiary; |
|---|
| 190 | } |
|---|
| 191 | else if (a && !b && c) { // AC |
|---|
| 192 | scale_primary = a / nA; |
|---|
| 193 | scale_secondary = c / nC; |
|---|
| 194 | scale_tertiary = scale_primary * scale_secondary; |
|---|
| 195 | nA = a; |
|---|
| 196 | nC = c; |
|---|
| 197 | nB *= scale_tertiary; |
|---|
| 198 | } |
|---|
| 199 | else if (!a && b && c) { // BC |
|---|
| 200 | scale_primary = c / nC; |
|---|
| 201 | scale_secondary = b / nB; |
|---|
| 202 | scale_tertiary = scale_primary * scale_secondary; |
|---|
| 203 | nC = c; |
|---|
| 204 | nB = b; |
|---|
| 205 | nA *= scale_tertiary; |
|---|
| 206 | } |
|---|
| 207 | // Case three lengths are static |
|---|
| 208 | else if (a && b && c) { // ABC |
|---|
| 209 | nA = a; |
|---|
| 210 | nB = b; |
|---|
| 211 | nC = c; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | // Update unit cell |
|---|
| 215 | setCellInfo(nA, |
|---|
| 216 | nB, |
|---|
| 217 | nC, |
|---|
| 218 | nAlpha, |
|---|
| 219 | nBeta, |
|---|
| 220 | nGamma); |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | // Recalculate coordinates: |
|---|
| 224 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 225 | atomList.at(i)->setPos(fracToCart(fracCoordsList.at(i))); |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | bool Xtal::niggliReduce(const unsigned int iterations) |
|---|
| 229 | { |
|---|
| 230 | // Cache volume for later sanity checks |
|---|
| 231 | const double origVolume = cell()->GetCellVolume(); |
|---|
| 232 | |
|---|
| 233 | // Grab lattice vectors |
|---|
| 234 | const std::vector<OpenBabel::vector3> obvecs = cell()->GetCellVectors(); |
|---|
| 235 | const Eigen::Vector3d v1 (OB2Eigen(obvecs[0])); |
|---|
| 236 | const Eigen::Vector3d v2 (OB2Eigen(obvecs[1])); |
|---|
| 237 | const Eigen::Vector3d v3 (OB2Eigen(obvecs[2])); |
|---|
| 238 | |
|---|
| 239 | // Compute characteristic (step 0) |
|---|
| 240 | double A = v1.squaredNorm(); |
|---|
| 241 | double B = v2.squaredNorm(); |
|---|
| 242 | double C = v3.squaredNorm(); |
|---|
| 243 | double xi = 2*v2.dot(v3); |
|---|
| 244 | double eta = 2*v1.dot(v3); |
|---|
| 245 | double zeta = 2*v1.dot(v2); |
|---|
| 246 | |
|---|
| 247 | // Return value |
|---|
| 248 | bool ret = false; |
|---|
| 249 | |
|---|
| 250 | // comparison tolerance |
|---|
| 251 | double tol = STABLE_COMP_TOL * pow(this->getVolume(), 1.0/3.0); |
|---|
| 252 | |
|---|
| 253 | // Initialize change of basis matrices: |
|---|
| 254 | // |
|---|
| 255 | // Although the reduction algorithm produces quantities directly |
|---|
| 256 | // relatible to a,b,c,alpha,beta,gamma, we will calculate a change |
|---|
| 257 | // of basis matrix to use instead, and discard A, B, C, xi, eta, |
|---|
| 258 | // zeta. By multiplying the change of basis matrix against the |
|---|
| 259 | // current cell matrix, we avoid the problem of handling the |
|---|
| 260 | // orientation matrix already present in the cell. The inverse of |
|---|
| 261 | // this matrix can also be used later to convert the atomic |
|---|
| 262 | // positions. |
|---|
| 263 | // tmpMat is used to build other matrices |
|---|
| 264 | Eigen::Matrix3d tmpMat; |
|---|
| 265 | |
|---|
| 266 | // Cache static matrices: |
|---|
| 267 | |
|---|
| 268 | // Swap x,y (Used in Step 1). Negatives ensure proper sign of final |
|---|
| 269 | // determinant. |
|---|
| 270 | tmpMat << 0,-1,0, -1,0,0, 0,0,-1; |
|---|
| 271 | const Eigen::Matrix3d C1(tmpMat); |
|---|
| 272 | // Swap y,z (Used in Step 2). Negatives ensure proper sign of final |
|---|
| 273 | // determinant |
|---|
| 274 | tmpMat << -1,0,0, 0,0,-1, 0,-1,0; |
|---|
| 275 | const Eigen::Matrix3d C2(tmpMat); |
|---|
| 276 | // For step 8: |
|---|
| 277 | tmpMat << 1,0,1, 0,1,1, 0,0,1; |
|---|
| 278 | const Eigen::Matrix3d C8(tmpMat); |
|---|
| 279 | |
|---|
| 280 | // initial change of basis matrix |
|---|
| 281 | tmpMat << 1,0,0, 0,1,0, 0,0,1; |
|---|
| 282 | Eigen::Matrix3d cob(tmpMat); |
|---|
| 283 | |
|---|
| 284 | // Enable debugging output here: |
|---|
| 285 | //#define NIGGLI_DEBUG(step) qDebug() << iter << step << A << B << C << xi << eta << zeta << cob.determinant(); \ |
|---|
| 286 | //std::cout << cob << std::endl; |
|---|
| 287 | #define NIGGLI_DEBUG(step) |
|---|
| 288 | |
|---|
| 289 | unsigned int iter; |
|---|
| 290 | for (iter = 0; iter < iterations; ++iter) { |
|---|
| 291 | Q_ASSERT(fabs(cob.determinant() - 1.0) < 1e-5); |
|---|
| 292 | // Step 1: |
|---|
| 293 | if ( |
|---|
| 294 | StableComp::gt(A, B, tol) |
|---|
| 295 | || ( |
|---|
| 296 | StableComp::eq(A, B, tol) |
|---|
| 297 | && |
|---|
| 298 | StableComp::gt(fabs(xi), fabs(eta), tol) |
|---|
| 299 | ) |
|---|
| 300 | ) { |
|---|
| 301 | cob *= C1; |
|---|
| 302 | qSwap(A, B); |
|---|
| 303 | qSwap(xi, eta); |
|---|
| 304 | NIGGLI_DEBUG(1); |
|---|
| 305 | ++iter; |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | // Step 2: |
|---|
| 309 | if ( |
|---|
| 310 | StableComp::gt(B, C, tol) |
|---|
| 311 | || ( |
|---|
| 312 | StableComp::eq(B, C, tol) |
|---|
| 313 | && |
|---|
| 314 | StableComp::gt(fabs(eta), fabs(zeta), tol) |
|---|
| 315 | ) |
|---|
| 316 | ) { |
|---|
| 317 | cob *= C2; |
|---|
| 318 | qSwap(B, C); |
|---|
| 319 | qSwap(eta, zeta); |
|---|
| 320 | NIGGLI_DEBUG(2); |
|---|
| 321 | continue; |
|---|
| 322 | } |
|---|
| 323 | |
|---|
| 324 | // Step 3: |
|---|
| 325 | // Use exact comparisons in steps 3 and 4. |
|---|
| 326 | if (xi*eta*zeta > 0) { |
|---|
| 327 | // Update change of basis matrix: |
|---|
| 328 | tmpMat << |
|---|
| 329 | StableComp::sign(xi),0,0, |
|---|
| 330 | 0,StableComp::sign(eta),0, |
|---|
| 331 | 0,0,StableComp::sign(zeta); |
|---|
| 332 | cob *= tmpMat; |
|---|
| 333 | |
|---|
| 334 | // Update characteristic |
|---|
| 335 | xi = fabs(xi); |
|---|
| 336 | eta = fabs(eta); |
|---|
| 337 | zeta = fabs(zeta); |
|---|
| 338 | NIGGLI_DEBUG(3); |
|---|
| 339 | ++iter; |
|---|
| 340 | } |
|---|
| 341 | |
|---|
| 342 | // Step 4: |
|---|
| 343 | // Use exact comparisons for steps 3 and 4 |
|---|
| 344 | else { // either step 3 or 4 should run |
|---|
| 345 | // Update change of basis matrix: |
|---|
| 346 | double *p = NULL; |
|---|
| 347 | double i = 1; |
|---|
| 348 | double j = 1; |
|---|
| 349 | double k = 1; |
|---|
| 350 | if (xi > 0) { |
|---|
| 351 | i = -1; |
|---|
| 352 | } |
|---|
| 353 | else if (!(xi < 0)) { |
|---|
| 354 | p = &i; |
|---|
| 355 | } |
|---|
| 356 | if (eta > 0) { |
|---|
| 357 | j = -1; |
|---|
| 358 | } |
|---|
| 359 | else if (!(eta < 0)) { |
|---|
| 360 | p = &j; |
|---|
| 361 | } |
|---|
| 362 | if (zeta > 0) { |
|---|
| 363 | k = -1; |
|---|
| 364 | } |
|---|
| 365 | else if (!(zeta < 0)) { |
|---|
| 366 | p = &k; |
|---|
| 367 | } |
|---|
| 368 | if (i*j*k < 0) { |
|---|
| 369 | if (!p) { |
|---|
| 370 | std::cerr << "XtalComp warning: one of the input structures " |
|---|
| 371 | "contains a lattice that is confusing the Niggli " |
|---|
| 372 | "reduction algorithm. Try making a small perturbation " |
|---|
| 373 | "(approx. 2 orders of magnitude smaller than the " |
|---|
| 374 | "tolerance) to the input lattices and try again. The " |
|---|
| 375 | "results of this comparison should not be relied upon." |
|---|
| 376 | "\n"; |
|---|
| 377 | return false; |
|---|
| 378 | } |
|---|
| 379 | *p = -1; |
|---|
| 380 | } |
|---|
| 381 | tmpMat <<i,0,0, 0,j,0, 0,0,k; |
|---|
| 382 | cob *= tmpMat; |
|---|
| 383 | |
|---|
| 384 | // Update characteristic |
|---|
| 385 | xi = -fabs(xi); |
|---|
| 386 | eta = -fabs(eta); |
|---|
| 387 | zeta = -fabs(zeta); |
|---|
| 388 | NIGGLI_DEBUG(4); |
|---|
| 389 | ++iter; |
|---|
| 390 | } |
|---|
| 391 | |
|---|
| 392 | // Step 5: |
|---|
| 393 | if (StableComp::gt(fabs(xi), B, tol) |
|---|
| 394 | || (StableComp::eq(xi, B, tol) |
|---|
| 395 | && StableComp::lt(2*eta, zeta, tol) |
|---|
| 396 | ) |
|---|
| 397 | || (StableComp::eq(xi, -B, tol) |
|---|
| 398 | && StableComp::lt(zeta, 0, tol) |
|---|
| 399 | ) |
|---|
| 400 | ) { |
|---|
| 401 | double signXi = StableComp::sign(xi); |
|---|
| 402 | // Update change of basis matrix: |
|---|
| 403 | tmpMat << 1,0,0, 0,1,-signXi, 0,0,1; |
|---|
| 404 | cob *= tmpMat; |
|---|
| 405 | |
|---|
| 406 | // Update characteristic |
|---|
| 407 | C = B + C - xi*signXi; |
|---|
| 408 | eta = eta - zeta*signXi; |
|---|
| 409 | xi = xi - 2*B*signXi; |
|---|
| 410 | NIGGLI_DEBUG(5); |
|---|
| 411 | continue; |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| 414 | // Step 6: |
|---|
| 415 | if (StableComp::gt(fabs(eta), A, tol) |
|---|
| 416 | || (StableComp::eq(eta, A, tol) |
|---|
| 417 | && StableComp::lt(2*xi, zeta, tol) |
|---|
| 418 | ) |
|---|
| 419 | || (StableComp::eq(eta, -A, tol) |
|---|
| 420 | && StableComp::lt(zeta, 0, tol) |
|---|
| 421 | ) |
|---|
| 422 | ) { |
|---|
| 423 | double signEta = StableComp::sign(eta); |
|---|
| 424 | // Update change of basis matrix: |
|---|
| 425 | tmpMat << 1,0,-signEta, 0,1,0, 0,0,1; |
|---|
| 426 | cob *= tmpMat; |
|---|
| 427 | |
|---|
| 428 | // Update characteristic |
|---|
| 429 | C = A + C - eta*signEta; |
|---|
| 430 | xi = xi - zeta*signEta; |
|---|
| 431 | eta = eta - 2*A*signEta; |
|---|
| 432 | NIGGLI_DEBUG(6); |
|---|
| 433 | continue; |
|---|
| 434 | } |
|---|
| 435 | |
|---|
| 436 | // Step 7: |
|---|
| 437 | if (StableComp::gt(fabs(zeta), A, tol) |
|---|
| 438 | || (StableComp::eq(zeta, A, tol) |
|---|
| 439 | && StableComp::lt(2*xi, eta, tol) |
|---|
| 440 | ) |
|---|
| 441 | || (StableComp::eq(zeta, -A, tol) |
|---|
| 442 | && StableComp::lt(eta, 0, tol) |
|---|
| 443 | ) |
|---|
| 444 | ) { |
|---|
| 445 | double signZeta = StableComp::sign(zeta); |
|---|
| 446 | // Update change of basis matrix: |
|---|
| 447 | tmpMat << 1,-signZeta,0, 0,1,0, 0,0,1; |
|---|
| 448 | cob *= tmpMat; |
|---|
| 449 | |
|---|
| 450 | // Update characteristic |
|---|
| 451 | B = A + B - zeta*signZeta; |
|---|
| 452 | xi = xi - eta*signZeta; |
|---|
| 453 | zeta = zeta - 2*A*signZeta; |
|---|
| 454 | NIGGLI_DEBUG(7); |
|---|
| 455 | continue; |
|---|
| 456 | } |
|---|
| 457 | |
|---|
| 458 | // Step 8: |
|---|
| 459 | double sumAllButC = A + B + xi + eta + zeta; |
|---|
| 460 | if (StableComp::lt(sumAllButC, 0, tol) |
|---|
| 461 | || (StableComp::eq(sumAllButC, 0, tol) |
|---|
| 462 | && StableComp::gt(2*(A+eta)+zeta, 0, tol) |
|---|
| 463 | ) |
|---|
| 464 | ) { |
|---|
| 465 | // Update change of basis matrix: |
|---|
| 466 | cob *= C8; |
|---|
| 467 | |
|---|
| 468 | // Update characteristic |
|---|
| 469 | C = sumAllButC + C; |
|---|
| 470 | xi = 2*B + xi + zeta; |
|---|
| 471 | eta = 2*A + eta + zeta; |
|---|
| 472 | NIGGLI_DEBUG(8); |
|---|
| 473 | continue; |
|---|
| 474 | } |
|---|
| 475 | |
|---|
| 476 | // Done! |
|---|
| 477 | NIGGLI_DEBUG(999); |
|---|
| 478 | ret = true; |
|---|
| 479 | break; |
|---|
| 480 | } |
|---|
| 481 | |
|---|
| 482 | // No change, already reduced. Just return. |
|---|
| 483 | if (iter == 0) { |
|---|
| 484 | return true; |
|---|
| 485 | } |
|---|
| 486 | |
|---|
| 487 | // iterations exceeded |
|---|
| 488 | if (!ret) { |
|---|
| 489 | return false; |
|---|
| 490 | } |
|---|
| 491 | |
|---|
| 492 | Q_ASSERT_X(cob.determinant() == 1, Q_FUNC_INFO, |
|---|
| 493 | "Determinant of change of basis matrix must be 1."); |
|---|
| 494 | |
|---|
| 495 | // Update cell |
|---|
| 496 | setCellInfo( Eigen2OB(cob).transpose() * cell()->GetCellMatrix()); |
|---|
| 497 | |
|---|
| 498 | // Check that volume has not changed |
|---|
| 499 | Q_ASSERT_X(StableComp::eq(origVolume, cell()->GetCellVolume(), tol), |
|---|
| 500 | Q_FUNC_INFO, "Cell volume changed during Niggli reduction."); |
|---|
| 501 | |
|---|
| 502 | // Rotate and wrap |
|---|
| 503 | rotateCellAndCoordsToStandardOrientation(); |
|---|
| 504 | wrapAtomsToCell(); |
|---|
| 505 | return true; |
|---|
| 506 | } |
|---|
| 507 | |
|---|
| 508 | bool Xtal::isNiggliReduced() const |
|---|
| 509 | { |
|---|
| 510 | // cache params |
|---|
| 511 | double a = getA(); |
|---|
| 512 | double b = getB(); |
|---|
| 513 | double c = getC(); |
|---|
| 514 | double alpha = getAlpha(); |
|---|
| 515 | double beta = getBeta(); |
|---|
| 516 | double gamma = getGamma(); |
|---|
| 517 | |
|---|
| 518 | return Xtal::isNiggliReduced(a, b, c, alpha, beta, gamma); |
|---|
| 519 | } |
|---|
| 520 | |
|---|
| 521 | bool Xtal::isNiggliReduced(const double a, const double b, const double c, |
|---|
| 522 | const double alpha, const double beta, const double gamma) |
|---|
| 523 | { |
|---|
| 524 | // Calculate characteristic |
|---|
| 525 | double A = a*a; |
|---|
| 526 | double B = b*b; |
|---|
| 527 | double C = c*c; |
|---|
| 528 | double xi = 2*b*c*cos(alpha * DEG_TO_RAD); |
|---|
| 529 | double eta = 2*a*c*cos(beta * DEG_TO_RAD); |
|---|
| 530 | double zeta = 2*a*b*cos(gamma * DEG_TO_RAD); |
|---|
| 531 | |
|---|
| 532 | // comparison tolerance |
|---|
| 533 | double tol = STABLE_COMP_TOL * ( (a + b + c) * (1.0 / 3.0) ); |
|---|
| 534 | |
|---|
| 535 | // First check the Buerger conditions. Taken from: Gruber B.. Acta |
|---|
| 536 | // Cryst. A. 1973;29(4):433-440. Available at: |
|---|
| 537 | // http://scripts.iucr.org/cgi-bin/paper?S0567739473001063 |
|---|
| 538 | // [Accessed December 15, 2010]. |
|---|
| 539 | if (StableComp::gt(A, B, tol) || StableComp::gt(B, C, tol)) return false; |
|---|
| 540 | if (StableComp::eq(A, B, tol) && StableComp::gt(fabs(xi), fabs(eta), tol)) return false; |
|---|
| 541 | if (StableComp::eq(B, C, tol) && StableComp::gt(fabs(eta), fabs(zeta), tol)) return false; |
|---|
| 542 | if ( !(StableComp::gt(xi, 0.0, tol) && |
|---|
| 543 | StableComp::gt(eta, 0.0, tol) && |
|---|
| 544 | StableComp::gt(zeta, 0.0, tol)) |
|---|
| 545 | && |
|---|
| 546 | !(StableComp::leq(zeta, 0.0, tol) && |
|---|
| 547 | StableComp::leq(zeta, 0.0, tol) && |
|---|
| 548 | StableComp::leq(zeta, 0.0, tol)) ) return false; |
|---|
| 549 | |
|---|
| 550 | // Check against Niggli conditions (taken from Gruber 1973). The |
|---|
| 551 | // logic of the second comparison is reversed from the paper to |
|---|
| 552 | // simplify the algorithm. |
|---|
| 553 | if (StableComp::eq(xi, B, tol) && StableComp::gt (zeta, 2*eta, tol)) return false; |
|---|
| 554 | if (StableComp::eq(eta, A, tol) && StableComp::gt (zeta, 2*xi, tol)) return false; |
|---|
| 555 | if (StableComp::eq(zeta, A, tol) && StableComp::gt (eta, 2*xi, tol)) return false; |
|---|
| 556 | if (StableComp::eq(xi, -B, tol) && StableComp::neq(zeta, 0, tol)) return false; |
|---|
| 557 | if (StableComp::eq(eta, -A, tol) && StableComp::neq(zeta, 0, tol)) return false; |
|---|
| 558 | if (StableComp::eq(zeta, -A, tol) && StableComp::neq(eta, 0, tol)) return false; |
|---|
| 559 | |
|---|
| 560 | if (StableComp::eq(xi+eta+zeta+A+B, 0, tol) |
|---|
| 561 | && StableComp::gt(2*(A+eta)+zeta, 0, tol)) return false; |
|---|
| 562 | |
|---|
| 563 | // all good! |
|---|
| 564 | return true; |
|---|
| 565 | } |
|---|
| 566 | |
|---|
| 567 | bool Xtal::fixAngles(int attempts) |
|---|
| 568 | { |
|---|
| 569 | // Perform niggli reduction |
|---|
| 570 | if (!niggliReduce(attempts)) { |
|---|
| 571 | qDebug() << "Unable to perform cell reduction on Xtal " << getIDString() |
|---|
| 572 | << "( " << getA() << getB() << getC() |
|---|
| 573 | << getAlpha() << getBeta() << getGamma() << " )"; |
|---|
| 574 | return false; |
|---|
| 575 | } |
|---|
| 576 | |
|---|
| 577 | findSpaceGroup(); |
|---|
| 578 | return true; |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | bool Xtal::operator==(const Xtal &o) const |
|---|
| 582 | { |
|---|
| 583 | // Compare coordinates using the default tolerance |
|---|
| 584 | if (!compareCoordinates(o)) |
|---|
| 585 | return false; |
|---|
| 586 | |
|---|
| 587 | return true; |
|---|
| 588 | } |
|---|
| 589 | |
|---|
| 590 | bool Xtal::compareCoordinates(const Xtal &other, const double lengthTol, |
|---|
| 591 | const double angleTol) const |
|---|
| 592 | { |
|---|
| 593 | // Cell matrices as row vectors |
|---|
| 594 | const OpenBabel::matrix3x3 thisCellOB (this->cell()->GetCellMatrix()); |
|---|
| 595 | const OpenBabel::matrix3x3 otherCellOB (other.cell()->GetCellMatrix()); |
|---|
| 596 | XcMatrix thisCell (thisCellOB(0,0), thisCellOB(0,1), thisCellOB(0,2), |
|---|
| 597 | thisCellOB(1,0), thisCellOB(1,1), thisCellOB(1,2), |
|---|
| 598 | thisCellOB(2,0), thisCellOB(2,1), thisCellOB(2,2)); |
|---|
| 599 | XcMatrix otherCell(otherCellOB(0,0), otherCellOB(0,1), otherCellOB(0,2), |
|---|
| 600 | otherCellOB(1,0), otherCellOB(1,1), otherCellOB(1,2), |
|---|
| 601 | otherCellOB(2,0), otherCellOB(2,1), otherCellOB(2,2)); |
|---|
| 602 | |
|---|
| 603 | // vectors of fractional coordinates and atomic numbers |
|---|
| 604 | std::vector<XcVector> thisCoords; |
|---|
| 605 | std::vector<XcVector> otherCoords; |
|---|
| 606 | std::vector<unsigned int> thisTypes; |
|---|
| 607 | std::vector<unsigned int> otherTypes; |
|---|
| 608 | thisCoords.reserve(this->numAtoms()); |
|---|
| 609 | thisTypes.reserve(this->numAtoms()); |
|---|
| 610 | otherCoords.reserve(other.numAtoms()); |
|---|
| 611 | otherTypes.reserve(other.numAtoms()); |
|---|
| 612 | Eigen::Vector3d pos; |
|---|
| 613 | for (QList<Atom*>::const_iterator it = this->m_atomList.constBegin(), |
|---|
| 614 | it_end = this->m_atomList.constEnd(); it != it_end; ++it) { |
|---|
| 615 | pos = this->cartToFrac(*(*it)->pos()); |
|---|
| 616 | thisCoords.push_back(XcVector(pos.x(), pos.y(), pos.z())); |
|---|
| 617 | thisTypes.push_back((*it)->atomicNumber()); |
|---|
| 618 | } |
|---|
| 619 | for (QList<Atom*>::const_iterator it = other.m_atomList.constBegin(), |
|---|
| 620 | it_end = other.m_atomList.constEnd(); it != it_end; ++it) { |
|---|
| 621 | pos = other.cartToFrac(*(*it)->pos()); |
|---|
| 622 | otherCoords.push_back(XcVector(pos.x(), pos.y(), pos.z())); |
|---|
| 623 | otherTypes.push_back((*it)->atomicNumber()); |
|---|
| 624 | } |
|---|
| 625 | |
|---|
| 626 | return XtalComp::compare(thisCell, thisTypes, thisCoords, |
|---|
| 627 | otherCell, otherTypes, otherCoords, |
|---|
| 628 | NULL, lengthTol, angleTol); |
|---|
| 629 | } |
|---|
| 630 | |
|---|
| 631 | OpenBabel::OBUnitCell* Xtal::cell() const |
|---|
| 632 | { |
|---|
| 633 | return OBUnitCell(); |
|---|
| 634 | } |
|---|
| 635 | |
|---|
| 636 | bool Xtal::addAtomRandomly(uint atomicNumber, double minIAD, double maxIAD, int maxAttempts, Atom **atom) { |
|---|
| 637 | INIT_RANDOM_GENERATOR(); |
|---|
| 638 | Q_UNUSED(maxIAD); |
|---|
| 639 | |
|---|
| 640 | double IAD = -1; |
|---|
| 641 | int i = 0; |
|---|
| 642 | vector3 cartCoords; |
|---|
| 643 | |
|---|
| 644 | // For first atom, add to 0, 0, 0 |
|---|
| 645 | if (numAtoms() == 0) { |
|---|
| 646 | cartCoords = vector3 (0,0,0); |
|---|
| 647 | } |
|---|
| 648 | else { |
|---|
| 649 | do { |
|---|
| 650 | // Generate fractional coordinates |
|---|
| 651 | IAD = -1; |
|---|
| 652 | double x = RANDDOUBLE(); |
|---|
| 653 | double y = RANDDOUBLE(); |
|---|
| 654 | double z = RANDDOUBLE(); |
|---|
| 655 | |
|---|
| 656 | // Convert to cartesian coordinates and store |
|---|
| 657 | vector3 fracCoords (x,y,z); |
|---|
| 658 | cartCoords = fracToCart(fracCoords); |
|---|
| 659 | if (minIAD != -1) { |
|---|
| 660 | getNearestNeighborDistance(cartCoords[0], |
|---|
| 661 | cartCoords[1], |
|---|
| 662 | cartCoords[2], |
|---|
| 663 | IAD); |
|---|
| 664 | } |
|---|
| 665 | else { break;}; |
|---|
| 666 | i++; |
|---|
| 667 | } while (i < maxAttempts && IAD <= minIAD); |
|---|
| 668 | |
|---|
| 669 | if (i >= maxAttempts) return false; |
|---|
| 670 | } |
|---|
| 671 | Atom *atm = addAtom(); |
|---|
| 672 | atom = &atm; |
|---|
| 673 | Eigen::Vector3d pos (cartCoords[0],cartCoords[1],cartCoords[2]); |
|---|
| 674 | (*atom)->setPos(pos); |
|---|
| 675 | (*atom)->setAtomicNumber(static_cast<int>(atomicNumber)); |
|---|
| 676 | return true; |
|---|
| 677 | } |
|---|
| 678 | |
|---|
| 679 | bool Xtal::addAtomRandomly( |
|---|
| 680 | unsigned int atomicNumber, |
|---|
| 681 | const QHash<unsigned int, XtalCompositionStruct> & limits, |
|---|
| 682 | int maxAttempts, Avogadro::Atom **atom) |
|---|
| 683 | { |
|---|
| 684 | Eigen::Vector3d cartCoords; |
|---|
| 685 | bool success; |
|---|
| 686 | |
|---|
| 687 | // For first atom, add to 0, 0, 0 |
|---|
| 688 | if (numAtoms() == 0) { |
|---|
| 689 | cartCoords = Eigen::Vector3d (0,0,0); |
|---|
| 690 | } |
|---|
| 691 | else { |
|---|
| 692 | unsigned int i = 0; |
|---|
| 693 | vector3 fracCoords; |
|---|
| 694 | |
|---|
| 695 | // Cache the minimum radius for the new atom |
|---|
| 696 | const double newMinRadius = limits.value(atomicNumber).minRadius; |
|---|
| 697 | |
|---|
| 698 | // Compute a cut off distance -- atoms farther away than this value |
|---|
| 699 | // will abort the check early. |
|---|
| 700 | double maxCheckDistance = 0.0; |
|---|
| 701 | for (QHash<unsigned int, XtalCompositionStruct>::const_iterator |
|---|
| 702 | it = limits.constBegin(), it_end = limits.constEnd(); it != it_end; |
|---|
| 703 | ++it) { |
|---|
| 704 | if (it.value().minRadius > maxCheckDistance) { |
|---|
| 705 | maxCheckDistance = it.value().minRadius; |
|---|
| 706 | } |
|---|
| 707 | } |
|---|
| 708 | maxCheckDistance += newMinRadius; |
|---|
| 709 | const double maxCheckDistSquared = maxCheckDistance*maxCheckDistance; |
|---|
| 710 | |
|---|
| 711 | do { |
|---|
| 712 | // Reset sentinal |
|---|
| 713 | success = true; |
|---|
| 714 | |
|---|
| 715 | // Generate fractional coordinates |
|---|
| 716 | fracCoords.Set(RANDDOUBLE(), RANDDOUBLE(), RANDDOUBLE()); |
|---|
| 717 | |
|---|
| 718 | // Convert to cartesian coordinates and store |
|---|
| 719 | cartCoords = Eigen::Vector3d(this->fracToCart(fracCoords).AsArray()); |
|---|
| 720 | |
|---|
| 721 | // Compare distance to each atom in xtal with minimum radii |
|---|
| 722 | QVector<double> squaredDists; |
|---|
| 723 | this->getSquaredAtomicDistancesToPoint(cartCoords, &squaredDists); |
|---|
| 724 | Q_ASSERT_X(squaredDists.size() == this->numAtoms(), Q_FUNC_INFO, |
|---|
| 725 | "Size of distance list does not match number of atoms."); |
|---|
| 726 | |
|---|
| 727 | for (int dist_ind = 0; dist_ind < squaredDists.size(); ++dist_ind) { |
|---|
| 728 | const double &curDistSquared = squaredDists[dist_ind]; |
|---|
| 729 | // Save a bit of time if distance is huge... |
|---|
| 730 | if (curDistSquared > maxCheckDistSquared) { |
|---|
| 731 | continue; |
|---|
| 732 | } |
|---|
| 733 | // Compare distance to minimum: |
|---|
| 734 | const double minDist = newMinRadius + limits.value( |
|---|
| 735 | this->atom(dist_ind)->atomicNumber()).minRadius; |
|---|
| 736 | const double minDistSquared = minDist * minDist; |
|---|
| 737 | |
|---|
| 738 | if (curDistSquared < minDistSquared) { |
|---|
| 739 | success = false; |
|---|
| 740 | break; |
|---|
| 741 | } |
|---|
| 742 | } |
|---|
| 743 | |
|---|
| 744 | } while (++i < maxAttempts && !success); |
|---|
| 745 | |
|---|
| 746 | if (i >= maxAttempts) return false; |
|---|
| 747 | } |
|---|
| 748 | Atom *atm = addAtom(); |
|---|
| 749 | atom = &atm; |
|---|
| 750 | (*atom)->setPos(cartCoords); |
|---|
| 751 | (*atom)->setAtomicNumber(static_cast<int>(atomicNumber)); |
|---|
| 752 | return true; |
|---|
| 753 | } |
|---|
| 754 | |
|---|
| 755 | bool Xtal::checkInteratomicDistances( |
|---|
| 756 | const QHash<unsigned int, XtalCompositionStruct> &limits, |
|---|
| 757 | int *atom1, int *atom2, double *IAD) |
|---|
| 758 | { |
|---|
| 759 | // Compute a cut off distance -- atoms farther away than this value |
|---|
| 760 | // will abort the check early. |
|---|
| 761 | double maxCheckDistance = 0.0; |
|---|
| 762 | for (QHash<unsigned int, XtalCompositionStruct>::const_iterator |
|---|
| 763 | it = limits.constBegin(), it_end = limits.constEnd(); it != it_end; |
|---|
| 764 | ++it) { |
|---|
| 765 | if (it.value().minRadius > maxCheckDistance) { |
|---|
| 766 | maxCheckDistance = it.value().minRadius; |
|---|
| 767 | } |
|---|
| 768 | } |
|---|
| 769 | maxCheckDistance += maxCheckDistance; |
|---|
| 770 | const double maxCheckDistSquared = maxCheckDistance*maxCheckDistance; |
|---|
| 771 | |
|---|
| 772 | // Iterate through all of the atoms in the molecule for "a1" |
|---|
| 773 | for (QList<Atom*>::const_iterator a1 = m_atomList.constBegin(), |
|---|
| 774 | a1_end = m_atomList.constEnd(); a1 != a1_end; ++a1) { |
|---|
| 775 | |
|---|
| 776 | // Get list of minimum squared distances between each atom and a1 |
|---|
| 777 | QVector<double> squaredDists; |
|---|
| 778 | this->getSquaredAtomicDistancesToPoint(*(*a1)->pos(), &squaredDists); |
|---|
| 779 | Q_ASSERT_X(squaredDists.size() == this->numAtoms(), Q_FUNC_INFO, |
|---|
| 780 | "Size of distance list does not match number of atoms."); |
|---|
| 781 | |
|---|
| 782 | // Cache the minimum radius of a1 |
|---|
| 783 | const double minA1Radius = |
|---|
| 784 | limits.value((*a1)->atomicNumber()).minRadius; |
|---|
| 785 | |
|---|
| 786 | // Iterate through each distance |
|---|
| 787 | for (int i = 0; i < squaredDists.size(); ++i) { |
|---|
| 788 | |
|---|
| 789 | // Grab the atom pointer at i, a2 |
|---|
| 790 | Atom *a2 = this->atom(i); |
|---|
| 791 | |
|---|
| 792 | // If a1 and a2 are the same, skip the comparison |
|---|
| 793 | if (*a1 == a2) { |
|---|
| 794 | continue; |
|---|
| 795 | } |
|---|
| 796 | |
|---|
| 797 | // Cache the squared distance between a1 and a2 |
|---|
| 798 | const double &curDistSquared = squaredDists[i]; |
|---|
| 799 | |
|---|
| 800 | // Skip comparison if the current distance exceeds the cutoff |
|---|
| 801 | if (curDistSquared > maxCheckDistSquared) { |
|---|
| 802 | continue; |
|---|
| 803 | } |
|---|
| 804 | |
|---|
| 805 | // Calculate the minimum distance for the atom pair |
|---|
| 806 | const double minDist = limits.value(a2->atomicNumber()).minRadius |
|---|
| 807 | + minA1Radius; |
|---|
| 808 | const double minDistSquared = minDist * minDist; |
|---|
| 809 | |
|---|
| 810 | // If the distance is too small, set atom1/atom2 and return false |
|---|
| 811 | if (curDistSquared < minDistSquared) { |
|---|
| 812 | if (atom1 != NULL && atom2 != NULL) { |
|---|
| 813 | *atom1 = m_atomList.indexOf(*a1); |
|---|
| 814 | *atom2 = m_atomList.indexOf(a2); |
|---|
| 815 | if (IAD != NULL) { |
|---|
| 816 | *IAD = sqrt(curDistSquared); |
|---|
| 817 | } |
|---|
| 818 | } |
|---|
| 819 | return false; |
|---|
| 820 | } |
|---|
| 821 | |
|---|
| 822 | // Atom a2 is ok with respect to a1 |
|---|
| 823 | } |
|---|
| 824 | // Atom a1 is ok will all a2 |
|---|
| 825 | } |
|---|
| 826 | // all distances check out -- return true. |
|---|
| 827 | if (atom1 != NULL && atom2 != NULL) { |
|---|
| 828 | *atom1 = *atom2 = -1; |
|---|
| 829 | if (IAD != NULL) { |
|---|
| 830 | *IAD = 0.0; |
|---|
| 831 | } |
|---|
| 832 | } |
|---|
| 833 | return true; |
|---|
| 834 | } |
|---|
| 835 | |
|---|
| 836 | bool Xtal::checkInteratomicDistances( |
|---|
| 837 | const QHash<unsigned int, XtalCompositionStruct> &limits, |
|---|
| 838 | const QList<Atom *> atoms, int *atom1, int *atom2, double *IAD) |
|---|
| 839 | { |
|---|
| 840 | // Compute a cut off distance -- atoms farther away than this value |
|---|
| 841 | // will abort the check early. |
|---|
| 842 | double maxCheckDistance = 0.0; |
|---|
| 843 | for (QHash<unsigned int, XtalCompositionStruct>::const_iterator |
|---|
| 844 | it = limits.constBegin(), it_end = limits.constEnd(); it != it_end; |
|---|
| 845 | ++it) { |
|---|
| 846 | if (it.value().minRadius > maxCheckDistance) { |
|---|
| 847 | maxCheckDistance = it.value().minRadius; |
|---|
| 848 | } |
|---|
| 849 | } |
|---|
| 850 | maxCheckDistance += maxCheckDistance; |
|---|
| 851 | const double maxCheckDistSquared = maxCheckDistance*maxCheckDistance; |
|---|
| 852 | |
|---|
| 853 | // Iterate through all of the atoms in the list for "a1" |
|---|
| 854 | for (QList<Atom*>::const_iterator a1 = atoms.constBegin(), |
|---|
| 855 | a1_end = atoms.constEnd(); a1 != a1_end; ++a1) { |
|---|
| 856 | |
|---|
| 857 | // Get list of minimum squared distances between each atom and a1 |
|---|
| 858 | QVector<double> squaredDists; |
|---|
| 859 | this->getSquaredAtomicDistancesToPoint(*(*a1)->pos(), &squaredDists); |
|---|
| 860 | Q_ASSERT_X(squaredDists.size() == this->numAtoms(), Q_FUNC_INFO, |
|---|
| 861 | "Size of distance list does not match number of atoms."); |
|---|
| 862 | |
|---|
| 863 | // Cache the minimum radius of a1 |
|---|
| 864 | const double minA1Radius = |
|---|
| 865 | limits.value((*a1)->atomicNumber()).minRadius; |
|---|
| 866 | |
|---|
| 867 | // Iterate through each distance |
|---|
| 868 | for (int i = 0; i < squaredDists.size(); ++i) { |
|---|
| 869 | |
|---|
| 870 | // Grab the atom pointer at i, a2 |
|---|
| 871 | Atom *a2 = this->atom(i); |
|---|
| 872 | |
|---|
| 873 | // Cache the squared distance between a1 and a2 |
|---|
| 874 | const double &curDistSquared = squaredDists[i]; |
|---|
| 875 | |
|---|
| 876 | // Skip comparison if the current distance exceeds the cutoff |
|---|
| 877 | if (curDistSquared > maxCheckDistSquared) { |
|---|
| 878 | continue; |
|---|
| 879 | } |
|---|
| 880 | |
|---|
| 881 | // Calculate the minimum distance for the atom pair |
|---|
| 882 | const double minDist = limits.value(a2->atomicNumber()).minRadius |
|---|
| 883 | + minA1Radius; |
|---|
| 884 | const double minDistSquared = minDist * minDist; |
|---|
| 885 | |
|---|
| 886 | // If the distance is too small, set atom1/atom2 and return false |
|---|
| 887 | if (curDistSquared < minDistSquared) { |
|---|
| 888 | if (atom1 != NULL && atom2 != NULL) { |
|---|
| 889 | *atom1 = atoms.indexOf(*a1); |
|---|
| 890 | *atom2 = m_atomList.indexOf(a2); |
|---|
| 891 | if (IAD != NULL) { |
|---|
| 892 | *IAD = sqrt(curDistSquared); |
|---|
| 893 | } |
|---|
| 894 | } |
|---|
| 895 | return false; |
|---|
| 896 | } |
|---|
| 897 | |
|---|
| 898 | // Atom a2 is ok with respect to a1 |
|---|
| 899 | } |
|---|
| 900 | // Atom a1 is ok will all a2 |
|---|
| 901 | } |
|---|
| 902 | // all distances check out -- return true. |
|---|
| 903 | if (atom1 != NULL && atom2 != NULL) { |
|---|
| 904 | *atom1 = *atom2 = -1; |
|---|
| 905 | if (IAD != NULL) { |
|---|
| 906 | *IAD = 0.0; |
|---|
| 907 | } |
|---|
| 908 | } |
|---|
| 909 | return true; |
|---|
| 910 | } |
|---|
| 911 | |
|---|
| 912 | |
|---|
| 913 | bool Xtal::getShortestInteratomicDistance(double & shortest) const { |
|---|
| 914 | QList<Atom*> atomList = atoms(); |
|---|
| 915 | if (atomList.size() <= 1) return false; // Need at least two atoms! |
|---|
| 916 | QList<Vector3d> atomPositions; |
|---|
| 917 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 918 | atomPositions.push_back(*(atomList.at(i)->pos())); |
|---|
| 919 | |
|---|
| 920 | // Initialize vars |
|---|
| 921 | // Atomic Positions |
|---|
| 922 | Vector3d v1= atomPositions.at(0); |
|---|
| 923 | Vector3d v2= atomPositions.at(1); |
|---|
| 924 | // Unit Cell Vectors |
|---|
| 925 | // First get OB matrix, extract vectors, then convert to Eigen::Vector3d's |
|---|
| 926 | matrix3x3 obcellMatrix = cell()->GetCellMatrix(); |
|---|
| 927 | vector3 obU1 = obcellMatrix.GetRow(0); |
|---|
| 928 | vector3 obU2 = obcellMatrix.GetRow(1); |
|---|
| 929 | vector3 obU3 = obcellMatrix.GetRow(2); |
|---|
| 930 | Vector3d u1 (obU1.x(), obU1.y(), obU1.z()); |
|---|
| 931 | Vector3d u2 (obU2.x(), obU2.y(), obU2.z()); |
|---|
| 932 | Vector3d u3 (obU3.x(), obU3.y(), obU3.z()); |
|---|
| 933 | // Find all combinations of unit cell vectors to get wrapped neighbors |
|---|
| 934 | QList<Vector3d> uVecs; |
|---|
| 935 | int s_1, s_2, s_3; // will be -1, 0, +1 multipliers |
|---|
| 936 | for (s_1 = -1; s_1 <= 1; s_1++) { |
|---|
| 937 | for (s_2 = -1; s_2 <= 1; s_2++) { |
|---|
| 938 | for (s_3 = -1; s_3 <= 1; s_3++) { |
|---|
| 939 | uVecs.append(s_1*u1 + s_2*u2 + s_3*u3); |
|---|
| 940 | } |
|---|
| 941 | } |
|---|
| 942 | } |
|---|
| 943 | |
|---|
| 944 | shortest = fabs((v1-v2).norm()); |
|---|
| 945 | double distance; |
|---|
| 946 | |
|---|
| 947 | // Find shortest distance |
|---|
| 948 | for (int i = 0; i < atomList.size(); i++) { |
|---|
| 949 | v1 = atomPositions.at(i); |
|---|
| 950 | for (int j = i+1; j < atomList.size(); j++) { |
|---|
| 951 | v2 = atomPositions.at(j); |
|---|
| 952 | // Intercell |
|---|
| 953 | distance = fabs((v1-v2).norm()); |
|---|
| 954 | if (distance < shortest) shortest = distance; |
|---|
| 955 | // Intracell |
|---|
| 956 | for (int vecInd = 0; vecInd < uVecs.size(); vecInd++) { |
|---|
| 957 | distance = fabs(((v1+uVecs.at(vecInd))-v2).norm()); |
|---|
| 958 | if (distance < shortest) shortest = distance; |
|---|
| 959 | } |
|---|
| 960 | } |
|---|
| 961 | } |
|---|
| 962 | |
|---|
| 963 | return true; |
|---|
| 964 | } |
|---|
| 965 | |
|---|
| 966 | bool Xtal::getSquaredAtomicDistancesToPoint(const Eigen::Vector3d &coord, |
|---|
| 967 | QVector<double> *distances) |
|---|
| 968 | { |
|---|
| 969 | int atmCount = this->numAtoms(); |
|---|
| 970 | if (atmCount < 1) { |
|---|
| 971 | return false; |
|---|
| 972 | } |
|---|
| 973 | |
|---|
| 974 | // Allocate memory |
|---|
| 975 | distances->resize(atmCount); |
|---|
| 976 | |
|---|
| 977 | // Create list of all translation vectors to build a 3x3x3 supercell |
|---|
| 978 | // First get OB matrix, extract vectors, then convert to Eigen::Vector3d's |
|---|
| 979 | matrix3x3 obcellMatrix = cell()->GetCellMatrix(); |
|---|
| 980 | const Eigen::Vector3d u1 (obcellMatrix.GetRow(0).AsArray()); |
|---|
| 981 | const Eigen::Vector3d u2 (obcellMatrix.GetRow(1).AsArray()); |
|---|
| 982 | const Eigen::Vector3d u3 (obcellMatrix.GetRow(2).AsArray()); |
|---|
| 983 | // Find all combinations of unit cell vectors to get wrapped neighbors |
|---|
| 984 | QVector<Vector3d> uVecs; |
|---|
| 985 | uVecs.clear(); |
|---|
| 986 | uVecs.reserve(27); |
|---|
| 987 | short s_1, s_2, s_3; // will be -1, 0, +1 multipliers |
|---|
| 988 | for (s_1 = -1; s_1 <= 1; ++s_1) { |
|---|
| 989 | for (s_2 = -1; s_2 <= 1; ++s_2) { |
|---|
| 990 | for (s_3 = -1; s_3 <= 1; ++s_3) { |
|---|
| 991 | uVecs.append(s_1*u1 + s_2*u2 + s_3*u3); |
|---|
| 992 | } |
|---|
| 993 | } |
|---|
| 994 | } |
|---|
| 995 | |
|---|
| 996 | for (int i = 0; i < atmCount; ++i) { |
|---|
| 997 | const Eigen::Vector3d *pos = (this->atom(i)->pos()); |
|---|
| 998 | double shortest = DBL_MAX; |
|---|
| 999 | for (QVector<Vector3d>::const_iterator it = uVecs.constBegin(), |
|---|
| 1000 | it_end = uVecs.constEnd(); it != it_end; ++it) { |
|---|
| 1001 | register double current = ((*it + *pos) - coord).squaredNorm(); |
|---|
| 1002 | if (current < shortest) { |
|---|
| 1003 | shortest = current; |
|---|
| 1004 | } |
|---|
| 1005 | } |
|---|
| 1006 | (*distances)[i] = shortest; |
|---|
| 1007 | } |
|---|
| 1008 | |
|---|
| 1009 | return true; |
|---|
| 1010 | } |
|---|
| 1011 | |
|---|
| 1012 | |
|---|
| 1013 | bool Xtal::getNearestNeighborDistance(const double x, |
|---|
| 1014 | const double y, |
|---|
| 1015 | const double z, |
|---|
| 1016 | double & shortest) const { |
|---|
| 1017 | if (this->numAtoms() < 1) { |
|---|
| 1018 | return false; // Need at least one atom! |
|---|
| 1019 | } |
|---|
| 1020 | |
|---|
| 1021 | // Initialize vars |
|---|
| 1022 | // Atomic Positions |
|---|
| 1023 | Vector3d v1 (x, y, z); |
|---|
| 1024 | const Vector3d *v2 = this->atom(0)->pos(); |
|---|
| 1025 | // Unit Cell Vectors |
|---|
| 1026 | // First get OB matrix, extract vectors, then convert to Eigen::Vector3d's |
|---|
| 1027 | matrix3x3 obcellMatrix = cell()->GetCellMatrix(); |
|---|
| 1028 | vector3 obU1 = obcellMatrix.GetRow(0); |
|---|
| 1029 | vector3 obU2 = obcellMatrix.GetRow(1); |
|---|
| 1030 | vector3 obU3 = obcellMatrix.GetRow(2); |
|---|
| 1031 | Vector3d u1 (obU1.x(), obU1.y(), obU1.z()); |
|---|
| 1032 | Vector3d u2 (obU2.x(), obU2.y(), obU2.z()); |
|---|
| 1033 | Vector3d u3 (obU3.x(), obU3.y(), obU3.z()); |
|---|
| 1034 | // Find all combinations of unit cell vectors to get wrapped neighbors |
|---|
| 1035 | QList<Vector3d> uVecs; |
|---|
| 1036 | int s_1, s_2, s_3; // will be -1, 0, +1 multipliers |
|---|
| 1037 | for (s_1 = -1; s_1 <= 1; s_1++) { |
|---|
| 1038 | for (s_2 = -1; s_2 <= 1; s_2++) { |
|---|
| 1039 | for (s_3 = -1; s_3 <= 1; s_3++) { |
|---|
| 1040 | uVecs.append(s_1*u1 + s_2*u2 + s_3*u3); |
|---|
| 1041 | } |
|---|
| 1042 | } |
|---|
| 1043 | } |
|---|
| 1044 | |
|---|
| 1045 | shortest = fabs((v1 - (*v2) ).norm()); |
|---|
| 1046 | |
|---|
| 1047 | double distance; |
|---|
| 1048 | |
|---|
| 1049 | // Find shortest distance |
|---|
| 1050 | for (int j = 0; j < this->numAtoms(); j++) { |
|---|
| 1051 | v2 = this->atom(j)->pos(); |
|---|
| 1052 | // Intercell |
|---|
| 1053 | distance = fabs((v1 - (*v2)).norm()); |
|---|
| 1054 | if (distance < shortest) shortest = distance; |
|---|
| 1055 | // Intracell |
|---|
| 1056 | for (int vecInd = 0; vecInd < uVecs.size(); vecInd++) { |
|---|
| 1057 | distance = fabs((((*v2)+uVecs.at(vecInd)) - v1).norm()); |
|---|
| 1058 | if (distance < shortest) shortest = distance; |
|---|
| 1059 | } |
|---|
| 1060 | } |
|---|
| 1061 | return true; |
|---|
| 1062 | } |
|---|
| 1063 | |
|---|
| 1064 | bool Xtal::getIADHistogram(QList<double> * distance, |
|---|
| 1065 | QList<double> * frequency, |
|---|
| 1066 | double min, double max, double step, |
|---|
| 1067 | Atom *atom) const { |
|---|
| 1068 | |
|---|
| 1069 | if (min > max && step > 0) { |
|---|
| 1070 | qWarning() << "Xtal::getIADHistogram: min cannot be greater than max!"; |
|---|
| 1071 | return false; |
|---|
| 1072 | } |
|---|
| 1073 | if (step < 0 || step == 0) { |
|---|
| 1074 | qWarning() << "Xtal::getIADHistogram: invalid step size:" << step; |
|---|
| 1075 | return false; |
|---|
| 1076 | } |
|---|
| 1077 | |
|---|
| 1078 | // Populate distance list |
|---|
| 1079 | distance->clear(); |
|---|
| 1080 | frequency->clear(); |
|---|
| 1081 | double val = min; |
|---|
| 1082 | do { |
|---|
| 1083 | distance->append(val); |
|---|
| 1084 | frequency->append(0); |
|---|
| 1085 | val += step; |
|---|
| 1086 | } while (val < max); |
|---|
| 1087 | |
|---|
| 1088 | QList<Atom*> atomList = atoms(); |
|---|
| 1089 | QList<Vector3d> atomPositions; |
|---|
| 1090 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 1091 | atomPositions.push_back(*(atomList.at(i)->pos())); |
|---|
| 1092 | |
|---|
| 1093 | // Initialize vars |
|---|
| 1094 | // Atomic Positions |
|---|
| 1095 | Vector3d v1= atomPositions.at(0); |
|---|
| 1096 | Vector3d v2= atomPositions.at(1); |
|---|
| 1097 | // Unit Cell Vectors |
|---|
| 1098 | // First get OB matrix, extract vectors, then convert to Eigen::Vector3d's |
|---|
| 1099 | matrix3x3 obcellMatrix = cell()->GetCellMatrix(); |
|---|
| 1100 | vector3 obU1 = obcellMatrix.GetRow(0); |
|---|
| 1101 | vector3 obU2 = obcellMatrix.GetRow(1); |
|---|
| 1102 | vector3 obU3 = obcellMatrix.GetRow(2); |
|---|
| 1103 | Vector3d u1 (obU1.x(), obU1.y(), obU1.z()); |
|---|
| 1104 | Vector3d u2 (obU2.x(), obU2.y(), obU2.z()); |
|---|
| 1105 | Vector3d u3 (obU3.x(), obU3.y(), obU3.z()); |
|---|
| 1106 | // Find all combinations of unit cell vectors to get wrapped neighbors |
|---|
| 1107 | QList<Vector3d> uVecs; |
|---|
| 1108 | int s_1, s_2, s_3; // will be -1, 0, +1 multipliers |
|---|
| 1109 | for (s_1 = -1; s_1 <= 1; s_1++) { |
|---|
| 1110 | for (s_2 = -1; s_2 <= 1; s_2++) { |
|---|
| 1111 | for (s_3 = -1; s_3 <= 1; s_3++) { |
|---|
| 1112 | uVecs.append(s_1*u1 + s_2*u2 + s_3*u3); |
|---|
| 1113 | } |
|---|
| 1114 | } |
|---|
| 1115 | } |
|---|
| 1116 | double diff; |
|---|
| 1117 | |
|---|
| 1118 | // build histogram |
|---|
| 1119 | // Loop over all atoms |
|---|
| 1120 | if (atom == 0) { |
|---|
| 1121 | for (int i = 0; i < atomList.size(); i++) { |
|---|
| 1122 | v1 = atomPositions.at(i); |
|---|
| 1123 | for (int j = i+1; j < atomList.size(); j++) { |
|---|
| 1124 | v2 = atomPositions.at(j); |
|---|
| 1125 | // Intercell |
|---|
| 1126 | diff = fabs((v1-v2).norm()); |
|---|
| 1127 | for (int k = 0; k < distance->size(); k++) { |
|---|
| 1128 | double radius = distance->at(k); |
|---|
| 1129 | if (fabs(diff-radius) < step/2) { |
|---|
| 1130 | (*frequency)[k]++; |
|---|
| 1131 | } |
|---|
| 1132 | } |
|---|
| 1133 | // Intracell |
|---|
| 1134 | for (int vecInd = 0; vecInd < uVecs.size(); vecInd++) { |
|---|
| 1135 | diff = fabs(((v1+uVecs.at(vecInd))-v2).norm()); |
|---|
| 1136 | for (int k = 0; k < distance->size(); k++) { |
|---|
| 1137 | double radius = distance->at(k); |
|---|
| 1138 | if (fabs(diff-radius) < step/2) { |
|---|
| 1139 | (*frequency)[k]++; |
|---|
| 1140 | } |
|---|
| 1141 | } |
|---|
| 1142 | } |
|---|
| 1143 | } |
|---|
| 1144 | } |
|---|
| 1145 | } |
|---|
| 1146 | // Or, just the one requested |
|---|
| 1147 | else { |
|---|
| 1148 | v1 = *atom->pos(); |
|---|
| 1149 | for (int j = 0; j < atomList.size(); j++) { |
|---|
| 1150 | v2 = atomPositions.at(j); |
|---|
| 1151 | // Intercell |
|---|
| 1152 | diff = fabs((v1-v2).norm()); |
|---|
| 1153 | for (int k = 0; k < distance->size(); k++) { |
|---|
| 1154 | double radius = distance->at(k); |
|---|
| 1155 | if (diff != 0 && fabs(diff-radius) < step/2) { |
|---|
| 1156 | (*frequency)[k]++; |
|---|
| 1157 | } |
|---|
| 1158 | } |
|---|
| 1159 | // Intracell |
|---|
| 1160 | for (int vecInd = 0; vecInd < uVecs.size(); vecInd++) { |
|---|
| 1161 | diff = fabs(((v1+uVecs.at(vecInd))-v2).norm()); |
|---|
| 1162 | for (int k = 0; k < distance->size(); k++) { |
|---|
| 1163 | double radius = distance->at(k); |
|---|
| 1164 | if (fabs(diff-radius) < step/2) { |
|---|
| 1165 | (*frequency)[k]++; |
|---|
| 1166 | } |
|---|
| 1167 | } |
|---|
| 1168 | } |
|---|
| 1169 | } |
|---|
| 1170 | } |
|---|
| 1171 | |
|---|
| 1172 | return true; |
|---|
| 1173 | } |
|---|
| 1174 | |
|---|
| 1175 | // Compare the symbols of two atoms to see which comes first alphabetically |
|---|
| 1176 | bool atomicSymbolSortLessThan(Atom *a1, Atom *a2) |
|---|
| 1177 | { |
|---|
| 1178 | // We know that the symbol is between 1-3 symbols long, so we can limit |
|---|
| 1179 | // the tests |
|---|
| 1180 | const char *s1 = OpenBabel::etab.GetSymbol(a1->atomicNumber()); |
|---|
| 1181 | const char *s2 = OpenBabel::etab.GetSymbol(a2->atomicNumber()); |
|---|
| 1182 | int ls1 = tolower(s1[0]); |
|---|
| 1183 | int ls2 = tolower(s2[0]); |
|---|
| 1184 | if (ls1 == ls2) { |
|---|
| 1185 | ls1 = tolower(s1[1]); |
|---|
| 1186 | ls2 = tolower(s2[1]); |
|---|
| 1187 | if (ls1 == ls2 && ls1 * ls2 != 0) { |
|---|
| 1188 | ls1 = tolower(s1[2]); |
|---|
| 1189 | ls2 = tolower(s2[2]); |
|---|
| 1190 | } |
|---|
| 1191 | } |
|---|
| 1192 | return (ls1 < ls2); |
|---|
| 1193 | } |
|---|
| 1194 | |
|---|
| 1195 | QList<Atom*> Xtal::getAtomsSortedBySymbol() const |
|---|
| 1196 | { |
|---|
| 1197 | QList<Atom*> list = m_atomList; |
|---|
| 1198 | qSort(list.begin(), list.end(), atomicSymbolSortLessThan); |
|---|
| 1199 | return list; |
|---|
| 1200 | } |
|---|
| 1201 | |
|---|
| 1202 | Eigen::Vector3d Xtal::fracToCart(const Eigen::Vector3d & fracCoords) const { |
|---|
| 1203 | OpenBabel::vector3 obfrac (fracCoords.x(), fracCoords.y(), fracCoords.z()); |
|---|
| 1204 | OpenBabel::vector3 obcart = cell()->FractionalToCartesian(obfrac); |
|---|
| 1205 | Eigen::Vector3d cartCoords (obcart.x(), obcart.y(), obcart.z()); |
|---|
| 1206 | return cartCoords; |
|---|
| 1207 | } |
|---|
| 1208 | |
|---|
| 1209 | Eigen::Vector3d *Xtal::fracToCart(const Eigen::Vector3d *fracCoords) const { |
|---|
| 1210 | OpenBabel::vector3 obfrac (fracCoords->x(), fracCoords->y(), fracCoords->z()); |
|---|
| 1211 | OpenBabel::vector3 obcart = cell()->FractionalToCartesian(obfrac); |
|---|
| 1212 | Eigen::Vector3d *cartCoords = new Eigen::Vector3d (obcart.x(), obcart.y(), obcart.z()); |
|---|
| 1213 | return cartCoords; |
|---|
| 1214 | } |
|---|
| 1215 | |
|---|
| 1216 | Eigen::Vector3d Xtal::cartToFrac(const Eigen::Vector3d & cartCoords) const { |
|---|
| 1217 | OpenBabel::vector3 obcart (cartCoords.x(), cartCoords.y(), cartCoords.z()); |
|---|
| 1218 | OpenBabel::vector3 obfrac = cell()->CartesianToFractional(obcart); |
|---|
| 1219 | Eigen::Vector3d fracCoords (obfrac.x(), obfrac.y(), obfrac.z()); |
|---|
| 1220 | return fracCoords; |
|---|
| 1221 | } |
|---|
| 1222 | |
|---|
| 1223 | Eigen::Vector3d *Xtal::cartToFrac(const Eigen::Vector3d *cartCoords) const { |
|---|
| 1224 | OpenBabel::vector3 obcart (cartCoords->x(), cartCoords->y(), cartCoords->z()); |
|---|
| 1225 | OpenBabel::vector3 obfrac = cell()->CartesianToFractional(obcart); |
|---|
| 1226 | Eigen::Vector3d *fracCoords = new Eigen::Vector3d (obfrac.x(), obfrac.y(), obfrac.z()); |
|---|
| 1227 | return fracCoords; |
|---|
| 1228 | } |
|---|
| 1229 | |
|---|
| 1230 | void Xtal::wrapAtomsToCell() { |
|---|
| 1231 | //qDebug() << "Xtal::wrapAtomsToCell() called"; |
|---|
| 1232 | // Store position of atoms in fractional units |
|---|
| 1233 | QList<Atom*> atomList = atoms(); |
|---|
| 1234 | QList<Vector3d> fracCoordsList; |
|---|
| 1235 | for (int i = 0; i < atomList.size(); i++) |
|---|
| 1236 | fracCoordsList.append(cartToFrac(*(atomList.at(i)->pos()))); |
|---|
| 1237 | |
|---|
| 1238 | // wrap fractional coordinates to [0,1) |
|---|
| 1239 | for (int i = 0; i < fracCoordsList.size(); i++) { |
|---|
| 1240 | fracCoordsList[i][0] = fmod(fracCoordsList[i][0]+100, 1); |
|---|
| 1241 | fracCoordsList[i][1] = fmod(fracCoordsList[i][1]+100, 1); |
|---|
| 1242 | fracCoordsList[i][2] = fmod(fracCoordsList[i][2]+100, 1); |
|---|
| 1243 | } |
|---|
| 1244 | |
|---|
| 1245 | // Recalculate cartesian coordinates: |
|---|
| 1246 | Eigen::Vector3d cartCoord; |
|---|
| 1247 | for (int i = 0; i < atomList.size(); i++) { |
|---|
| 1248 | cartCoord = fracToCart(fracCoordsList.at(i)); |
|---|
| 1249 | atomList.at(i)->setPos(cartCoord); |
|---|
| 1250 | } |
|---|
| 1251 | } |
|---|
| 1252 | |
|---|
| 1253 | QHash<QString, QVariant> Xtal::getFingerprint() |
|---|
| 1254 | { |
|---|
| 1255 | QHash<QString, QVariant> fp = Structure::getFingerprint(); |
|---|
| 1256 | fp.insert("volume", getVolume()); |
|---|
| 1257 | fp.insert("spacegroup", getSpaceGroupNumber()); |
|---|
| 1258 | return fp; |
|---|
| 1259 | } |
|---|
| 1260 | |
|---|
| 1261 | QString Xtal::getResultsEntry() const |
|---|
| 1262 | { |
|---|
| 1263 | QString status; |
|---|
| 1264 | switch (getStatus()) { |
|---|
| 1265 | case Optimized: |
|---|
| 1266 | status = "Optimized"; |
|---|
| 1267 | break; |
|---|
| 1268 | case Killed: |
|---|
| 1269 | case Removed: |
|---|
| 1270 | status = "Killed"; |
|---|
| 1271 | break; |
|---|
| 1272 | case Duplicate: |
|---|
| 1273 | status = "Duplicate"; |
|---|
| 1274 | break; |
|---|
| 1275 | case Error: |
|---|
| 1276 | status = "Error"; |
|---|
| 1277 | break; |
|---|
| 1278 | case Preoptimizing: |
|---|
| 1279 | status = "Preoptimizing"; |
|---|
| 1280 | break; |
|---|
| 1281 | case StepOptimized: |
|---|
| 1282 | case WaitingForOptimization: |
|---|
| 1283 | case InProcess: |
|---|
| 1284 | case Empty: |
|---|
| 1285 | case Updating: |
|---|
| 1286 | case Restart: |
|---|
| 1287 | case Submitted: |
|---|
| 1288 | status = "In progress"; |
|---|
| 1289 | break; |
|---|
| 1290 | default: |
|---|
| 1291 | status = "Unknown"; |
|---|
| 1292 | break; |
|---|
| 1293 | } |
|---|
| 1294 | return QString("%1 %2 %3 %4 %5 %6") |
|---|
| 1295 | .arg(getRank(), 6) |
|---|
| 1296 | .arg(getGeneration(), 6) |
|---|
| 1297 | .arg(getIDNumber(), 6) |
|---|
| 1298 | .arg(getEnthalpy(), 10) |
|---|
| 1299 | .arg(m_spgSymbol, 10) |
|---|
| 1300 | .arg(status, 11); |
|---|
| 1301 | }; |
|---|
| 1302 | |
|---|
| 1303 | |
|---|
| 1304 | uint Xtal::getSpaceGroupNumber() { |
|---|
| 1305 | if (m_spgNumber > 230) |
|---|
| 1306 | findSpaceGroup(); |
|---|
| 1307 | return m_spgNumber; |
|---|
| 1308 | } |
|---|
| 1309 | |
|---|
| 1310 | QString Xtal::getSpaceGroupSymbol() { |
|---|
| 1311 | if (m_spgNumber > 230) |
|---|
| 1312 | findSpaceGroup(); |
|---|
| 1313 | return m_spgSymbol; |
|---|
| 1314 | } |
|---|
| 1315 | |
|---|
| 1316 | QString Xtal::getHTMLSpaceGroupSymbol() { |
|---|
| 1317 | if (m_spgNumber > 230) |
|---|
| 1318 | findSpaceGroup(); |
|---|
| 1319 | |
|---|
| 1320 | QString s = m_spgSymbol; |
|---|
| 1321 | |
|---|
| 1322 | // Prepare HTML tags |
|---|
| 1323 | s.prepend("<HTML>"); |
|---|
| 1324 | s.append("</HTML>"); |
|---|
| 1325 | |
|---|
| 1326 | // "_X" --> "<sub>X</sub>" |
|---|
| 1327 | int ind = s.indexOf("_"); |
|---|
| 1328 | while (ind != -1) { |
|---|
| 1329 | s = s.insert(ind+2, "</sub>"); |
|---|
| 1330 | s = s.replace(ind, 1, "<sub>"); |
|---|
| 1331 | ind = s.indexOf("_"); |
|---|
| 1332 | } |
|---|
| 1333 | |
|---|
| 1334 | // "-X" --> "<span style="text-decoration: overline">X</span>" |
|---|
| 1335 | ind = s.indexOf("-"); |
|---|
| 1336 | while (ind != -1) { |
|---|
| 1337 | s = s.insert(ind+2, "</span>"); |
|---|
| 1338 | s = s.replace(ind, 1, "<span style=\"text-decoration: overline\">"); |
|---|
| 1339 | ind = s.indexOf("-", ind+35); |
|---|
| 1340 | } |
|---|
| 1341 | |
|---|
| 1342 | return s; |
|---|
| 1343 | } |
|---|
| 1344 | |
|---|
| 1345 | void Xtal::findSpaceGroup(double prec) { |
|---|
| 1346 | // Check that the precision is reasonable |
|---|
| 1347 | if (prec < 1e-5) { |
|---|
| 1348 | qWarning() << "Xtal::findSpaceGroup called with a precision of " |
|---|
| 1349 | << prec << ". This is likely an error. Resetting prec to " |
|---|
| 1350 | << 0.05 << "."; |
|---|
| 1351 | prec = 0.05; |
|---|
| 1352 | } |
|---|
| 1353 | |
|---|
| 1354 | // reset space group to 0 so we can exit if needed |
|---|
| 1355 | m_spgNumber = 0; |
|---|
| 1356 | m_spgSymbol = "Unknown"; |
|---|
| 1357 | int num = numAtoms(); |
|---|
| 1358 | |
|---|
| 1359 | // if no unit cell or atoms, exit |
|---|
| 1360 | if (!cell() || num == 0) { |
|---|
| 1361 | qWarning() << "Xtal::findSpaceGroup( " << prec << " ) called on atom with no cell or atoms!"; |
|---|
| 1362 | return; |
|---|
| 1363 | } |
|---|
| 1364 | |
|---|
| 1365 | // get lattice matrix |
|---|
| 1366 | std::vector<OpenBabel::vector3> vecs = cell()->GetCellVectors(); |
|---|
| 1367 | vector3 obU1 = vecs[0]; |
|---|
| 1368 | vector3 obU2 = vecs[1]; |
|---|
| 1369 | vector3 obU3 = vecs[2]; |
|---|
| 1370 | double lattice[3][3] = { |
|---|
| 1371 | {obU1.x(), obU2.x(), obU3.x()}, |
|---|
| 1372 | {obU1.y(), obU2.y(), obU3.y()}, |
|---|
| 1373 | {obU1.z(), obU2.z(), obU3.z()} |
|---|
| 1374 | }; |
|---|
| 1375 | |
|---|
| 1376 | // Get atom info |
|---|
| 1377 | double (*positions)[3] = new double[num][3]; |
|---|
| 1378 | int *types = new int[num]; |
|---|
| 1379 | QList<Atom*> atomList = atoms(); |
|---|
| 1380 | Eigen::Vector3d fracCoords; |
|---|
| 1381 | for (int i = 0; i < atomList.size(); i++) { |
|---|
| 1382 | fracCoords = cartToFrac(*(atomList.at(i)->pos())); |
|---|
| 1383 | types[i] = atomList.at(i)->atomicNumber(); |
|---|
| 1384 | positions[i][0] = fracCoords.x(); |
|---|
| 1385 | positions[i][1] = fracCoords.y(); |
|---|
| 1386 | positions[i][2] = fracCoords.z(); |
|---|
| 1387 | } |
|---|
| 1388 | |
|---|
| 1389 | // find spacegroup |
|---|
| 1390 | char symbol[21]; |
|---|
| 1391 | m_spgNumber = spg_get_international(symbol, |
|---|
| 1392 | lattice, |
|---|
| 1393 | positions, |
|---|
| 1394 | types, |
|---|
| 1395 | num, prec); |
|---|
| 1396 | |
|---|
| 1397 | delete [] positions; |
|---|
| 1398 | delete [] types; |
|---|
| 1399 | |
|---|
| 1400 | // Update the OBUnitCell object. |
|---|
| 1401 | cell()->SetSpaceGroup(m_spgNumber); |
|---|
| 1402 | |
|---|
| 1403 | // Fail if m_spgNumber is still 0 |
|---|
| 1404 | if (m_spgNumber == 0) { |
|---|
| 1405 | return; |
|---|
| 1406 | } |
|---|
| 1407 | |
|---|
| 1408 | // Set and clean up the symbol string |
|---|
| 1409 | m_spgSymbol = QString(symbol); |
|---|
| 1410 | m_spgSymbol.remove(" "); |
|---|
| 1411 | return; |
|---|
| 1412 | } |
|---|
| 1413 | |
|---|
| 1414 | void Xtal::getSpglibFormat() const { |
|---|
| 1415 | std::vector<OpenBabel::vector3> vecs = cell()->GetCellVectors(); |
|---|
| 1416 | vector3 obU1 = vecs[0]; |
|---|
| 1417 | vector3 obU2 = vecs[1]; |
|---|
| 1418 | vector3 obU3 = vecs[2]; |
|---|
| 1419 | |
|---|
| 1420 | QString t; |
|---|
| 1421 | QTextStream out (&t); |
|---|
| 1422 | |
|---|
| 1423 | out << "double lattice[3][3] = {\n" |
|---|
| 1424 | << " {" << obU1.x() << ", " << obU2.x() << ", " << obU3.x() << "},\n" |
|---|
| 1425 | << " {" << obU1.y() << ", " << obU2.y() << ", " << obU3.y() << "},\n" |
|---|
| 1426 | << " {" << obU1.z() << ", " << obU2.z() << ", " << obU3.z() << "}};\n\n"; |
|---|
| 1427 | |
|---|
| 1428 | out << "double position[][3] = {"; |
|---|
| 1429 | for (unsigned int i = 0; i < numAtoms(); i++) { |
|---|
| 1430 | if (i == 0 || i != numAtoms()-1) out << ","; |
|---|
| 1431 | out << "\n"; |
|---|
| 1432 | out << " {" << atom(i)->pos()->x() << ", " |
|---|
| 1433 | << atom(i)->pos()->y() << ", " |
|---|
| 1434 | << atom(i)->pos()->z() << "}"; |
|---|
| 1435 | } |
|---|
| 1436 | out << "};\n\n"; |
|---|
| 1437 | out << "int types[] = { "; |
|---|
| 1438 | for (unsigned int i = 0; i < numAtoms(); i++) { |
|---|
| 1439 | if (i == 0 || i != numAtoms()-1) out << ", "; |
|---|
| 1440 | out << atom(i)->atomicNumber(); |
|---|
| 1441 | } |
|---|
| 1442 | out << " };\n"; |
|---|
| 1443 | out << "int num_atom = " << numAtoms() << ";\n"; |
|---|
| 1444 | qDebug() << t; |
|---|
| 1445 | } |
|---|
| 1446 | |
|---|
| 1447 | bool Xtal::rotateCellToStandardOrientation() |
|---|
| 1448 | { |
|---|
| 1449 | // Get correct matrix |
|---|
| 1450 | Eigen::Matrix3d newMat |
|---|
| 1451 | (getCellMatrixInStandardOrientation()); |
|---|
| 1452 | |
|---|
| 1453 | // check that the matrix is valid |
|---|
| 1454 | if (newMat.isZero()) { |
|---|
| 1455 | const OpenBabel::matrix3x3 mat = cell()->GetCellMatrix(); |
|---|
| 1456 | qDebug() << "Cannot rotate cell to std orientation:\n" |
|---|
| 1457 | << QString("%L1 %L2 %L3\n%L4 %L5 %L6\n%L7 %L8 %L9") |
|---|
| 1458 | .arg(mat(0,0), -9, 'g').arg(mat(0,1), -9, 'g').arg(mat(0,2), -9, 'g') |
|---|
| 1459 | .arg(mat(1,0), -9, 'g').arg(mat(1,1), -9, 'g').arg(mat(1,2), -9, 'g') |
|---|
| 1460 | .arg(mat(2,0), -9, 'g').arg(mat(2,1), -9, 'g').arg(mat(2,2), -9, 'g'); |
|---|
| 1461 | return false; |
|---|
| 1462 | } |
|---|
| 1463 | |
|---|
| 1464 | // Set the rotated basis |
|---|
| 1465 | setCellInfo(Eigen2OB(newMat)); |
|---|
| 1466 | |
|---|
| 1467 | return true; |
|---|
| 1468 | } |
|---|
| 1469 | |
|---|
| 1470 | bool Xtal::rotateCellAndCoordsToStandardOrientation() |
|---|
| 1471 | { |
|---|
| 1472 | // Cache fractional coordinates |
|---|
| 1473 | QList<Eigen::Vector3d> fcoords; |
|---|
| 1474 | for (QList<Atom*>::const_iterator it = m_atomList.constBegin(), |
|---|
| 1475 | it_end = m_atomList.constEnd(); it != it_end; ++it) { |
|---|
| 1476 | fcoords.append( cartToFrac(*(*it)->pos())); |
|---|
| 1477 | } |
|---|
| 1478 | |
|---|
| 1479 | if (!rotateCellToStandardOrientation()) { |
|---|
| 1480 | return false; |
|---|
| 1481 | } |
|---|
| 1482 | |
|---|
| 1483 | // Reset coords |
|---|
| 1484 | Q_ASSERT(this->m_atomList.size() == fcoords.size()); |
|---|
| 1485 | for (int i = 0; i < m_atomList.size(); ++i) { |
|---|
| 1486 | this->atom(i)->setPos(this->fracToCart(fcoords[i])); |
|---|
| 1487 | } |
|---|
| 1488 | |
|---|
| 1489 | return true; |
|---|
| 1490 | } |
|---|
| 1491 | |
|---|
| 1492 | Eigen::Matrix3d Xtal::getCellMatrixInStandardOrientation() const |
|---|
| 1493 | { |
|---|
| 1494 | // Cell matrix as row vectors |
|---|
| 1495 | const OpenBabel::matrix3x3 origRowMat = cell()->GetCellMatrix(); |
|---|
| 1496 | return getCellMatrixInStandardOrientation(OB2Eigen(origRowMat)); |
|---|
| 1497 | } |
|---|
| 1498 | |
|---|
| 1499 | Eigen::Matrix3d Xtal::getCellMatrixInStandardOrientation |
|---|
| 1500 | (const Eigen::Matrix3d &origRowMat) |
|---|
| 1501 | { |
|---|
| 1502 | // Extract vector components: |
|---|
| 1503 | const double &x1 = origRowMat(0,0); |
|---|
| 1504 | const double &y1 = origRowMat(0,1); |
|---|
| 1505 | const double &z1 = origRowMat(0,2); |
|---|
| 1506 | |
|---|
| 1507 | const double &x2 = origRowMat(1,0); |
|---|
| 1508 | const double &y2 = origRowMat(1,1); |
|---|
| 1509 | const double &z2 = origRowMat(1,2); |
|---|
| 1510 | |
|---|
| 1511 | const double &x3 = origRowMat(2,0); |
|---|
| 1512 | const double &y3 = origRowMat(2,1); |
|---|
| 1513 | const double &z3 = origRowMat(2,2); |
|---|
| 1514 | |
|---|
| 1515 | // Cache some frequently used values: |
|---|
| 1516 | // Length of v1 |
|---|
| 1517 | const double L1 = sqrt(x1*x1 + y1*y1 + z1*z1); |
|---|
| 1518 | // Squared norm of v1's yz projection |
|---|
| 1519 | const double sqrdnorm1yz = y1*y1 + z1*z1; |
|---|
| 1520 | // Squared norm of v2's yz projection |
|---|
| 1521 | const double sqrdnorm2yz = y2*y2 + z2*z2; |
|---|
| 1522 | // Determinant of v1 and v2's projections in yz plane |
|---|
| 1523 | const double detv1v2yz = y2*z1 - y1*z2; |
|---|
| 1524 | // Scalar product of v1 and v2's projections in yz plane |
|---|
| 1525 | const double dotv1v2yz = y1*y2 + z1*z2; |
|---|
| 1526 | |
|---|
| 1527 | // Used for denominators, since we want to check that they are |
|---|
| 1528 | // sufficiently far from 0 to keep things reasonable: |
|---|
| 1529 | double denom; |
|---|
| 1530 | const double DENOM_TOL = 1e-5; |
|---|
| 1531 | |
|---|
| 1532 | // Create target matrix, fill with zeros |
|---|
| 1533 | Eigen::Matrix3d newMat (Eigen::Matrix3d::Zero()); |
|---|
| 1534 | |
|---|
| 1535 | // Set components of new v1: |
|---|
| 1536 | newMat(0,0) = L1; |
|---|
| 1537 | |
|---|
| 1538 | // Set components of new v2: |
|---|
| 1539 | denom = L1; |
|---|
| 1540 | if (fabs(denom) < DENOM_TOL) { |
|---|
| 1541 | return Eigen::Matrix3d::Zero(); |
|---|
| 1542 | }; |
|---|
| 1543 | newMat(1,0) = (x1*x2 + y1*y2 + z1*z2) / denom; |
|---|
| 1544 | |
|---|
| 1545 | newMat(1,1) = sqrt(x2*x2 * sqrdnorm1yz + |
|---|
| 1546 | detv1v2yz*detv1v2yz - |
|---|
| 1547 | 2*x1*x2*dotv1v2yz + |
|---|
| 1548 | x1*x1*sqrdnorm2yz) / denom; |
|---|
| 1549 | |
|---|
| 1550 | // Set components of new v3 |
|---|
| 1551 | // denom is still L1 |
|---|
| 1552 | Q_ASSERT(denom == L1); |
|---|
| 1553 | newMat(2,0) = (x1*x3 + y1*y3 + z1*z3) / denom; |
|---|
| 1554 | |
|---|
| 1555 | denom = L1*L1 * newMat(1,1); |
|---|
| 1556 | if (fabs(denom) < DENOM_TOL) { |
|---|
| 1557 | return Eigen::Matrix3d::Zero(); |
|---|
| 1558 | }; |
|---|
| 1559 | newMat(2,1) = (x1*x1*(y2*y3 + z2*z3) + |
|---|
| 1560 | x2*(x3*sqrdnorm1yz - |
|---|
| 1561 | x1*(y1*y3 + z1*z3) |
|---|
| 1562 | ) + |
|---|
| 1563 | detv1v2yz*(y3*z1 - y1*z3) - |
|---|
| 1564 | x1*x3*dotv1v2yz) / denom; |
|---|
| 1565 | |
|---|
| 1566 | denom = L1 * newMat(1,1); |
|---|
| 1567 | if (fabs(denom) < DENOM_TOL) { |
|---|
| 1568 | return Eigen::Matrix3d::Zero(); |
|---|
| 1569 | }; |
|---|
| 1570 | // Numerator is determinant of original cell: |
|---|
| 1571 | newMat(2,2) = (x1*y2*z3 - x1*y3*z2 + |
|---|
| 1572 | x2*y3*z1 - x2*y1*z3 + |
|---|
| 1573 | x3*y1*z2 - x3*y2*z1) / denom; |
|---|
| 1574 | |
|---|
| 1575 | return newMat; |
|---|
| 1576 | } |
|---|
| 1577 | |
|---|
| 1578 | // Initialize static members for COB list generation |
|---|
| 1579 | QMutex Xtal::m_validCOBsGenMutex; |
|---|
| 1580 | QVector<Eigen::Matrix3d> Xtal::m_transformationMatrices; |
|---|
| 1581 | QVector<Eigen::Matrix3d> Xtal::m_mixMatrices; |
|---|
| 1582 | |
|---|
| 1583 | static inline bool COBIsValid(const Eigen::Matrix3d &cob) |
|---|
| 1584 | { |
|---|
| 1585 | // determinant must be +/- 1 |
|---|
| 1586 | if (fabs(fabs(cob.determinant()) - 1.0) < 1e-4) |
|---|
| 1587 | return false; |
|---|
| 1588 | |
|---|
| 1589 | return true; |
|---|
| 1590 | } |
|---|
| 1591 | |
|---|
| 1592 | void Xtal::generateValidCOBs() |
|---|
| 1593 | { |
|---|
| 1594 | m_validCOBsGenMutex.lock(); |
|---|
| 1595 | |
|---|
| 1596 | // Has another instance beat us to the punch? |
|---|
| 1597 | if (m_mixMatrices.size()) { |
|---|
| 1598 | m_validCOBsGenMutex.unlock(); |
|---|
| 1599 | return; |
|---|
| 1600 | } |
|---|
| 1601 | |
|---|
| 1602 | Eigen::Matrix3d tmpMat; |
|---|
| 1603 | |
|---|
| 1604 | m_transformationMatrices.clear(); |
|---|
| 1605 | m_transformationMatrices.reserve(32); |
|---|
| 1606 | m_mixMatrices.clear(); |
|---|
| 1607 | m_mixMatrices.reserve(8); |
|---|
| 1608 | |
|---|
| 1609 | // Build list of transformation matrices |
|---|
| 1610 | // First build list of 90 degree rotations |
|---|
| 1611 | tmpMat << 1, 0, 0, 0, 1, 0, 0, 0, 1; m_transformationMatrices<<tmpMat; |
|---|
| 1612 | tmpMat << 1, 0, 0, 0, 0, 1, 0, 1, 0; m_transformationMatrices<<tmpMat; |
|---|
| 1613 | tmpMat << 0, 1, 0, 1, 0, 0, 0, 0, 1; m_transformationMatrices<<tmpMat; |
|---|
| 1614 | tmpMat << 0, 0, 1, 0, 1, 0, 1, 0, 0; m_transformationMatrices<<tmpMat; |
|---|
| 1615 | for (unsigned short int i = 0; i < 4; ++i) { |
|---|
| 1616 | // Now apply all possible reflections to 90 rotations |
|---|
| 1617 | tmpMat <<-1, 0, 0, 0, 1, 0, 0, 0, 1; |
|---|
| 1618 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1619 | tmpMat << 1, 0, 0, 0,-1, 0, 0, 0, 1; |
|---|
| 1620 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1621 | tmpMat << 1, 0, 0, 0, 1, 0, 0, 0,-1; |
|---|
| 1622 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1623 | tmpMat <<-1, 0, 0, 0,-1, 0, 0, 0, 1; |
|---|
| 1624 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1625 | tmpMat <<-1, 0, 0, 0, 1, 0, 0, 0,-1; |
|---|
| 1626 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1627 | tmpMat << 1, 0, 0, 0,-1, 0, 0, 0,-1; |
|---|
| 1628 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1629 | tmpMat <<-1, 0, 0, 0,-1, 0, 0, 0,-1; |
|---|
| 1630 | m_transformationMatrices << (tmpMat * m_transformationMatrices[i]); |
|---|
| 1631 | } |
|---|
| 1632 | |
|---|
| 1633 | // Now build list of mix matrices |
|---|
| 1634 | // Identity |
|---|
| 1635 | tmpMat << 1, 0, 0, 0, 1, 0, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1636 | // Upper triangular mixes |
|---|
| 1637 | tmpMat << 1, 1, 0, 0, 1, 0, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1638 | tmpMat << 1, 1, 1, 0, 1, 0, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1639 | tmpMat << 1, 1, 0, 0, 1, 1, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1640 | tmpMat << 1, 1, 1, 0, 1, 1, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1641 | tmpMat << 1, 0, 1, 0, 1, 0, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1642 | tmpMat << 1, 0, 1, 0, 1, 1, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1643 | tmpMat << 1, 0, 0, 0, 1, 1, 0, 0, 1; m_mixMatrices.append(tmpMat); |
|---|
| 1644 | |
|---|
| 1645 | m_validCOBsGenMutex.unlock(); |
|---|
| 1646 | } |
|---|
| 1647 | |
|---|
| 1648 | Xtal * Xtal::getRandomRepresentation() const |
|---|
| 1649 | { |
|---|
| 1650 | // Cache volume for later sanity checks |
|---|
| 1651 | const double origVolume = cell()->GetCellVolume(); |
|---|
| 1652 | |
|---|
| 1653 | // Randomly select a mix matrix to create a new cell matrix by |
|---|
| 1654 | // taking a linear combination of the current cell vectors |
|---|
| 1655 | const Eigen::Matrix3d &mix |
|---|
| 1656 | (m_mixMatrices[RANDUINT() % m_mixMatrices.size()]); |
|---|
| 1657 | |
|---|
| 1658 | // Build new Xtal with the new basis |
|---|
| 1659 | Xtal *nxtal = new Xtal (this->parent()); |
|---|
| 1660 | nxtal->setCellInfo(Eigen2OB(mix) * this->cell()->GetCellMatrix()); |
|---|
| 1661 | |
|---|
| 1662 | Q_ASSERT_X(StableComp::eq(origVolume, nxtal->cell()->GetCellVolume()), |
|---|
| 1663 | Q_FUNC_INFO, "Randomized cell volume not " |
|---|
| 1664 | "equal to original structure."); |
|---|
| 1665 | |
|---|
| 1666 | // Generate a random translation (i.e. between 0 and 1) |
|---|
| 1667 | const double maxTranslation = getA() + getB() + getC(); |
|---|
| 1668 | const Eigen::Vector3d randTranslation |
|---|
| 1669 | (RANDDOUBLE() * maxTranslation, |
|---|
| 1670 | RANDDOUBLE() * maxTranslation, |
|---|
| 1671 | RANDDOUBLE() * maxTranslation); |
|---|
| 1672 | |
|---|
| 1673 | // Add atoms |
|---|
| 1674 | for (QList<Atom*>::const_iterator it = m_atomList.constBegin(), |
|---|
| 1675 | it_end = m_atomList.constEnd(); it != it_end; ++it) { |
|---|
| 1676 | Atom * atom = nxtal->addAtom(); |
|---|
| 1677 | atom->setAtomicNumber((*it)->atomicNumber()); |
|---|
| 1678 | atom->setPos( (*(*it)->pos()) + randTranslation); |
|---|
| 1679 | } |
|---|
| 1680 | |
|---|
| 1681 | // rotate and wrap: |
|---|
| 1682 | nxtal->rotateCellAndCoordsToStandardOrientation(); |
|---|
| 1683 | nxtal->wrapAtomsToCell(); |
|---|
| 1684 | return nxtal; |
|---|
| 1685 | } |
|---|
| 1686 | |
|---|
| 1687 | Xtal* Xtal::POSCARToXtal(const QString &poscar) |
|---|
| 1688 | { |
|---|
| 1689 | QTextStream ps (&const_cast<QString &>(poscar)); |
|---|
| 1690 | QStringList sl; |
|---|
| 1691 | vector3 v1, v2, v3, pos; |
|---|
| 1692 | Xtal *xtal = new Xtal; |
|---|
| 1693 | |
|---|
| 1694 | ps.readLine(); // title |
|---|
| 1695 | float scale = ps.readLine().toFloat(); // Scale factor |
|---|
| 1696 | sl = ps.readLine().split(QRegExp("\\s+"), QString::SkipEmptyParts); // v1 |
|---|
| 1697 | v1.x() = sl.at(0).toFloat() * scale; |
|---|
| 1698 | v1.y() = sl.at(1).toFloat() * scale; |
|---|
| 1699 | v1.z() = sl.at(2).toFloat() * scale; |
|---|
| 1700 | |
|---|
| 1701 | sl = ps.readLine().split(QRegExp("\\s+"), QString::SkipEmptyParts); // v2 |
|---|
| 1702 | v2.x() = sl.at(0).toFloat() * scale; |
|---|
| 1703 | v2.y() = sl.at(1).toFloat() * scale; |
|---|
| 1704 | v2.z() = sl.at(2).toFloat() * scale; |
|---|
| 1705 | |
|---|
| 1706 | sl = ps.readLine().split(QRegExp("\\s+"), QString::SkipEmptyParts); // v3 |
|---|
| 1707 | v3.x() = sl.at(0).toFloat() * scale; |
|---|
| 1708 | v3.y() = sl.at(1).toFloat() * scale; |
|---|
| 1709 | v3.z() = sl.at(2).toFloat() * scale; |
|---|
| 1710 | |
|---|
| 1711 | xtal->setCellInfo(v1, v2, v3); |
|---|
| 1712 | |
|---|
| 1713 | sl = ps.readLine().split(QRegExp("\\s+"), QString::SkipEmptyParts); // atom types |
|---|
| 1714 | unsigned int numAtomTypes = sl.size(); |
|---|
| 1715 | QList<unsigned int> atomCounts; |
|---|
| 1716 | for (int i = 0; i < numAtomTypes; i++) { |
|---|
| 1717 | atomCounts.append(sl.at(i).toUInt()); |
|---|
| 1718 | } |
|---|
| 1719 | |
|---|
| 1720 | // TODO this will assume fractional coordinates. VASP can use cartesian! |
|---|
| 1721 | ps.readLine(); // direct or cartesian |
|---|
| 1722 | // Atom coords begin |
|---|
| 1723 | Atom *atom; |
|---|
| 1724 | for (unsigned int i = 0; i < numAtomTypes; i++) { |
|---|
| 1725 | for (unsigned int j = 0; j < atomCounts.at(i); j++) { |
|---|
| 1726 | // Actual identity of the atoms doesn't matter for the symmetry |
|---|
| 1727 | // test. Just use (i+1) as the atomic number. |
|---|
| 1728 | atom = xtal->addAtom(); |
|---|
| 1729 | atom->setAtomicNumber(i+1); |
|---|
| 1730 | // Get coords |
|---|
| 1731 | sl = ps.readLine().split(QRegExp("\\s+"), QString::SkipEmptyParts); // coords |
|---|
| 1732 | Eigen::Vector3d pos; |
|---|
| 1733 | pos.x() = sl.at(0).toDouble(); |
|---|
| 1734 | pos.y() = sl.at(1).toDouble(); |
|---|
| 1735 | pos.z() = sl.at(2).toDouble(); |
|---|
| 1736 | atom->setPos(xtal->fracToCart(pos)); |
|---|
| 1737 | } |
|---|
| 1738 | } |
|---|
| 1739 | |
|---|
| 1740 | return xtal; |
|---|
| 1741 | } |
|---|
| 1742 | |
|---|
| 1743 | Xtal* Xtal::POSCARToXtal(QFile *file) |
|---|
| 1744 | { |
|---|
| 1745 | QString poscar; |
|---|
| 1746 | file->open(QFile::ReadOnly); |
|---|
| 1747 | poscar = file->readAll(); |
|---|
| 1748 | file->close(); |
|---|
| 1749 | return POSCARToXtal(poscar); |
|---|
| 1750 | } |
|---|
| 1751 | |
|---|
| 1752 | void Xtal::shortenFractionalVector(Eigen::Vector3d *fracVec) |
|---|
| 1753 | { |
|---|
| 1754 | while (fracVec->x() > 0.5) --fracVec->x(); |
|---|
| 1755 | while (fracVec->x() < -0.5) ++fracVec->x(); |
|---|
| 1756 | while (fracVec->y() > 0.5) --fracVec->y(); |
|---|
| 1757 | while (fracVec->y() < -0.5) ++fracVec->y(); |
|---|
| 1758 | while (fracVec->z() > 0.5) --fracVec->z(); |
|---|
| 1759 | while (fracVec->z() < -0.5) ++fracVec->z(); |
|---|
| 1760 | } |
|---|
| 1761 | |
|---|
| 1762 | } // end namespace XtalOpt |
|---|