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