1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * 4 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org 5 * 6 * Contact: jmol-developers@lists.sf.net 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 */ 22 23 /* 24 * Copyright (C) 2009 Joerg Meyer, FHI Berlin 25 * 26 * Contact: meyer@fhi-berlin.mpg.de 27 * 28 * This library is free software; you can redistribute it and/or 29 * modify it under the terms of the GNU Lesser General Public 30 * License as published by the Free Software Foundation; either 31 * version 2.1 of the License, or (at your option) any later version. 32 * 33 * This library is distributed in the hope that it will be useful, 34 * but WITHOUT ANY WARRANTY; without even the implied warranty of 35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 36 * Lesser General Public License for more details. 37 * 38 * You should have received a copy of the GNU Lesser General Public 39 * License along with this library; if not, write to the Free Software 40 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 41 */ 42 43 package org.jmol.adapter.readers.xtal; 44 45 import javajs.util.DF; 46 import javajs.util.Lst; 47 import javajs.util.M4; 48 import javajs.util.PT; 49 50 51 import org.jmol.adapter.smarter.AtomSetCollectionReader; 52 import org.jmol.adapter.smarter.Atom; 53 import org.jmol.util.Escape; 54 import org.jmol.util.Logger; 55 import javajs.util.P3; 56 import org.jmol.util.Tensor; 57 import javajs.util.V3; 58 59 60 /** 61 * CASTEP (http://www.castep.org) .cell file format relevant section of .cell 62 * file are included as comments below 63 * 64 * preliminary .castep, .phonon frequency reader 65 * -- hansonr@stolaf.edu 9/2011 66 * -- Many thanks to Keith Refson for his assistance with this implementation 67 * -- atom's mass is encoded as bfactor 68 * -- FILTER options include 69 * "q=n" where n is an integer 70 * "q={1/4 1/4 0}" 71 * "q=ALL" 72 * -- for non-simple fractions, you must use the exact form of the wavevector description: 73 * -- load "xxx.phonon" FILTER "q=(-0.083333 0.083333 0.500000) 74 * -- for simple fractions, you can also just specify SUPERCELL {a b c} where 75 * the number of cells matches a given wavevector -- SUPERCELL {4 4 1}, for example 76 * 77 * note: following was never implemented? 78 * 79 * -- following this with ".1" ".2" etc. gives first, second, third, etc. occurance: 80 * -- load "xxx.phonon" FILTER "q=1.3" .... 81 * -- load "xxx.phonon" FILTER "{0 0 0}.3" .... 82 * 83 * 84 * @author Joerg Meyer, FHI Berlin 2009 (meyer@fhi-berlin.mpg.de) 85 * @version 1.2 86 */ 87 88 public class CastepReader extends AtomSetCollectionReader { 89 90 private static final float RAD_TO_DEG = (float) (180.0 / Math.PI); 91 92 private String[] tokens; 93 94 private boolean isPhonon; 95 private boolean isTS; 96 private boolean isOutput; 97 private boolean isCell; 98 99 private float a, b, c, alpha, beta, gamma; 100 private V3[] abc = new V3[3]; 101 102 private int ac; 103 private P3[] atomPts; 104 105 private boolean havePhonons = false; 106 private String lastQPt; 107 private int qpt2; 108 private V3 desiredQpt; 109 private String desiredQ; 110 111 private String chargeType = "MULL"; 112 113 private boolean isAllQ; 114 115 private boolean haveCharges; 116 117 private String tsType; 118 119 @Override initializeReader()120 public void initializeReader() throws Exception { 121 if (filter != null) { 122 chargeType = getFilter("CHARGE="); 123 if (chargeType != null && chargeType.length() > 4) 124 chargeType = chargeType.substring(0, 4); 125 filter = filter.replace('(', '{').replace(')', '}'); 126 filter = PT.rep(filter, " ", " "); 127 isAllQ = checkFilterKey("Q=ALL"); 128 tsType = getFilter("TSTYPE="); 129 if (!isAllQ && filter.indexOf("{") >= 0) 130 setDesiredQpt(filter.substring(filter.indexOf("{"))); 131 filter = PT.rep(filter, "-PT", ""); 132 } 133 continuing = readFileData(); 134 } 135 setDesiredQpt(String s)136 private void setDesiredQpt(String s) { 137 desiredQpt = new V3(); 138 desiredQ = ""; 139 float num = 1; 140 float denom = 1; 141 int ipt = 0; 142 int xyz = 0; 143 boolean useSpace = (s.indexOf(',') < 0); 144 for (int i = 0; i < s.length(); i++) { 145 char c = s.charAt(i); 146 switch (c) { 147 case '{': 148 ipt = i + 1; 149 num = 1; 150 denom = 1; 151 break; 152 case '/': 153 num = parseFloatStr(s.substring(ipt, i)); 154 ipt = i + 1; 155 denom = 0; 156 break; 157 case ',': 158 case ' ': 159 case '}': 160 if (c == '}') 161 desiredQ = s.substring(0, i + 1); 162 else if ((c == ' ') != useSpace) 163 break; 164 if (denom == 0) { 165 denom = parseFloatStr(s.substring(ipt, i)); 166 } else { 167 num = parseFloatStr(s.substring(ipt, i)); 168 } 169 num /= denom; 170 switch (xyz++) { 171 case 0: 172 desiredQpt.x = num; 173 break; 174 case 1: 175 desiredQpt.y = num; 176 break; 177 case 2: 178 desiredQpt.z = num; 179 break; 180 } 181 denom = 1; 182 if (c == '}') 183 i = s.length(); 184 ipt = i + 1; 185 break; 186 } 187 } 188 Logger.info("Looking for q-pt=" + desiredQpt); 189 } 190 readFileData()191 private boolean readFileData() throws Exception { 192 while (tokenizeCastepCell() > 0) 193 if (tokens.length >= 2 && tokens[0].equalsIgnoreCase("%BLOCK")) { 194 Logger.info(line); 195 /* 196 %BLOCK LATTICE_ABC 197 ang 198 16.66566792 8.33283396 16.82438907 199 90.0 90.0 90.0 200 %ENDBLOCK LATTICE_ABC 201 */ 202 if (tokens[1].equalsIgnoreCase("LATTICE_ABC")) { 203 readLatticeAbc(); 204 continue; 205 } 206 /* 207 %BLOCK LATTICE_CART 208 ang 209 16.66566792 0.0 0.0 210 0.0 8.33283396 0.0 211 0.0 0.0 16.82438907 212 %ENDBLOCK LATTICE_CART 213 */ 214 if (tokens[1].equalsIgnoreCase("LATTICE_CART")) { 215 readLatticeCart(); 216 continue; 217 } 218 219 /* coordinates are set immediately */ 220 /* 221 %BLOCK POSITIONS_FRAC 222 Pd 0.0 0.0 0.0 223 %ENDBLOCK POSITIONS_FRAC 224 */ 225 if (tokens[1].equalsIgnoreCase("POSITIONS_FRAC")) { 226 setFractionalCoordinates(true); 227 readPositionsFrac(); 228 continue; 229 } 230 /* 231 %BLOCK POSITIONS_ABS 232 ang 233 Pd 0.00000000 0.00000000 0.00000000 234 %ENDBLOCK POSITIONS_ABS 235 */ 236 if (tokens[1].equalsIgnoreCase("POSITIONS_ABS")) { 237 setFractionalCoordinates(false); 238 readPositionsAbs(); 239 continue; 240 } 241 } 242 if (isPhonon || isOutput || isTS) { 243 if (isPhonon) { 244 isTrajectory = (desiredVibrationNumber <= 0); 245 asc.allowMultiple = false; 246 } 247 return true; // use checkLine 248 } 249 return false; 250 } 251 252 @Override checkLine()253 protected boolean checkLine() throws Exception { 254 // only for .phonon, castep output, or other BEGIN HEADER type files 255 if (isOutput) { 256 if (line.contains("Real Lattice(A)")) { 257 readOutputUnitCell(); 258 } else if (line.contains("Fractional coordinates of atoms")) { 259 if (doGetModel(++modelNumber, null)) { 260 readOutputAtoms(); 261 } 262 } else if (doProcessLines && 263 (line.contains("Atomic Populations (Mulliken)") 264 || line.contains("Hirshfield Charge (e)"))) { 265 readOutputCharges(); 266 } else if (doProcessLines && line.contains("Born Effective Charges")) { 267 readOutputBornChargeTensors(); 268 } else if (line.contains("Final energy ")) { // not "Final energy, E" 269 readEnergy(3, null); 270 } else if (line.contains("Dispersion corrected final energy*")) { 271 readEnergy(5, null); 272 } else if (line.contains("Total energy corrected")) { 273 readEnergy(8, null); 274 } 275 return true; 276 } 277 278 // phonon only from here 279 if (line.contains("<-- E")) { 280 readPhononTrajectories(); 281 return true; 282 } 283 if (line.indexOf("Unit cell vectors") == 1) { 284 readPhononUnitCell(); 285 return true; 286 } 287 if (line.indexOf("Fractional Co-ordinates") >= 0) { 288 readPhononFractionalCoord(); 289 return true; 290 } 291 if (line.indexOf("q-pt") >= 0) { 292 readPhononFrequencies(); 293 return true; 294 } 295 return true; 296 } 297 298 /* 299 Real Lattice(A) Reciprocal Lattice(1/A) 300 2.6954645 2.6954645 0.0000000 1.1655107 1.1655107 -1.1655107 301 2.6954645 0.0000000 2.6954645 1.1655107 -1.1655107 1.1655107 302 0.0000000 2.6954645 2.6954645 -1.1655107 1.1655107 1.1655107 303 */ 304 readOutputUnitCell()305 private void readOutputUnitCell() throws Exception { 306 applySymmetryAndSetTrajectory(); 307 asc.newAtomSetClear(false); 308 setFractionalCoordinates(true); 309 abc = read3Vectors(false); 310 setLatticeVectors(); 311 } 312 313 /* 314 x Element Atom Fractional coordinates of atoms x 315 x Number u v w x 316 x----------------------------------------------------------x 317 x Si 1 0.000000 0.000000 0.000000 x 318 x Si 2 0.250000 0.250000 0.250000 x 319 320 */ readOutputAtoms()321 private void readOutputAtoms() throws Exception { 322 readLines(2); 323 while (rd().indexOf("xxx") < 0) { 324 Atom atom = new Atom(); 325 tokens = getTokens(); 326 atom.elementSymbol = tokens[1]; 327 atom.atomName = tokens[1] + tokens[2]; 328 asc.addAtomWithMappedName(atom); 329 setAtomCoordTokens(atom, tokens, 3); 330 } 331 332 } 333 readEnergy(int pt, String prefix)334 private void readEnergy(int pt, String prefix) throws Exception { 335 if (isTrajectory) 336 applySymmetryAndSetTrajectory(); 337 tokens = getTokens(); 338 try { 339 Double energy = Double.valueOf(Double.parseDouble(tokens[pt])); 340 asc.setAtomSetName(prefix + "Energy = " + energy + " eV"); 341 asc.setAtomSetEnergy("" + energy, energy.floatValue()); 342 asc.setCurrentModelInfo("Energy", energy); 343 } catch (Exception e) { 344 appendLoadNote("CASTEP Energy could not be read: " + line); 345 } 346 347 // this change, 4/16/2013, broke the optimization reader 348 // one should never start a new atom set without actually 349 // adding new atoms. The reader will apply symmetry in the 350 // finalization stage. 351 // 352 // /* 353 // is better to do this also here 354 // in case the output is only a 355 // geometry optimization and not 356 // both volume and geometry 357 // */ 358 // applySymmetryAndSetTrajectory(); 359 // asc.newAtomSetClear(false); 360 // setLatticeVectors(); 361 } 362 readPhononTrajectories()363 private void readPhononTrajectories() throws Exception { 364 if (!isTS) // force this only for .phonon, not .ts 365 isTrajectory = (desiredVibrationNumber <= 0); 366 if (isTrajectory) 367 asc.setTrajectory(); 368 doApplySymmetry = true; 369 while (line != null && line.contains("<-- E")) { 370 boolean skip = (isTS && tsType != null && prevline.indexOf(tsType) < 0); 371 if (!skip) { 372 asc.newAtomSetClear(false); 373 if (isTS) 374 readEnergy(0, PT.getTokens(prevline + " -")[0] + " "); 375 discardLinesUntilContains("<-- h"); 376 setSpaceGroupName("P1"); 377 abc = read3Vectors(true); 378 setLatticeVectors(); 379 setFractionalCoordinates(false); 380 discardLinesUntilContains("<-- R"); 381 while (line != null && line.contains("<-- R")) { 382 tokens = getTokens(); 383 setAtomCoordScaled(null, tokens, 2, ANGSTROMS_PER_BOHR).elementSymbol = tokens[0]; 384 rd(); 385 } 386 applySymmetryAndSetTrajectory(); 387 } 388 discardLinesUntilContains("<-- E"); 389 } 390 } 391 392 @Override finalizeSubclassReader()393 protected void finalizeSubclassReader() throws Exception { 394 if (isPhonon || isOutput || isTS) { 395 isTrajectory = false; 396 } else { 397 doApplySymmetry = true; 398 setLatticeVectors(); 399 int nAtoms = asc.ac; 400 /* 401 * this needs to be run either way (i.e. even if coordinates are already 402 * fractional) - to satisfy the logic in AtomSetCollectionReader() 403 */ 404 for (int i = 0; i < nAtoms; i++) 405 setAtomCoord(asc.atoms[i]); 406 } 407 finalizeReaderASCR(); 408 } 409 setLatticeVectors()410 private void setLatticeVectors() { 411 if (abc[0] == null) { 412 setUnitCell(a, b, c, alpha, beta, gamma); 413 return; 414 } 415 float[] lv = new float[3]; 416 for (int i = 0; i < 3; i++) { 417 lv[0] = abc[i].x; 418 lv[1] = abc[i].y; 419 lv[2] = abc[i].z; 420 addExplicitLatticeVector(i, lv, 0); 421 } 422 } 423 readLatticeAbc()424 private void readLatticeAbc() throws Exception { 425 if (tokenizeCastepCell() == 0) 426 return; 427 float factor = readLengthUnit(tokens[0]); 428 if (tokens.length >= 3) { 429 a = parseFloatStr(tokens[0]) * factor; 430 b = parseFloatStr(tokens[1]) * factor; 431 c = parseFloatStr(tokens[2]) * factor; 432 } else { 433 Logger 434 .warn("error reading a,b,c in %BLOCK LATTICE_ABC in CASTEP .cell file"); 435 return; 436 } 437 438 if (tokenizeCastepCell() == 0) 439 return; 440 if (tokens.length >= 3) { 441 alpha = parseFloatStr(tokens[0]); 442 beta = parseFloatStr(tokens[1]); 443 gamma = parseFloatStr(tokens[2]); 444 } else { 445 Logger 446 .warn("error reading alpha,beta,gamma in %BLOCK LATTICE_ABC in CASTEP .cell file"); 447 } 448 } 449 readLatticeCart()450 private void readLatticeCart() throws Exception { 451 if (tokenizeCastepCell() == 0) 452 return; 453 float factor = readLengthUnit(tokens[0]); 454 float x, y, z; 455 for (int i = 0; i < 3; i++) { 456 if (tokens.length >= 3) { 457 x = parseFloatStr(tokens[0]) * factor; 458 y = parseFloatStr(tokens[1]) * factor; 459 z = parseFloatStr(tokens[2]) * factor; 460 abc[i] = V3.new3(x, y, z); 461 } else { 462 Logger.warn("error reading coordinates of lattice vector " 463 + Integer.toString(i + 1) 464 + " in %BLOCK LATTICE_CART in CASTEP .cell file"); 465 return; 466 } 467 if (tokenizeCastepCell() == 0) 468 return; 469 } 470 a = abc[0].length(); 471 b = abc[1].length(); 472 c = abc[2].length(); 473 alpha = (abc[1].angle(abc[2]) * RAD_TO_DEG); 474 beta = (abc[2].angle(abc[0]) * RAD_TO_DEG); 475 gamma = (abc[0].angle(abc[1]) * RAD_TO_DEG); 476 } 477 readPositionsFrac()478 private void readPositionsFrac() throws Exception { 479 if (tokenizeCastepCell() == 0) 480 return; 481 readAtomData(1.0f); 482 } 483 readPositionsAbs()484 private void readPositionsAbs() throws Exception { 485 if (tokenizeCastepCell() == 0) 486 return; 487 float factor = readLengthUnit(tokens[0]); 488 readAtomData(factor); 489 } 490 491 /* 492 to be kept in sync with Utilities/io.F90 493 */ 494 private final static String[] lengthUnitIds = { "bohr", "m", "cm", "nm", 495 "ang", "a0" }; 496 497 private final static float[] lengthUnitFactors = { ANGSTROMS_PER_BOHR, 1E10f, 498 1E8f, 1E1f, 1.0f, ANGSTROMS_PER_BOHR }; 499 readLengthUnit(String units)500 private float readLengthUnit(String units) throws Exception { 501 float factor = 1.0f; 502 for (int i = 0; i < lengthUnitIds.length; i++) 503 if (units.equalsIgnoreCase(lengthUnitIds[i])) { 504 factor = lengthUnitFactors[i]; 505 tokenizeCastepCell(); 506 break; 507 } 508 return factor; 509 } 510 readAtomData(float factor)511 private void readAtomData(float factor) throws Exception { 512 do { 513 if (tokens.length >= 4) { 514 Atom atom = asc.addNewAtom(); 515 int pt = tokens[0].indexOf(":"); 516 if (pt >= 0) { 517 atom.elementSymbol = tokens[0].substring(0, pt); 518 atom.atomName = tokens[0]; 519 } else { 520 atom.elementSymbol = tokens[0]; 521 } 522 523 atom.set(parseFloatStr(tokens[1]), parseFloatStr(tokens[2]), 524 parseFloatStr(tokens[3])); 525 atom.scale(factor); 526 } else { 527 Logger.warn("cannot read line with CASTEP atom data: " + line); 528 } 529 } while (tokenizeCastepCell() > 0 530 && !tokens[0].equalsIgnoreCase("%ENDBLOCK")); 531 } 532 tokenizeCastepCell()533 private int tokenizeCastepCell() throws Exception { 534 while (rd() != null) { 535 if ((line = line.trim()).length() == 0 || line.startsWith("#") 536 || line.startsWith("!")) 537 continue; 538 if (!isCell) { 539 if (line.startsWith("%")) { 540 isCell = true; 541 break; 542 } 543 if (line.startsWith("LST")) { 544 isTS = true; 545 Logger.info("reading CASTEP .ts file"); 546 return -1; 547 } 548 if (line.startsWith("BEGIN header")) { 549 isPhonon = true; 550 Logger.info("reading CASTEP .phonon file"); 551 return -1; 552 } 553 if (line.contains("CASTEP")) { 554 isOutput = true; 555 Logger.info("reading CASTEP .castep file"); 556 return -1; 557 } 558 } 559 break; 560 } 561 return (line == null ? 0 : (tokens = getTokens()).length); 562 } 563 564 /* 565 Born Effective Charges 566 ---------------------- 567 O 1 -5.27287 -0.15433 1.86524 568 -0.32884 -1.78984 0.13678 569 1.81939 0.06085 -1.80221 570 */ readOutputBornChargeTensors()571 private void readOutputBornChargeTensors() throws Exception { 572 if (rd().indexOf("--------") < 0) 573 return; 574 Atom[] atoms = asc.atoms; 575 appendLoadNote("Ellipsoids: Born Charge Tensors"); 576 while (rd().indexOf('=') < 0) 577 getTensor(atoms[readOutputAtomIndex()], line.substring(12)); 578 } 579 580 readOutputAtomIndex()581 private int readOutputAtomIndex() { 582 tokens = getTokens(); 583 return asc.getAtomIndex(tokens[0] + tokens[1]); 584 } 585 getTensor(Atom atom, String line0)586 private void getTensor(Atom atom, String line0) throws Exception { 587 float[] data = new float[9]; 588 double[][] a = new double[3][3]; 589 fillFloatArray(line0, 0, data); 590 Logger.info("tensor " + atom.atomName 591 + "\t" +Escape.eAF(data)); 592 for (int p = 0, i = 0; i < 3; i++) 593 for (int j = 0; j < 3; j++) 594 a[i][j] = data[p++]; 595 atom.addTensor((new Tensor()).setFromAsymmetricTensor(a, "charge", atom.atomName + " " + line0), null, false); 596 if (!haveCharges) 597 appendLoadNote("Ellipsoids set \"charge\": Born Effective Charges"); 598 haveCharges = true; 599 } 600 601 /* 602 Hirshfeld Analysis 603 ------------------ 604 Species Ion Hirshfeld Charge (e) 605 ====================================== 606 H 1 0.05 607 ... 608 O 6 -0.24 609 O 7 -0.25 610 O 8 -0.25 611 ====================================== 612 613 or 614 615 Atomic Populations (Mulliken) 616 ----------------------------- 617 Species Ion s p d f Total Charge (e) 618 ============================================================== 619 O 1 1.86 4.84 0.00 0.00 6.70 -0.70 620 .. 621 Ti 3 2.23 6.33 2.12 0.00 10.67 1.33 622 Ti 4 2.23 6.33 2.12 0.00 10.67 1.33 623 ============================================================== 624 625 */ 626 627 /** 628 * read Mulliken or Hirshfield charges 629 * @throws Exception 630 */ readOutputCharges()631 private void readOutputCharges() throws Exception { 632 if (line.toUpperCase().indexOf(chargeType ) < 0) 633 return; 634 Logger.info("reading charges: " + line); 635 readLines(2); 636 boolean haveSpin = (line.indexOf("Spin") >= 0); 637 rd(); 638 Atom[] atoms = asc.atoms; 639 float[] spins = (haveSpin ? new float[atoms.length] : null); 640 if (spins != null) 641 for (int i = 0; i < spins.length; i++) 642 spins[i] = 0; 643 while (rd() != null && line.indexOf('=') < 0) { 644 int index = readOutputAtomIndex(); 645 float charge = parseFloatStr(tokens[haveSpin ? tokens.length - 2 : tokens.length - 1]); 646 atoms[index].partialCharge = charge; 647 if (haveSpin) 648 spins[index] = parseFloatStr(tokens[tokens.length - 1]); 649 } 650 if (haveSpin) 651 asc.setAtomProperties("spin", spins, -1, false); 652 653 } 654 655 656 //////////// phonon code //////////// 657 658 /* 659 Unit cell vectors (A) 660 0.000000 1.819623 1.819623 661 1.819623 0.000000 1.819623 662 1.819623 1.819623 0.000000 663 Fractional Co-ordinates 664 1 0.000000 0.000000 0.000000 B 10.811000 665 2 0.250000 0.250000 0.250000 N 14.006740 666 */ readPhononUnitCell()667 private void readPhononUnitCell() throws Exception { 668 abc = read3Vectors(line.indexOf("bohr") >= 0); 669 setSpaceGroupName("P1"); 670 setLatticeVectors(); 671 } 672 readPhononFractionalCoord()673 private void readPhononFractionalCoord() throws Exception { 674 setFractionalCoordinates(true); 675 while (rd() != null && line.indexOf("END") < 0) { 676 tokens = getTokens(); 677 addAtomXYZSymName(tokens, 1, tokens[4], null).bfactor = parseFloatStr(tokens[5]); // mass, actually 678 } 679 ac = asc.ac; 680 // we collect the atom points, because any supercell business 681 // will trash those, and we need the originals 682 atomPts = new P3[ac]; 683 Atom[] atoms = asc.atoms; 684 for (int i = 0; i < ac; i++) 685 atomPts[i] = P3.newP(atoms[i]); 686 } 687 688 /* 689 q-pt= 1 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 690 1 58.268188 0.0000000 691 2 58.268188 0.0000000 692 3 58.292484 0.0000000 693 4 1026.286406 13.9270643 694 5 1026.286406 13.9270643 695 6 1262.072445 13.9271267 696 Phonon Eigenvectors 697 Mode Ion X Y Z 698 1 1 -0.188759409143 0.000000000000 0.344150676582 0.000000000000 -0.532910085817 0.000000000000 699 1 2 -0.213788416373 0.000000000000 0.389784162147 0.000000000000 -0.603572578624 0.000000000000 700 2 1 -0.506371267280 0.000000000000 -0.416656077168 0.000000000000 -0.089715190073 0.000000000000 701 2 2 -0.573514781701 0.000000000000 -0.471903590472 0.000000000000 -0.101611191184 0.000000000000 702 3 1 0.381712598768 0.000000000000 -0.381712598812 0.000000000000 -0.381712598730 0.000000000000 703 3 2 0.433161430960 0.000000000000 -0.433161431010 0.000000000000 -0.433161430917 0.000000000000 704 4 1 0.431092607594 0.000000000000 -0.160735361462 0.000000000000 0.591827969056 0.000000000000 705 4 2 -0.380622988260 0.000000000000 0.141917473232 0.000000000000 -0.522540461492 0.000000000000 706 5 1 0.434492641457 0.000000000000 0.590583470288 0.000000000000 -0.156090828832 0.000000000000 707 5 2 -0.383624967478 0.000000000000 -0.521441660837 0.000000000000 0.137816693359 0.000000000000 708 6 1 0.433161430963 0.000000000000 -0.433161430963 0.000000000000 -0.433161430963 0.000000000000 709 6 2 -0.381712598770 0.000000000000 0.381712598770 0.000000000000 0.381712598770 0.000000000000 710 */ 711 readPhononFrequencies()712 private void readPhononFrequencies() throws Exception { 713 tokens = getTokens(); 714 V3 v = new V3(); 715 V3 qvec = V3.new3(parseFloatStr(tokens[2]), parseFloatStr(tokens[3]), 716 parseFloatStr(tokens[4])); 717 String fcoord = getFractionalCoord(qvec); 718 String qtoks = "{" + tokens[2] + " " + tokens[3] + " " + tokens[4] + "}"; 719 if (fcoord == null) 720 fcoord = qtoks; 721 else 722 fcoord = "{" + fcoord + "}"; 723 boolean isOK = isAllQ; 724 boolean isSecond = (tokens[1].equals(lastQPt)); 725 qpt2 = (isSecond ? qpt2 + 1 : 1); 726 727 lastQPt = tokens[1]; 728 //TODO not quite right: can have more than two options. 729 if (!isOK && checkFilterKey("Q=")) { 730 // check for an explicit q=n or q={1/4 1/2 1/4} 731 if (desiredQpt != null) { 732 v.sub2(desiredQpt, qvec); 733 if (v.length() < 0.001f) 734 fcoord = desiredQ; 735 } 736 isOK = (checkFilterKey("Q=" + fcoord + "." + qpt2 + ";") 737 || checkFilterKey("Q=" + lastQPt + "." + qpt2 + ";") 738 || !isSecond && checkFilterKey("Q=" + fcoord + ";") 739 || !isSecond && checkFilterKey("Q=" + lastQPt + ";")); 740 if (!isOK) 741 return; 742 } 743 boolean isGammaPoint = (qvec.length() == 0); 744 float nx = 1, ny = 1, nz = 1; 745 if (ptSupercell != null && !isOK && !isSecond) { 746 matSupercell = new M4(); 747 matSupercell.m00 = ptSupercell.x; 748 matSupercell.m11 = ptSupercell.y; 749 matSupercell.m22 = ptSupercell.z; 750 matSupercell.m33 = 1; 751 Logger.info("Using supercell \n" + matSupercell); 752 nx = ptSupercell.x; 753 ny = ptSupercell.y; 754 nz = ptSupercell.z; 755 // only select corresponding phonon vector 756 // relating to this supercell -- one that has integral dot product 757 float dx = (qvec.x == 0 ? 1 : qvec.x) * nx; 758 float dy = (qvec.y == 0 ? 1 : qvec.y) * ny; 759 float dz = (qvec.z == 0 ? 1 : qvec.z) * nz; 760 if ((nx != 1 || ny != 1 || nz != 1) && isGammaPoint || !isInt(dx) || !isInt(dy) || !isInt(dz)) 761 return; 762 isOK = true; 763 } 764 if (ptSupercell == null || !havePhonons) 765 appendLoadNote(line); 766 if (!isOK && isSecond) 767 return; 768 if (!isOK && (ptSupercell == null) == !isGammaPoint) 769 return; 770 if (havePhonons && !isAllQ) 771 return; 772 havePhonons = true; 773 String qname = "q=" + lastQPt + " " + fcoord; 774 applySymmetryAndSetTrajectory(); 775 if (isGammaPoint) 776 qvec = null; 777 Lst<Float> freqs = new Lst<Float>(); 778 while (rd() != null && line.indexOf("Phonon") < 0) { 779 tokens = getTokens(); 780 freqs.addLast(Float.valueOf(parseFloatStr(tokens[1]))); 781 } 782 rd(); 783 int frequencyCount = freqs.size(); 784 float[] data = new float[8]; 785 V3 t = new V3(); 786 asc.setCollectionName(qname); 787 for (int i = 0; i < frequencyCount; i++) { 788 if (!doGetVibration(++vibrationNumber)) { 789 for (int j = 0; j < ac; j++) 790 rd(); 791 continue; 792 } 793 if (desiredVibrationNumber <= 0) { 794 if (!isTrajectory) { 795 cloneLastAtomSet(ac, atomPts); 796 applySymmetryAndSetTrajectory(); 797 } 798 } 799 symmetry = asc.getSymmetry(); 800 int iatom = asc.getLastAtomSetAtomIndex(); 801 float freq = freqs.get(i).floatValue(); 802 Atom[] atoms = asc.atoms; 803 int aCount = asc.ac; 804 for (int j = 0; j < ac; j++) { 805 fillFloatArray(null, 0, data); 806 for (int k = iatom++; k < aCount; k++) 807 if (atoms[k].atomSite == j) { 808 t.sub2(atoms[k], atoms[atoms[k].atomSite]); 809 // for supercells, fractional coordinates end up 810 // in terms of the SUPERCELL and need to be transformed. 811 // TODO: UNTESTED 812 if (matSupercell != null) 813 matSupercell.rotTrans(t); 814 setPhononVector(data, atoms[k], t, qvec, v); 815 asc.addVibrationVectorWithSymmetry(k, v.x, v.y, v.z, true); 816 } 817 } 818 if (isTrajectory) 819 asc.setTrajectory(); 820 asc.setAtomSetFrequency(vibrationNumber, null, null, "" + freq, null); 821 asc.setAtomSetName(DF.formatDecimal(freq, 2) 822 + " cm-1 " + qname); 823 } 824 } 825 826 private M4 matSupercell; 827 getFractionalCoord(V3 qvec)828 private String getFractionalCoord(V3 qvec) { 829 return (symmetry != null && isInt(qvec.x * 12) 830 && isInt(qvec.y * 12) && isInt(qvec.z * 12) ? symmetry.fcoord(qvec) 831 : null); 832 } 833 isInt(float f)834 private static boolean isInt(float f) { 835 return (Math.abs(f - Math.round(f)) < 0.001f); 836 } 837 838 private static final double TWOPI = Math.PI * 2; 839 840 /** 841 * transform complex vibration vector to a real vector by applying the 842 * appropriate translation, storing the results in v 843 * 844 * @param data 845 * from .phonon line parsed for floats 846 * @param atom 847 * @param rTrans 848 * translation vector in unit fractional coord 849 * @param qvec 850 * q point vector 851 * @param v 852 * return vector 853 */ setPhononVector(float[] data, Atom atom, V3 rTrans, V3 qvec, V3 v)854 private void setPhononVector(float[] data, Atom atom, V3 rTrans, 855 V3 qvec, V3 v) { 856 // complex[r/i] vx = data[2/3], vy = data[4/5], vz = data[6/7] 857 if (qvec == null) { 858 v.set(data[2], data[4], data[6]); 859 } else { 860 // from CASTEP ceteprouts.pm: 861 // $phase = $qptx*$$sh[0] + $qpty*$$sh[1] + $qptz*$$sh[2]; 862 // $cosph = cos($twopi*$phase); $sinph = sin($twopi*$phase); 863 // push @$pertxo, $cosph*$$pertx_r[$iat] - $sinph*$$pertx_i[$iat]; 864 // push @$pertyo, $cosph*$$perty_r[$iat] - $sinph*$$perty_i[$iat]; 865 // push @$pertzo, $cosph*$$pertz_r[$iat] - $sinph*$$pertz_i[$iat]; 866 867 double phase = qvec.dot(rTrans); 868 double cosph = Math.cos(TWOPI * phase); 869 double sinph = Math.sin(TWOPI * phase); 870 v.x = (float) (cosph * data[2] - sinph * data[3]); 871 v.y = (float) (cosph * data[4] - sinph * data[5]); 872 v.z = (float) (cosph * data[6] - sinph * data[7]); 873 } 874 v.scale((float) Math.sqrt(1 / atom.bfactor)); // mass stored in bfactor 875 } 876 877 } 878