PageRenderTime 80ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/source/RMG/jing/chem/QMTP.java

https://github.com/rwest/RMG-Java
Java | 3717 lines | 2641 code | 57 blank | 1019 comment | 766 complexity | 7d2a8e4aaaf0e894bcd0c17001f4c8b5 MD5 | raw file
Possible License(s): LGPL-2.1
  1. // //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // RMG - Reaction Mechanism Generator
  4. //
  5. // Copyright (c) 2002-2011 Prof. William H. Green (whgreen@mit.edu) and the
  6. // RMG Team (rmg_dev@mit.edu)
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a
  9. // copy of this software and associated documentation files (the "Software"),
  10. // to deal in the Software without restriction, including without limitation
  11. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  12. // and/or sell copies of the Software, and to permit persons to whom the
  13. // Software is furnished to do so, subject to the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included in
  16. // all copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. // DEALINGS IN THE SOFTWARE.
  25. //
  26. // //////////////////////////////////////////////////////////////////////////////
  27. package jing.chem;
  28. import java.util.*;
  29. import jing.chemParser.ChemParser;
  30. import jing.chemUtil.*;
  31. import jing.param.*;
  32. import java.io.File;
  33. import java.io.FileReader;
  34. import java.io.FileWriter;
  35. import java.io.IOException;
  36. import java.io.BufferedReader;
  37. import java.io.InputStream;
  38. import java.io.InputStreamReader;
  39. import jing.rxnSys.Logger;
  40. // quantum mechanics thermo property estimator; analog of GATP
  41. public class QMTP implements GeneralGAPP {
  42. final protected static double ENTHALPY_HYDROGEN = 52.1; // needed for HBI
  43. private static QMTP INSTANCE = new QMTP(); // ## attribute INSTANCE
  44. protected static PrimaryThermoLibrary primaryLibrary;// Note: may be able to separate this out into GeneralGAPP, as
  45. // this is common to both GATP and QMTP
  46. public static String qmfolder = System.getProperty("RMG.qmCalculationsDir"); // location for Gaussian/mopac output
  47. protected LinkedHashMap<String, ThermoData> qmLibrary = new LinkedHashMap<String, ThermoData>();
  48. // protected static LinkedHashMap library; //as above, may be able to move this and associated functions to GeneralGAPP
  49. // (and possibly change from "x implements y" to "x extends y"), as it is common to both GATP and QMTP
  50. protected Vector<String> failedQm = new Vector<String>();
  51. protected ThermoGAGroupLibrary thermoLibrary; // needed for HBI
  52. public static String qmprogram = "both";// the qmprogram can be "mopac", "gaussian03", "both" (MOPAC and Gaussian),
  53. // or "mm4"/"mm4hr"
  54. public static boolean usePolar = false; // use polar keyword in MOPAC
  55. public static boolean useCanTherm = true; // whether to use CanTherm in MM4 cases for interpreting output via
  56. // force-constant matrix; this will hopefully avoid zero frequency issues
  57. public static boolean useHindRot = false;// whether to use HinderedRotor scans with MM4 (requires useCanTherm=true)
  58. public static boolean keepQMfiles = true; // whether to keep all the Qmfiles generated by Gaussian/mopac or not
  59. public static int connectivityCheck = 0; // level of connectivity checking; 0: "off": no checking; 1: "check":
  60. // (print warning if connectivity doesn't seem to match); 2: "confirm": consider the run a failure if the connectivity
  61. // doesn't seem to match
  62. public static double R = 1.9872; // ideal gas constant in cal/mol-K (does this appear elsewhere in RMG, so I don't
  63. // need to reuse it?)
  64. public static double Hartree_to_kcal = 627.5095; // conversion from Hartree to kcal/mol taken from Gaussian thermo
  65. // white paper
  66. public static double Na = 6.02214179E23;// Avagadro's number; cf.
  67. // http://physics.nist.gov/cgi-bin/cuu/Value?na|search_for=physchem_in!
  68. public static double k = 1.3806504E-23;// Boltzmann's constant in J/K; cf.
  69. // http://physics.nist.gov/cgi-bin/cuu/Value?na|search_for=physchem_in!
  70. public static double h = 6.62606896E-34;// Planck's constant in J-s; cf.
  71. // http://physics.nist.gov/cgi-bin/cuu/Value?h|search_for=universal_in!
  72. public static double c = 299792458. * 100;// speed of light in vacuum in cm/s, cf.
  73. // http://physics.nist.gov/cgi-bin/cuu/Value?c|search_for=universal_in!
  74. public static double deltaTheta = 5.0;// degree increment for rotor scans when using useHindRot
  75. // Constructors
  76. // ## operation QMTP()
  77. private QMTP() {
  78. initializePrimaryThermoLibrary();
  79. initalizeQmLibrary();
  80. }
  81. public String getQmMethod()
  82. throws CouldNotDetermineQMMethodException {
  83. String qmProgram = qmprogram;
  84. String qmMethod = "";
  85. if (qmProgram.equals("mm4") || qmProgram.equals("mm4hr")) {
  86. qmMethod = "mm4";
  87. if (qmProgram.equals("mm4hr"))
  88. useHindRot = true;
  89. } else if (qmProgram.equals("mopac")) {
  90. qmMethod = "pm7"; // may eventually want to pass this to various functions to choose which "sub-function" to
  91. // call
  92. } else if (qmProgram.equals("gaussian03") || qmProgram.equals("both")) {
  93. qmMethod = "pm3";
  94. } else {
  95. throw new CouldNotDetermineQMMethodException();
  96. }
  97. return qmMethod;
  98. }
  99. public ThermoData generateFakeThermoData() {
  100. ThermoData result = null;
  101. ThermoGAValue impossible = new ThermoGAValue(1000
  102. , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
  103. result.plus(impossible);
  104. return result;
  105. }
  106. // ## operation generateThermoData(ChemGraph)
  107. public ThermoData generateThermoData(ChemGraph p_chemGraph) {
  108. if (!keepQMfiles) {
  109. cleanQmFiles();
  110. }
  111. // #[ operation generateThermoData(ChemGraph)
  112. // first, check for thermo data in the primary thermo library and library (?); if it is there, use it
  113. ThermoData result = null;
  114. ThermoData tmpTherm = primaryLibrary.getThermoData(p_chemGraph
  115. .getGraph());
  116. // Logger.info(result);
  117. if (tmpTherm != null) {
  118. result = tmpTherm.copyWithExtraInfo();// use a copy of the object!; that way, subsequent modifications of
  119. // this object don't change the primary thermo library
  120. p_chemGraph.fromprimarythermolibrary = true;
  121. return result;
  122. }
  123. result = new ThermoData();
  124. int maxRadNumForQM = Global.maxRadNumForQM;
  125. if (p_chemGraph.getRadicalNumber() > maxRadNumForQM)// use HBI if the molecule has more radicals than
  126. // maxRadNumForQM; this is helpful because ; also MM4 (and MM3) look like they may have issues with radicals
  127. {// this code is based closely off of GATP saturation (in getGAGroup()), but there are some modifications,
  128. // particularly for symmetry correction
  129. // find the initial symmetry number
  130. int sigmaRadical = p_chemGraph.getSymmetryNumber();
  131. Graph g = p_chemGraph.getGraph();
  132. LinkedHashMap oldCentralNode = (LinkedHashMap) (p_chemGraph.getCentralNode())
  133. .clone();
  134. // saturate radical site
  135. int max_radNum_molecule = ChemGraph.getMAX_RADICAL_NUM();
  136. int max_radNum_atom = Math.min(8, max_radNum_molecule);
  137. int[] idArray = new int[max_radNum_molecule];
  138. Atom[] atomArray = new Atom[max_radNum_molecule];
  139. Node[][] newnode = new Node[max_radNum_molecule][max_radNum_atom];
  140. int radicalSite = 0;
  141. Iterator iter = p_chemGraph.getNodeList();
  142. FreeElectron satuated = FreeElectron.make("0");
  143. while (iter.hasNext()) {
  144. Node node = (Node) iter.next();
  145. Atom atom = (Atom) node.getElement();
  146. if (atom.isRadical()) {
  147. radicalSite++;
  148. // save the old radical atom
  149. idArray[radicalSite - 1] = node.getID().intValue();
  150. atomArray[radicalSite - 1] = atom;
  151. // new a satuated atom and replace the old one
  152. Atom newAtom = Atom.make(atom.getChemElement(), satuated);
  153. node.setElement(newAtom);
  154. node.updateFeElement();
  155. }
  156. }
  157. // add H to saturate chem graph
  158. Atom H = Atom.make(ChemElement.make("H"), satuated);
  159. Bond S = Bond.make("S");
  160. for (int i = 0; i < radicalSite; i++) {
  161. Node node = p_chemGraph.getNodeAt(idArray[i]);
  162. Atom atom = atomArray[i];
  163. int HNum = atom.getRadicalNumber();
  164. for (int j = 0; j < HNum; j++) {
  165. newnode[i][j] = g.addNode(H);
  166. g.addArcBetween(node, S, newnode[i][j]);
  167. }
  168. node.updateFgElement();
  169. }
  170. // find the saturated symmetry number
  171. int sigmaSaturated = p_chemGraph.getSymmetryNumber();
  172. // result = generateThermoData(g);//I'm not sure what GATP does, but this recursive calling will use HBIs on
  173. // saturated species if it exists in PrimaryThermoLibrary
  174. // check the primary thermo library for the saturated graph
  175. tmpTherm = primaryLibrary.getThermoData(p_chemGraph.getGraph());
  176. // Logger.info(result);
  177. if (tmpTherm != null) {
  178. result = tmpTherm.copyWithExtraInfo();// use a copy of the object!; that way, subsequent modifications
  179. // of this object don't change the primary thermo library
  180. p_chemGraph.fromprimarythermolibrary = false;// we don't want to set fromprimarythermolibrary to true,
  181. // because the result is not directly from the PTL, but comes via PTL + HBI corrections; if true is set here, this would
  182. // affect two things, both in Species.java: 1) the naming; in the HBI case, we don't want to use a name derived from the
  183. // thermo name as weird things can happen 2)findStablestThermoData considers the value to be the final word; however,
  184. // since this is a radical that is not directly in the primaryThermoLibrary, we want to consider alternative thermo for
  185. // all possible resonance isomers
  186. } else {
  187. tmpTherm = getQMThermoData(p_chemGraph);
  188. result = tmpTherm.copyWithExtraInfo();
  189. }
  190. // find the BDE for all radical groups
  191. if (thermoLibrary == null)
  192. initGAGroupLibrary();
  193. for (int i = 0; i < radicalSite; i++) {
  194. int id = idArray[i];
  195. Node node = g.getNodeAt(id);
  196. Atom old = (Atom) node.getElement();
  197. node.setElement(atomArray[i]);
  198. node.updateFeElement();
  199. // get rid of the extra H at ith site
  200. int HNum = atomArray[i].getRadicalNumber();
  201. for (int j = 0; j < HNum; j++) {
  202. g.removeNode(newnode[i][j]);
  203. }
  204. node.updateFgElement();
  205. p_chemGraph.resetThermoSite(node);
  206. ThermoGAValue thisGAValue = thermoLibrary
  207. .findRadicalGroup(p_chemGraph);
  208. if (thisGAValue == null) {
  209. Logger.error("Radical group not found: " + node.getID());
  210. } else {
  211. // Logger.info(node.getID() + " radical correction: " + thisGAValue.getName() +
  212. // " "+thisGAValue.toString());
  213. result.plus(thisGAValue);
  214. }
  215. // recover the saturated site for next radical site calculation
  216. node.setElement(old);
  217. node.updateFeElement();
  218. for (int j = 0; j < HNum; j++) {
  219. newnode[i][j] = g.addNode(H);
  220. g.addArcBetween(node, S, newnode[i][j]);
  221. }
  222. node.updateFgElement();
  223. }
  224. // recover the chem graph structure
  225. // recover the radical
  226. for (int i = 0; i < radicalSite; i++) {
  227. int id = idArray[i];
  228. Node node = g.getNodeAt(id);
  229. node.setElement(atomArray[i]);
  230. node.updateFeElement();
  231. int HNum = atomArray[i].getRadicalNumber();
  232. // get rid of extra H
  233. for (int j = 0; j < HNum; j++) {
  234. g.removeNode(newnode[i][j]);
  235. }
  236. node.updateFgElement();
  237. }
  238. // subtract the enthalphy of H from the result
  239. int rad_number = p_chemGraph.getRadicalNumber();
  240. ThermoGAValue enthalpy_H = new ThermoGAValue(ENTHALPY_HYDROGEN
  241. * rad_number, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
  242. result.minus(enthalpy_H);
  243. // correct the symmetry number based on the relative radical and saturated symmetry number; this should
  244. // hopefully sidestep potential complications based on the fact that certain symmetry effects could be included in HBI
  245. // value itself, and the fact that the symmetry number correction for saturated molecule has already been implemented,
  246. // and it is likely to be different than symmetry number considered here, since the correction for the saturated
  247. // molecule will have been external symmetry number, whereas RMG's ChemGraph symmetry number estimator includes both
  248. // internal and external symmetry contributions; even so, I don't know if this will handle a change from chiral to
  249. // achiral (or vice versa) properly
  250. ThermoGAValue symmetryNumberCorrection = new ThermoGAValue(0, -1
  251. * GasConstant.getCalMolK()
  252. * Math.log((double) (sigmaRadical)
  253. / (double) (sigmaSaturated)), 0, 0, 0, 0, 0, 0, 0,
  254. 0, 0, 0, null);
  255. result.plus(symmetryNumberCorrection);
  256. p_chemGraph.setCentralNode(oldCentralNode);
  257. // display corrected thermo to user
  258. String[] InChInames = getQMFileName(p_chemGraph);// determine the filename (InChIKey) and InChI with
  259. // appended info for triplets, etc.
  260. String name = InChInames[0];
  261. String InChIaug = InChInames[1];
  262. Logger.info("HBI-based thermo for " + name + "(" + InChIaug + "): "
  263. + result.toString());// print result, at least for debugging purposes
  264. } else {
  265. // no need for HBI radical corrections, just get the QM result
  266. tmpTherm = getQMThermoData(p_chemGraph);
  267. result = tmpTherm.copyWithExtraInfo();
  268. }
  269. return result;
  270. // #]
  271. }
  272. public ThermoData getQMThermoData(ChemGraph p_chemGraph) {
  273. // Try to get the QMThermoData from the qmLibrary, if it's not there then call getQMThermoData and save it for
  274. // future.
  275. ThermoData result = null;
  276. // First to get from qmLibrary
  277. String[] InChInames = getQMFileName(p_chemGraph);// determine the filename (InChIKey) and InChI with appended
  278. // info for triplets, etc.
  279. String name = InChInames[0];
  280. String InChIaug = InChInames[1];
  281. ThermoData tempTherm = qmLibrary.get(InChIaug);
  282. if (tempTherm != null) {
  283. result = tempTherm.copyWithExtraInfo(); // use a copy of the object!; that way, subsequent modifications of
  284. // this object don't change the QM library
  285. Logger.info("QM calculation for "
  286. + InChIaug
  287. + " previously performed. Using Thermo from previous results in QMlibrary.");
  288. result.setSource(result.comments);
  289. // Added for debugging
  290. Logger.info("Thermo Data for " + InChIaug + " is " + result);
  291. } else { // couldn't find it in qmLibrary
  292. // Check to see if QMTP has failed all attempts on this molecule before
  293. // If so, generate Thermo using Benson groups
  294. if (failedQm.contains(InChIaug)) {
  295. Logger.info("All attempts at QMTP for "
  296. + name
  297. + "("
  298. + InChIaug
  299. + ") have previously failed. Falling back to Benson Group Additivity.");
  300. TDGenerator gen = new BensonTDGenerator();
  301. return gen.generateFakeThermo();
  302. // return gen.generateThermo(p_chemGraph);
  303. }
  304. // generate new QM Thermo Data
  305. try {
  306. String qmMethod = getQmMethod();
  307. result = generateQMThermoData(p_chemGraph, qmMethod);
  308. // now save it for next time
  309. QMLibraryEditor.addQMTPThermo(p_chemGraph, InChIaug, result,
  310. qmMethod, qmprogram);
  311. qmLibrary.put(InChIaug, result);
  312. } catch (AllQmtpAttemptsFailedException e) {
  313. Logger.warning("Falling back to Benson group additivity due to repeated failure in QMTP calculations");
  314. TDGenerator gen = new BensonTDGenerator();
  315. // return gen.generateThermo(p_chemGraph);
  316. return gen.generateFakeThermo();
  317. }
  318. }
  319. return result;
  320. }
  321. private Thread setHoldAndGetClearerThread(final String name) {
  322. /*
  323. * Creates a .hold file for the given name, and creates a shutdown hook to delete the hold file. Returns the
  324. * hook, which is a thread which you can later run and de-register.
  325. */
  326. Logger.debug("Creating hold file for " + name);
  327. final File holdFile = new File(qmfolder, name + ".hold");
  328. try {
  329. holdFile.createNewFile();
  330. } catch (Exception e) {
  331. Logger.error("Error creating .hold file for " + name + ": "
  332. + e.toString());
  333. System.exit(0);
  334. throw new RuntimeException("Error creating .hold file for " + name,
  335. e);
  336. }
  337. Thread hook = new Thread() {
  338. public void run() {
  339. Logger.debug("Deleting hold file for " + name);
  340. holdFile.delete();
  341. };
  342. };
  343. Runtime.getRuntime().addShutdownHook(hook);
  344. return hook;
  345. }
  346. private boolean checkHoldExists(String name) {
  347. /*
  348. * Checks for existance of a .hold file for the given name. Returns true if it exists (and you shouldn't run a
  349. * new job).
  350. */
  351. Logger.debug("Checking hold file for " + name);
  352. final File holdFile = new File(qmfolder, name + ".hold");
  353. return holdFile.exists();
  354. }
  355. private void clearHold(Thread holdClearerThread) {
  356. /*
  357. * Clears the hold, by running and unregistering the hold-clearing thread.
  358. */
  359. Runtime.getRuntime().removeShutdownHook(holdClearerThread);
  360. holdClearerThread.start();
  361. try {
  362. holdClearerThread.join(); // wait for it to finish
  363. } catch (InterruptedException e) {
  364. Thread.currentThread().interrupt();
  365. }
  366. }
  367. public ThermoData generateQMThermoData(ChemGraph p_chemGraph, String qmMethod)
  368. throws AllQmtpAttemptsFailedException {
  369. // if there is no data in the libraries, calculate the result based on QM or MM calculations; the below steps
  370. // will be generalized later to allow for other quantum mechanics packages, etc.
  371. String qmProgram = qmprogram;
  372. ThermoData result = new ThermoData();
  373. double[] dihedralMinima = null;
  374. String[] InChInames = getQMFileName(p_chemGraph);// determine the filename (InChIKey) and InChI with appended
  375. // info for triplets, etc.
  376. String name = InChInames[0];
  377. String InChIaug = InChInames[1];
  378. String directory = qmfolder;
  379. File dir = new File(directory);
  380. directory = dir.getAbsolutePath();// this and previous three lines get the absolute path for the directory
  381. // check for existing hold file before starting calculations (to avoid the possibility of interference with
  382. // other jobs using the same QMfiles folder)
  383. try {
  384. while (checkHoldExists(name)) {
  385. Logger.info("Existence of hold file for "
  386. + name
  387. + " suggests that another RMG process is currently running calculations on this molecule; waiting for other RMG process to finish; will check again in 60 seconds...");
  388. Thread.sleep(60000);
  389. }
  390. } catch (Exception e) {
  391. Logger.error("Unexpected error while waiting for hold to be lifted: "
  392. + e.toString());
  393. System.exit(0);
  394. }
  395. if (qmMethod.equals("pm3") || qmMethod.equals("pm7")) {
  396. // first, check to see if the result already exists and the job terminated successfully
  397. boolean gaussianResultExists = successfulGaussianResultExistsQ(
  398. name, directory, InChIaug);
  399. boolean mopacResultExists = successfulMopacResultExistsQ(name,
  400. directory, InChIaug);
  401. if (!gaussianResultExists && !mopacResultExists) {// if a successful result doesn't exist from previous run
  402. // (or from this run), run the calculation; if a successful result exists, we will skip directly to parsing the file
  403. // step 0: create a .hold file to prevent other jobs running with the same directory from interfering by
  404. // trying to run with the same molecule
  405. Thread holdClearer = setHoldAndGetClearerThread(name);
  406. // steps 1 and 2: create 2D and 3D mole files
  407. molFile p_3dfile = create3Dmolfile(name, p_chemGraph);
  408. // 3. create the Gaussian or MOPAC input file
  409. directory = qmfolder;
  410. dir = new File(directory);
  411. directory = dir.getAbsolutePath();// this and previous three lines get the absolute path for the
  412. // directory
  413. int attemptNumber = 1;// counter for attempts using different keywords
  414. int successFlag = 0;// flag for success of Gaussian run; 0 means it failed, 1 means it succeeded
  415. int maxAttemptNumber = 1;
  416. int multiplicity = p_chemGraph.getRadicalNumber() + 1; // multiplicity = radical number + 1
  417. while (successFlag == 0 && attemptNumber <= maxAttemptNumber) {
  418. // IF block to check which program to use
  419. if (qmProgram.equals("gaussian03")) {
  420. if (p_chemGraph.getAtomNumber() > 1) {
  421. maxAttemptNumber = createGaussianInput(name,
  422. directory, p_3dfile, attemptNumber,
  423. InChIaug, multiplicity, qmMethod);
  424. } else {
  425. maxAttemptNumber = createGaussianInput(name,
  426. directory, p_3dfile, -1, InChIaug,
  427. multiplicity, qmMethod);// use -1 for attemptNumber for monoatomic case
  428. }
  429. // 4. run Gaussian
  430. successFlag = runGaussian(name, directory, InChIaug);
  431. } else if (qmProgram.equals("mopac")
  432. || qmProgram.equals("both")) {
  433. // Modified by Aaron
  434. maxAttemptNumber = createMopacInput(name, directory,
  435. p_3dfile, attemptNumber, InChIaug, multiplicity, qmMethod);
  436. successFlag = runMOPAC(name, directory, InChIaug);
  437. } else {
  438. Logger.critical("Unsupported quantum chemistry program");
  439. System.exit(0);
  440. }
  441. // new IF block to check success
  442. if (successFlag == 1) {
  443. clearHold(holdClearer);
  444. Logger.info("Attempt #" + attemptNumber
  445. + " on species " + name + " (" + InChIaug
  446. + ") succeeded.");
  447. } else if (successFlag == 0) {
  448. if (attemptNumber == maxAttemptNumber) {// if this is the last possible attempt, and the
  449. // calculation fails, exit with an error message
  450. if (qmProgram.equals("both")) { // if we are running with "both" option and all keywords
  451. // fail, try with Gaussian
  452. qmProgram = "gaussian03";
  453. Logger.info("*****Final MOPAC attempt (#"
  454. + maxAttemptNumber + ") on species "
  455. + name + " (" + InChIaug
  456. + ") failed. Trying to use Gaussian.");
  457. attemptNumber = 0;// this needs to be 0 so that when we increment attemptNumber below,
  458. // it becomes 1 when returning to the beginning of the for loop
  459. maxAttemptNumber = 1;
  460. } else {
  461. Logger.info("*****Final attempt (#"
  462. + maxAttemptNumber + ") on species "
  463. + name + " (" + InChIaug + ") failed.");
  464. Logger.info(p_chemGraph.toString());
  465. clearHold(holdClearer);
  466. // Add to augmented InChI failedQm so that RMG will not try to run this molecule in QMTP
  467. // again
  468. failedQm.add(InChIaug);
  469. throw new AllQmtpAttemptsFailedException();
  470. }
  471. }
  472. Logger.info("*****Attempt #" + attemptNumber
  473. + " on species " + name + " (" + InChIaug
  474. + ") failed. Will attempt a new keyword.");
  475. attemptNumber++;// try again with new keyword
  476. }
  477. }
  478. }
  479. // 5. parse QM output and record as thermo data (function includes symmetry/point group calcs, etc.); if
  480. // both Gaussian and MOPAC results exist, Gaussian result is used
  481. if (gaussianResultExists
  482. || (qmProgram.equals("gaussian03") && !mopacResultExists)) {
  483. result = parseGaussianPM3(name, directory, p_chemGraph);
  484. } else if (mopacResultExists || qmProgram.equals("mopac")
  485. || qmProgram.equals("both")) {
  486. result = parseMopac(name, directory, p_chemGraph, qmMethod);
  487. } else {
  488. Logger.critical("Unexpected situation in QMTP thermo estimation");
  489. System.exit(0);
  490. }
  491. } else {// mm4 case
  492. // first, check to see if the result already exists and the job terminated successfully
  493. boolean mm4ResultExists = successfulMM4ResultExistsQ(name,
  494. directory, InChIaug);
  495. if (!mm4ResultExists) {// if a successful result doesn't exist from previous run (or from this run), run the
  496. // calculation; if a successful result exists, we will skip directly to parsing the file
  497. // step 0: create a .hold file to prevent other jobs running with the same directory from interfering by
  498. // trying to run with the same molecule
  499. Thread holdClearer = setHoldAndGetClearerThread(name);
  500. // steps 1 and 2: create 2D and 3D mole files
  501. molFile p_3dfile = create3Dmolfile(name, p_chemGraph);
  502. // 3. create the MM4 input file
  503. directory = qmfolder;
  504. dir = new File(directory);
  505. directory = dir.getAbsolutePath();// this and previous three lines get the absolute path for the
  506. // directory
  507. int attemptNumber = 1;// counter for attempts using different keywords
  508. int successFlag = 0;// flag for success of MM4 run; 0 means it failed, 1 means it succeeded
  509. int maxAttemptNumber = 1;
  510. int multiplicity = p_chemGraph.getRadicalNumber() + 1; // multiplicity = radical number + 1
  511. while (successFlag == 0 && attemptNumber <= maxAttemptNumber) {
  512. maxAttemptNumber = createMM4Input(name, directory,
  513. p_3dfile, attemptNumber, InChIaug, multiplicity);
  514. // 4. run MM4
  515. successFlag = runMM4(name, directory, InChIaug);
  516. // new IF block to check success
  517. if (successFlag == 1) {
  518. clearHold(holdClearer);
  519. Logger.info("Attempt #" + attemptNumber
  520. + " on species " + name + " (" + InChIaug
  521. + ") succeeded.");
  522. // run rotor calculations if necessary
  523. int rotors = p_chemGraph.getInternalRotor();
  524. if (useHindRot && rotors > 0) {
  525. // we should re-run scans even if pre-existing scans exist because atom numbering may change
  526. // from case to case; a better solution would be to check for stored CanTherm output and use that if available
  527. Logger.info("Running rotor scans on " + name
  528. + "...");
  529. dihedralMinima = createMM4RotorInput(name,
  530. directory, p_chemGraph, rotors);// we don't worry about checking InChI here; if
  531. // there is an InChI mismatch it should be caught
  532. runMM4Rotor(name, directory, rotors);
  533. }
  534. } else if (successFlag == 0) {
  535. if (attemptNumber == maxAttemptNumber) {// if this is the last possible attempt, and the
  536. // calculation fails, exit with an error message
  537. Logger.info("*****Final attempt (#"
  538. + maxAttemptNumber + ") on species " + name
  539. + " (" + InChIaug + ") failed.");
  540. Logger.info(p_chemGraph.toString());
  541. // delete the hold file
  542. clearHold(holdClearer);
  543. throw new AllQmtpAttemptsFailedException();
  544. }
  545. Logger.info("*****Attempt #" + attemptNumber
  546. + " on species " + name + " (" + InChIaug
  547. + ") failed. Will attempt a new keyword.");
  548. attemptNumber++;// try again with new keyword
  549. }
  550. }
  551. if (useCanTherm) {
  552. performCanThermCalcs(name, directory, p_chemGraph,
  553. dihedralMinima, false);
  554. if (p_chemGraph.getInternalRotor() > 0)
  555. performCanThermCalcs(name, directory, p_chemGraph,
  556. dihedralMinima, true);// calculate RRHO case for comparison
  557. }
  558. }
  559. // 5. parse MM4 output and record as thermo data (function includes symmetry/point group calcs, etc.)
  560. if (!useCanTherm)
  561. result = parseMM4(name, directory, p_chemGraph);
  562. else {
  563. // if (qmdata==null) qmdata = getQMDataWithCClib(name, directory, p_chemGraph, true);//get qmdata if it
  564. // is null (i.e. if a pre-existing successful result exists and it wasn't read in above)
  565. result = parseCanThermFile(name, directory, p_chemGraph);
  566. if (p_chemGraph.getInternalRotor() > 0)
  567. parseCanThermFile(name + "_RRHO", directory, p_chemGraph);// print the RRHO result for comparison
  568. }
  569. }
  570. return result;
  571. }
  572. protected static QMTP getINSTANCE() {
  573. return INSTANCE;
  574. }
  575. public void initializePrimaryThermoLibrary() {
  576. primaryLibrary = PrimaryThermoLibrary.getINSTANCE();
  577. }
  578. // Checks to see if QmLibrary already exists. If not, creates the necessary directorys and files
  579. public void initalizeQmLibrary() {
  580. try {
  581. qmLibrary = QMLibraryEditor
  582. .readLibrary("QMThermoLibrary/Library.txt");
  583. QMLibraryEditor.initialize();
  584. } catch (IOException e) {
  585. Logger.info("No QM Thermo Library detected. New QM Library being created");
  586. QMLibraryEditor.initialize();
  587. }
  588. }
  589. // Deletes the contents of the QmFiles directory
  590. public void cleanQmFiles() {
  591. File qmFolder = new File(qmfolder);
  592. // Deletes folder and everything inside
  593. ChemParser.deleteDir(qmFolder);
  594. // Remake empty directory
  595. qmFolder.mkdir();
  596. }
  597. // creates a 3D molFile; for monoatomic species, it just returns the 2D molFile
  598. public molFile create3Dmolfile(String name, ChemGraph p_chemGraph) {
  599. // 1. create a 2D file
  600. // use the absolute path for directory, so we can easily reference from other directories in command-line paths
  601. String directory = System.getProperty("RMG.2DmolfilesDir");
  602. directory = new File(directory).getAbsolutePath();
  603. molFile p_2dfile = new molFile(name, directory, p_chemGraph);
  604. molFile p_3dfile = new molFile();// it seems this must be initialized, so we initialize to empty object
  605. // 2. convert from 2D to 3D using RDKit if the 2D molfile is for a molecule with 2 or more atoms
  606. int atoms = p_chemGraph.getAtomNumber();
  607. if (atoms > 1) {
  608. int distGeomAttempts = 1;
  609. if (atoms > 3) {// this check prevents the number of attempts from being negative
  610. distGeomAttempts = 5 * (p_chemGraph.getAtomNumber() - 3); // number of conformer attempts is just a
  611. // linear scaling with molecule size, due to time considerations; in practice, it is probably more like 3^(n-3) or
  612. // something like that
  613. }
  614. p_3dfile = embed3D(p_2dfile, distGeomAttempts);
  615. return p_3dfile;
  616. } else {
  617. return p_2dfile;
  618. }
  619. }
  620. // embed a molecule in 3D, using RDKit
  621. public molFile embed3D(molFile twoDmolFile, int numConfAttempts) {
  622. // convert to 3D MOL file using RDKit script
  623. int flag = 0;
  624. String directory = System.getProperty("RMG.3DmolfilesDir");
  625. directory = new File(directory).getAbsolutePath(); // get the absolute path for the directory
  626. String name = twoDmolFile.getName();
  627. String rdbase = System.getenv("RDBASE");
  628. if (rdbase == null) {
  629. Logger.critical("Please set your RDBASE environment variable to the directory containing RDKit.");
  630. System.exit(0);
  631. }
  632. try {
  633. File runningdir = new File(directory);
  634. String command = "";
  635. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths
  636. // can have spaces and are allowed to be surrounded by quotes
  637. command = "python \""
  638. + System.getProperty("RMG.workingDirectory")
  639. + "/scripts/distGeomScriptMolLowestEnergyConf.py\" ";
  640. String twoDmolpath = twoDmolFile.getPath();
  641. command = command.concat("\"" + twoDmolpath + "\" ");
  642. command = command.concat("\"" + name + ".mol\" ");// this is the target file name; use the same name as
  643. // the twoDmolFile (but it will be in he 3Dmolfiles folder
  644. command = command.concat("\"" + name + ".cmol\" ");// this is the target file name for crude coordinates
  645. // (corresponding to the minimum energy conformation based on UFF refinement); use the same name as the twoDmolFile (but
  646. // it will be in he 3Dmolfiles folder) and have suffix .cmol
  647. command = command.concat(numConfAttempts + " ");
  648. command = command.concat("\"" + rdbase + "\"");// pass the $RDBASE environment variable
  649. // to the script so it can use the approprate directory when importing rdkit
  650. } else {// non-Windows case
  651. command = "python "
  652. + System.getProperty("RMG.workingDirectory")
  653. + "/scripts/distGeomScriptMolLowestEnergyConf.py ";
  654. String twoDmolpath = twoDmolFile.getPath();
  655. command = command.concat("" + twoDmolpath + " ");
  656. command = command.concat(name + ".mol ");// this is the target file name; use the same name as the
  657. // twoDmolFile (but it will be in he 3Dmolfiles folder
  658. command = command.concat(name + ".cmol ");// this is the target file name for crude coordinates
  659. // (corresponding to the minimum energy conformation based on UFF refinement); use the same name as the twoDmolFile (but
  660. // it will be in he 3Dmolfiles folder) and have suffix .cmol
  661. command = command.concat(numConfAttempts + " ");
  662. command = command.concat(rdbase);// pass the $RDBASE environment variable to the script
  663. // so it can use the approprate directory when importing rdkit
  664. }
  665. Process pythonProc = Runtime.getRuntime().exec(command, null,
  666. runningdir);
  667. String killmsg = "Python process for "
  668. + twoDmolFile.getName()
  669. + " did not complete within 120 seconds, and the process was killed. File was probably not written.";// message
  670. // to print if the process times out
  671. // check for errors and display the error if there is one
  672. InputStream is = pythonProc.getErrorStream();
  673. InputStreamReader isr = new InputStreamReader(is);
  674. BufferedReader br = new BufferedReader(isr);
  675. String line = null;
  676. while ((line = br.readLine()) != null) {
  677. line = line.trim();
  678. Logger.error(line);
  679. flag = 1;
  680. }
  681. // if there was an error, indicate the file and InChI
  682. if (flag == 1) {
  683. Logger.info("RDKit received error (see above) on "
  684. + twoDmolFile.getName()
  685. + ". File was probably not written.");
  686. }
  687. int exitValue = pythonProc.waitFor();
  688. pythonProc.getInputStream().close();
  689. pythonProc.getOutputStream().close();
  690. br.close();
  691. isr.close();
  692. is.close();
  693. } catch (Exception e) {
  694. Logger.logStackTrace(e);
  695. String err = "Error in running RDKit Python process \n";
  696. err += e.toString();
  697. Logger.critical(err);
  698. System.exit(0);
  699. }
  700. // construct molFile pointer to new file (name will be same as 2D mol file
  701. return new molFile(name, directory);
  702. }
  703. // creates Gaussian PM3 input file in directory with filename name.gjf by using OpenBabel to convert p_molfile
  704. // attemptNumber determines which keywords to try
  705. // the function returns the maximum number of keywords that can be attempted; this will be the same throughout the
  706. // evaluation of the code, so it may be more appropriate to have this as a "constant" attribute of some sort
  707. // attemptNumber=-1 will call a special set of keywords for the monoatomic case
  708. public int createGaussianInput(String name, String directory,
  709. molFile p_molfile, int attemptNumber, String InChIaug,
  710. int multiplicity, String qmMethod) {
  711. // write a file with the input keywords
  712. int scriptAttempts = 18;// the number of keyword permutations available; update as additional options are added
  713. int maxAttemptNumber = 2 * scriptAttempts;// we will try a second time with crude coordinates if the UFF refined
  714. // coordinates do not work
  715. // String inpKeyStr="%chk="+directory+"/RMGrunCHKfile.chk\n";//by not specifying a filename, Gaussian will
  716. // create its own pseudo-random filename, and it will be deleted at the conclusion of a successful calculation; an
  717. // alternative would be to specify a checkpoint filename based on the modified InChIKey... however, it wouldn't be
  718. // automatically deleted after the completion of the calculation
  719. String inpKeyStr = "%mem=6MW\n";
  720. inpKeyStr += "%nproc=1\n";
  721. if (attemptNumber == -1)
  722. inpKeyStr += "# " + qmMethod + " freq";// keywords for monoatomic case (still need to use freq keyword to get molecular
  723. // mass)
  724. else if (attemptNumber % scriptAttempts == 1)
  725. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis) freq IOP(2/16=3)";// added IOP option to avoid aborting when
  726. // symmetry changes; 3 is supposed to be default according to documentation, but it seems that 0 (the default) is the
  727. // only option that doesn't work from 0-4; also, it is interesting to note that all 4 options seem to work for test case
  728. // with z-matrix input rather than xyz coords; cf. http://www.ccl.net/cgi-bin/ccl/message-new?2006+10+17+005 for
  729. // original idea for solution
  730. else if (attemptNumber % scriptAttempts == 2)
  731. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis) freq IOP(2/16=3) IOP(4/21=2)";// use different SCF method; this
  732. // addresses at least one case of failure for a C4H7J species
  733. else if (attemptNumber % scriptAttempts == 3)
  734. inpKeyStr += "# " + qmMethod + " opt=(verytight,calcfc,maxcyc=200) freq IOP(2/16=3) nosymm";// try multiple different
  735. // options (no gdiis, use calcfc, nosymm); 7/21/09: added maxcyc option to fix case of MPTBUKVAJYJXDE-UHFFFAOYAPmult3
  736. // (InChI=1/C4H10O5Si/c1-3-7-9-10(5,6)8-4-2/h4-5H,3H2,1-2H3/mult3) (file manually copied to speed things along)
  737. else if (attemptNumber % scriptAttempts == 4)
  738. inpKeyStr += "# " + qmMethod + " opt=(verytight,calcfc,maxcyc=200) freq=numerical IOP(2/16=3) nosymm";// 7/8/09:
  739. // numerical frequency keyword version of keyword #3; used to address GYFVJYRUZAKGFA-UHFFFAOYALmult3
  740. // (InChI=1/C6H14O6Si/c1-3-10-13(8,11-4-2)12-6-5-9-7/h6-7H,3-5H2,1-2H3/mult3) case; (none of the existing Gaussian or
  741. // MOPAC combinations worked with it)
  742. else if (attemptNumber % scriptAttempts == 5)
  743. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis,small) freq IOP(2/16=3)";// 7/10/09: somehow, this worked for
  744. // problematic case of ZGAWAHRALACNPM-UHFFFAOYAF
  745. // (InChI=1/C8H17O5Si/c1-3-11-14(10,12-4-2)13-8-5-7(9)6-8/h7-9H,3-6H2,1-2H3); (was otherwise giving l402 errors); even
  746. // though I had a keyword that worked for this case, I manually copied the fixed log file to QMfiles folder to speed
  747. // things along; note that there are a couple of very low frequencies (~5-6 cm^-1 for this case)
  748. else if (attemptNumber % scriptAttempts == 6)
  749. inpKeyStr += "# " + qmMethod + " opt=(verytight,nolinear,calcfc,small) freq IOP(2/16=3)";// used for troublesome C5H7J2
  750. // case (similar error to C5H7J below); calcfc is not necessary for this particular species, but it speeds convergence
  751. // and probably makes it more robust for other species
  752. else if (attemptNumber % scriptAttempts == 7)
  753. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis,maxcyc=200) freq=numerical IOP(2/16=3)"; // use numerical
  754. // frequencies; this takes a relatively long time, so should only be used as one of the last resorts; this seemed to
  755. // address at least one case of failure for a C6H10JJ species; 7/15/09: maxcyc=200 added to address
  756. // GVCMURUDAUQXEY-UHFFFAOYAVmult3 (InChI=1/C3H4O7Si/c1-2(9-6)10-11(7,8)3(4)5/h6-7H,1H2/mult3)...however, result was
  757. // manually pasted in QMfiles folder to speed things along
  758. else if (attemptNumber % scriptAttempts == 8)
  759. inpKeyStr += "# " + qmMethod + " opt=tight freq IOP(2/16=3)";// 7/10/09: this worked for problematic case of
  760. // SZSSHFMXPBKYPR-UHFFFAOYAF (InChI=1/C7H15O5Si/c1-3-10-13(8,11-4-2)12-7-5-6-9-7/h7H,3-6H2,1-2H3) (otherwise, it had
  761. // l402.exe errors); corrected log file was manually copied to QMfiles to speed things along; we could also add a
  762. // freq=numerical version of this keyword combination for added robustness; UPDATE: see below
  763. else if (attemptNumber % scriptAttempts == 9)
  764. inpKeyStr += "# " + qmMethod + " opt=tight freq=numerical IOP(2/16=3)";// 7/10/09: used for problematic case of
  765. // CIKDVMUGTARZCK-UHFFFAOYAImult4 (InChI=1/C8H15O6Si/c1-4-12-15(10,13-5-2)14-7-6-11-8(7,3)9/h7H,3-6H2,1-2H3/mult4 (most
  766. // other cases had l402.exe errors); corrected log file was manually copied to QMfiles to speed things along
  767. else if (attemptNumber % scriptAttempts == 10)
  768. inpKeyStr += "# " + qmMethod + " opt=(tight,nolinear,calcfc,small,maxcyc=200) freq IOP(2/16=3)";// 7/8/09: similar to
  769. // existing #5, but uses tight rather than verytight; used for ADMPQLGIEMRGAT-UHFFFAOYAUmult3
  770. // (InChI=1/C6H14O5Si/c1-4-9-12(8,10-5-2)11-6(3)7/h6-7H,3-5H2,1-2H3/mult3)
  771. else if (attemptNumber % scriptAttempts == 11)
  772. inpKeyStr += "# " + qmMethod + " opt freq IOP(2/16=3)"; // use default (not verytight) convergence criteria; use this as
  773. // last resort
  774. else if (attemptNumber % scriptAttempts == 12)
  775. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis) freq=numerical IOP(2/16=3) IOP(4/21=200)";// to address
  776. // problematic C10H14JJ case
  777. else if (attemptNumber % scriptAttempts == 13)
  778. inpKeyStr += "# " + qmMethod + " opt=(calcfc,verytight,newton,notrustupdate,small,maxcyc=100,maxstep=100) freq=(numerical,step=10) IOP(2/16=3) nosymm";// added
  779. // 6/10/09 for very troublesome RRMZRNPRCUANER-UHFFFAOYAQ (InChI=1/C5H7/c1-3-5-4-2/h3H,1-2H3) case...there were troubles
  780. // with negative frequencies, where I don't think they should have been; step size of numerical frequency was adjusted
  781. // to give positive result; accuracy of result is questionable; it is possible that not all of these keywords are
  782. // needed; note that for this and other nearly free rotor cases, I think heat capacity will be overestimated by R/2 (R
  783. // vs. R/2) (but this is a separate issue)
  784. else if (attemptNumber % scriptAttempts == 14)
  785. inpKeyStr += "# " + qmMethod + " opt=(tight,gdiis,small,maxcyc=200,maxstep=100) freq=numerical IOP(2/16=3) nosymm";// added
  786. // 6/22/09 for troublesome
  787. // QDERTVAGQZYPHT-UHFFFAOYAHmult3(InChI=1/C6H14O4Si/c1-4-8-11(7,9-5-2)10-6-3/h4H,5-6H2,1-3H3/mult3); key aspects appear
  788. // to be tight (rather than verytight) convergence criteria, no calculation of frequencies during optimization, use of
  789. // numerical frequencies, and probably also the use of opt=small
  790. // gmagoon 7/9/09: commented out since although this produces a "reasonable" result for the problematic case,
  791. // there is a large amount of spin contamination, apparently introducing 70+ kcal/mol of instability else
  792. // if(attemptNumber==12)
  793. // inpKeyStr+="# pm3 opt=(verytight,gdiis,small) freq=numerical IOP(2/16=3) IOP(4/21=200)";//7/9/09: similar to current
  794. // number 9 with keyword small; this addresses case of VCSJVABXVCFDRA-UHFFFAOYAI
  795. // (InChI=1/C8H19O5Si/c1-5-10-8(4)13-14(9,11-6-2)12-7-3/h8H,5-7H2,1-4H3)
  796. else if (attemptNumber % scriptAttempts == 15)
  797. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis,calcall) IOP(2/16=3)";// used for troublesome C5H7J case; note that
  798. // before fixing, I got errors like the following:
  799. // "Incomplete coordinate system. Try restarting with Geom=Check Guess=Read Opt=(ReadFC,NewRedundant) Incomplete coordinate system. Error termination via Lnk1e in l103.exe";
  800. // we could try to restart, but it is probably preferrable to have each keyword combination standalone; another keyword
  801. // that may be helpful if additional problematic cases are encountered is opt=small; 6/9/09 note: originally, this had #
  802. // pm3 opt=(verytight,gdiis,calcall) freq IOP(2/16=3)" (with freq keyword), but I discovered that in this case, there
  803. // are two thermochemistry sections and cclib parses frequencies twice, giving twice the number of desired frequencies
  804. // and hence produces incorrect thermo; this turned up on C5H6JJ isomer
  805. // gmagoon 7/3/09: it is probably best to retire this keyword combination in light of the similar combination below
  806. // //else if(attemptNumber==6) inpKeyStr+="# pm3 opt=(verytight,gdiis,calcall,small) IOP(2/16=3) IOP(4/21=2)";//6/10/09:
  807. // worked for OJZYSFFHCAPVGA-UHFFFAOYAK (InChI=1/C5H7/c1-3-5-4-2/h1,4H2,2H3) case; IOP(4/21) keyword was key
  808. else if (attemptNumber % scriptAttempts == 16)
  809. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis,calcall,small,maxcyc=200) IOP(2/16=3) IOP(4/21=2) nosymm";// 6/29/09:
  810. // worked for troublesome ketene case: CCGKOQOJPYTBIH-UHFFFAOYAO (InChI=1/C2H2O/c1-2-3/h1H2) (could just increase number
  811. // of iterations for similar keyword combination above (#6 at the time of this writing), allowing symmetry, but nosymm
  812. // seemed to reduce # of iterations; I think one of nosymm or higher number of iterations would allow the similar
  813. // keyword combination to converge; both are included here for robustness)
  814. else if (attemptNumber % scriptAttempts == 17)
  815. inpKeyStr += "# " + qmMethod + " opt=(verytight,gdiis,calcall,small) IOP(2/16=3) nosymm";// 7/1/09: added for case of
  816. // ZWMVZWMBTVHPBS-UHFFFAOYAEmult3 (InChI=1/C4H4O2/c1-3-5-6-4-2/h1-2H2/mult3)
  817. else if (attemptNumber % scriptAttempts == 0)
  818. inpKeyStr += "# " + qmMethod + " opt=(calcall,small,maxcyc=100) IOP(2/16=3)"; // 6/10/09: used to address troublesome
  819. // FILUFGAZMJGNEN-UHFFFAOYAImult3 case (InChI=1/C5H6/c1-3-5-4-2/h3H,1H2,2H3/mult3)
  820. else {// this point should not be reached
  821. Logger.error("Unexpected error in determining which G03 PM3 keyword set to use");
  822. System.exit(0);
  823. }
  824. // if(multiplicity == 3) inpKeyStr+= " guess=mix"; //assumed to be triplet biradical...use guess=mix to perform
  825. // unrestricted ; nevermind...I think this would only be for singlet biradicals based on
  826. // http://www.gaussian.com/g_tech/g_ur/k_guess.htm
  827. if (usePolar)
  828. inpKeyStr += " polar";
  829. // call the OpenBabel process (note that this requires OpenBabel environment variable)
  830. try {
  831. File runningdir = new File(directory);
  832. String molPath = null;
  833. if (attemptNumber <= scriptAttempts) {// use UFF-refined coordinates
  834. molPath = p_molfile.getPath();
  835. } else {// use crude coordinates
  836. molPath = p_molfile.getCrudePath();
  837. }
  838. String[] command = { "babel", "-imol", molPath, "-ogjf",
  839. name + ".gjf", "-xk", inpKeyStr, "--title", InChIaug };
  840. Process babelProc = Runtime.getRuntime().exec(command, null,
  841. runningdir);
  842. // read in output
  843. InputStream is = babelProc.getInputStream();
  844. InputStreamReader isr = new InputStreamReader(is);
  845. BufferedReader br = new BufferedReader(isr);
  846. String line = null;
  847. while ((line = br.readLine()) != null) {
  848. // do nothing
  849. }
  850. int exitValue = babelProc.waitFor();
  851. babelProc.getErrorStream().close();
  852. babelProc.getOutputStream().close();
  853. br.close();
  854. isr.close();
  855. is.close();
  856. } catch (Exception e) {
  857. String err = "Error in running OpenBabel MOL to GJF process \n";
  858. err += e.toString();
  859. Logger.logStackTrace(e);
  860. System.exit(0);
  861. }
  862. return maxAttemptNumber;
  863. }
  864. // creates MM4 input file and MM4 batch file in directory with filenames name.mm4 and name.com, respectively using
  865. // MoleCoor
  866. // attemptNumber determines which keywords to try
  867. // the function returns the maximum number of keywords that can be attempted; this will be the same throughout the
  868. // evaluation of the code, so it may be more appropriate to have this as a "constant" attribute of some sort
  869. public int createMM4Input(String name, String directory, molFile p_molfile,
  870. int attemptNumber, String InChIaug, int multiplicity) {
  871. // Step 1: write the script for MM4 batch operation
  872. // Example script file:
  873. // #! /bin/csh
  874. // cp testEthylene.mm4 CPD.MM4
  875. // cp $MM4_DATDIR/BLANK.DAT PARA.MM4
  876. // cp $MM4_DATDIR/CONST.MM4 .
  877. // $MM4_EXEDIR/mm4 <<%
  878. // 1
  879. // 2
  880. // 0
  881. // %
  882. // mv TAPE4.MM4 testEthyleneBatch.out
  883. // mv TAPE9.MM4 testEthyleneBatch.opt
  884. // exit
  885. int scriptAttempts = 2;// the number of script permutations available; update as additional options are added
  886. int maxAttemptNumber = 2 * scriptAttempts;// we will try a second time with crude coordinates if the UFF refined
  887. // coordinates do not work
  888. try {
  889. // create batch file with executable permissions: cf.
  890. // http://java.sun.com/docs/books/tutorial/essential/io/fileAttr.html#posix
  891. File inpKey = new File(directory + "/" + name + ".com");
  892. String inpKeyStr = "#! /bin/csh\n";
  893. inpKeyStr += "cp " + name + ".mm4 CPD.MM4\n";
  894. inpKeyStr += "cp $MM4_DATDIR/BLANK.DAT PARA.MM4\n";
  895. inpKeyStr += "cp $MM4_DATDIR/CONST.MM4 .\n";
  896. inpKeyStr += "$MM4_EXEDIR/mm4 <<%\n";
  897. inpKeyStr += "1\n";// read from first line of .mm4 file
  898. if (!useCanTherm) {
  899. if (attemptNumber % scriptAttempts == 1)
  900. inpKeyStr += "2\n"; // Block-Diagonal Method then Full-Matrix Method
  901. else if (attemptNumber % scriptAttempts == 0)
  902. inpKeyStr += "3\n"; // Full-Matrix Method only
  903. else
  904. throw new Exception();// this point should not be reached
  905. } else {// CanTherm case: write the FORCE.MAT file
  906. if (attemptNumber % scriptAttempts == 1)
  907. inpKeyStr += "4\n"; // Block-Diagonal Method then Full-Matrix Method
  908. else if (attemptNumber % scriptAttempts == 0)
  909. inpKeyStr += "5\n"; // Full-Matrix Method only
  910. else
  911. throw new Exception();// this point should not be reached
  912. inpKeyStr += "\n";// <RETURN> for temperature
  913. inpKeyStr += "4\n";// unofficial option 4 for vibrational eigenvector printout to generate Cartesian
  914. // force constant matrix in FORCE.MAT file
  915. inpKeyStr += "0\n";// no vibrational amplitude printout
  916. }
  917. inpKeyStr += "0\n";// terminate the job
  918. inpKeyStr += "%\n";
  919. inpKeyStr += "mv TAPE4.MM4 " + name + ".mm4out\n";
  920. inpKeyStr += "mv TAPE9.MM4 " + name + ".mm4opt\n";
  921. if (useCanTherm) {
  922. inpKeyStr += "mv FORCE.MAT " + name + ".fmat\n";
  923. }
  924. inpKeyStr += "exit\n";
  925. FileWriter fw = new FileWriter(inpKey);
  926. fw.write(inpKeyStr);
  927. fw.close();
  928. } catch (Exception e) {
  929. String err = "Error in writing MM4 script file\n";
  930. err += e.toString();
  931. Logger.logStackTrace(e);
  932. System.exit(0);
  933. }
  934. // Step 2: call the MoleCoor process to create the MM4 input file from the mole file
  935. try {
  936. File runningdir = new File(directory);
  937. // this will only be run on Linux so we don't have to worry about Linux vs. Windows issues
  938. String command = "python " + System.getenv("RMG")
  939. + "/scripts/MM4InputFileMaker.py ";
  940. // first argument: input file path; for the first attempts, we will use UFF refined coordinates; if that
  941. // doesn't work, (e.g. case of cyclopropene, InChI=1/C3H4/c1-2-3-1/h1-2H,3H2 OOXWYYGXTJLWHA-UHFFFAOYAJ) we will try
  942. // crude coordinates (.cmol suffix)
  943. if (attemptNumber <= scriptAttempts) {
  944. command = command.concat(p_molfile.getPath() + " ");
  945. } else {
  946. command = command.concat(p_molfile.getCrudePath() + " ");
  947. }
  948. // second argument: output path
  949. String inpfilepath = directory + "/" + name + ".mm4";
  950. command = command.concat(inpfilepath + " ");
  951. // third argument: molecule name (the augmented InChI)
  952. command = command.concat(InChIaug + " ");
  953. // fourth argument: PYTHONPATH
  954. command = command.concat(System.getenv("RMG") + "/source/MoleCoor");// this will pass $RMG/source/MoleCoor
  955. // to the script (in order to get the appropriate path for importing
  956. Process molecoorProc = Runtime.getRuntime().exec(command, null,
  957. runningdir);
  958. // read in output
  959. InputStream is = molecoorProc.getInputStream();
  960. InputStreamReader isr = new InputStreamReader(is);
  961. BufferedReader br = new BufferedReader(isr);
  962. String line = null;
  963. while ((line = br.readLine()) != null) {
  964. // do nothing
  965. }
  966. int exitValue = molecoorProc.waitFor();
  967. molecoorProc.getErrorStream().close();
  968. molecoorProc.getOutputStream().close();
  969. br.close();
  970. isr.close();
  971. is.close();
  972. } catch (Exception e) {
  973. String err = "Error in running MoleCoor MOL to .MM4 process \n";
  974. err += e.toString();
  975. Logger.logStackTrace(e);
  976. System.exit(0);
  977. }
  978. return maxAttemptNumber;
  979. }
  980. // creates MM4 rotor input file and MM4 batch file in directory with filenames name.mm4roti and name.comi,
  981. // respectively
  982. // the function returns the set of rotor dihedral angles for the minimum energy conformation
  983. public double[] createMM4RotorInput(String name, String directory,
  984. ChemGraph p_chemgraph, int rotors) {
  985. // read in the optimized coordinates from the completed "normal" MM4 job; this will be used as a template for
  986. // the rotor input files
  987. String mm4optContents = "";
  988. int indexForFirstAtom = -1;
  989. int lineIndex = 0;
  990. double[] dihedralMinima = new double[rotors];
  991. try {
  992. FileReader mm4opt = new FileReader(directory + "/" + name
  993. + ".mm4opt");
  994. BufferedReader reader = new BufferedReader(mm4opt);
  995. String line = reader.readLine();
  996. while (line != null) {
  997. if (indexForFirstAtom < 0 && line.length() >= 40
  998. && line.substring(35, 40).equals("( 1)"))
  999. indexForFirstAtom = lineIndex;// store the location of the first atom coordinate
  1000. mm4optContents += line + "\n";
  1001. line = reader.readLine();
  1002. lineIndex++;
  1003. }
  1004. reader.close();
  1005. mm4opt.close();
  1006. } catch (Exception e) {
  1007. String err = "Error in reading .mm4opt file\n";
  1008. err += e.toString();
  1009. Logger.logStackTrace(e);
  1010. System.exit(0);
  1011. }
  1012. String[] lines = mm4optContents.split("[\\r?\\n]+");// split by newlines, excluding blank lines; cf.
  1013. // http://stackoverflow.com/questions/454908/split-java-string-by-new-line
  1014. int flag = 0;// flag to indicate whether there is a "line 1a" with pi system information
  1015. if (lines[1].startsWith("T") || lines[1].startsWith("F"))
  1016. flag = 1;// pi system information is indicated by the second line beginning with T's and potentially F's
  1017. lines[1 + flag] = lines[1 + flag].substring(0, 78) + " 2";// take the first 78 characters of line 2 (or 3 if it
  1018. // is a pi-system compound), and append the option number for the NDRIVE option; in other words, we are replacing the
  1019. // NDRIVE=0 option with the desired option number
  1020. // reconstruct mm4optContents
  1021. mm4optContents = "";
  1022. for (int j = 0; j < lines.length; j++) {
  1023. mm4optContents += lines[j] + "\n";
  1024. }
  1025. // iterate over all the rotors in the molecule
  1026. int i = 0;// rotor index
  1027. LinkedHashMap rotorInfo = p_chemgraph.getInternalRotorInformation();
  1028. Iterator iter = rotorInfo.keySet().iterator();
  1029. while (iter.hasNext()) {
  1030. i++;
  1031. int[] rotorAtoms = (int[]) iter.next();
  1032. try {
  1033. // write one script file for each rotor
  1034. // Step 1: write the script for MM4 batch operation
  1035. // Example script file:
  1036. // #! /bin/csh
  1037. // cp testEthylene.mm4 CPD.MM4
  1038. // cp $MM4_DATDIR/BLANK.DAT PARA.MM4
  1039. // cp $MM4_DATDIR/CONST.MM4 .
  1040. // $MM4_EXEDIR/mm4 <<%
  1041. // 1
  1042. // 2
  1043. // 0
  1044. // %
  1045. // mv TAPE4.MM4 testEthyleneBatch.out
  1046. // mv TAPE9.MM4 testEthyleneBatch.opt
  1047. // exit
  1048. // create batch file with executable permissions: cf.
  1049. // http://java.sun.com/docs/books/tutorial/essential/io/fileAttr.html#posix
  1050. File inpKey = new File(directory + "/" + name + ".com" + i);
  1051. String inpKeyStr = "#! /bin/csh\n";
  1052. inpKeyStr += "cp " + name + ".mm4rot" + i + " CPD.MM4\n";
  1053. inpKeyStr += "cp $MM4_DATDIR/BLANK.DAT PARA.MM4\n";
  1054. inpKeyStr += "cp $MM4_DATDIR/CONST.MM4 .\n";
  1055. inpKeyStr += "$MM4_EXEDIR/mm4 <<%\n";
  1056. inpKeyStr += "1\n";// read from first line of .mm4 file
  1057. inpKeyStr += "3\n"; // Full-Matrix Method only
  1058. // inpKeyStr+="2\n"; //Block-Diagonal Method then Full-Matrix Method
  1059. inpKeyStr += "0\n";// terminate the job
  1060. inpKeyStr += "%\n";
  1061. inpKeyStr += "mv TAPE4.MM4 " + name + ".mm4rotout" + i + "\n";
  1062. inpKeyStr += "mv TAPE9.MM4 " + name + ".mm4rotopt" + i + "\n";
  1063. inpKeyStr += "exit\n";
  1064. FileWriter fw = new FileWriter(inpKey);
  1065. fw.write(inpKeyStr);
  1066. fw.close();
  1067. } catch (Exception e) {
  1068. String err = "Error in writing MM4 script files\n";
  1069. err += e.toString();
  1070. Logger.logStackTrace(e);
  1071. System.exit(0);
  1072. }
  1073. // Step 2: create the MM4 rotor job input file for rotor i, using the output file from the "normal" MM4 job
  1074. // as a template
  1075. // Step 2a: find the dihedral angle for the minimized conformation (we need to start at the minimum energy
  1076. // conformation because CanTherm assumes that theta=0 is the minimum
  1077. // extract the lines for dihedral atoms
  1078. String dihedral1s = lines[indexForFirstAtom + rotorAtoms[0] - 1];
  1079. String atom1s = lines[indexForFirstAtom + rotorAtoms[1] - 1];
  1080. String atom2s = lines[indexForFirstAtom + rotorAtoms[2] - 1];
  1081. String dihedral2s = lines[indexForFirstAtom + rotorAtoms[3] - 1];
  1082. // extract the x,y,z coordinates for each atom
  1083. double[] dihedral1 = {
  1084. Double.parseDouble(dihedral1s.substring(0, 10)),
  1085. Double.parseDouble(dihedral1s.substring(10, 20)),
  1086. Double.parseDouble(dihedral1s.substring(20, 30)) };
  1087. double[] atom1 = { Double.parseDouble(atom1s.substring(0, 10)),
  1088. Double.parseDouble(atom1s.substring(10, 20)),
  1089. Double.parseDouble(atom1s.substring(20, 30)) };
  1090. double[] atom2 = { Double.parseDouble(atom2s.substring(0, 10)),
  1091. Double.parseDouble(atom2s.substring(10, 20)),
  1092. Double.parseDouble(atom2s.substring(20, 30)) };
  1093. double[] dihedral2 = {
  1094. Double.parseDouble(dihedral2s.substring(0, 10)),
  1095. Double.parseDouble(dihedral2s.substring(10, 20)),
  1096. Double.parseDouble(dihedral2s.substring(20, 30)) };
  1097. // determine the dihedral angle
  1098. dihedralMinima[i - 1] = calculateDihedral(dihedral1, atom1, atom2,
  1099. dihedral2);
  1100. // double dihedral = calculateDihedral(dihedral1,atom1,atom2,dihedral2);
  1101. // if (dihedral < 0) dihedral = dihedral + 360;//make sure dihedral is positive; this will save an extra
  1102. // character in the limited space for specifying starting and ending angles
  1103. // eventually, when problems arise due to collinear atoms (both arguments to atan2 are zero) we can iterate
  1104. // over other atom combinations (with atoms in each piece determined by the corresponding value in rotorInfo for atom2
  1105. // and the complement of these (full set = p_chemgraph.getNodeIDs()) for atom1) until they are not collinear
  1106. // Step 2b: write the file for rotor i
  1107. try {
  1108. FileWriter mm4roti = new FileWriter(directory + "/" + name
  1109. + ".mm4rot" + i);
  1110. // mm4roti.write(mm4optContents+"\n"+String.format(" %3d %3d %3d %3d %5.1f%5.1f%5.1f",
  1111. // rotorAtoms[0],rotorAtoms[1],rotorAtoms[2],rotorAtoms[3], dihedral, dihedral + 360.0 - deltaTheta,
  1112. // deltaTheta)+"\n");//deltaTheta should be less than 100 degrees so that dihedral-deltaTheta still fits
  1113. // mm4roti.write(mm4optContents+"\n"+String.format(" %3d %3d %3d %3d %5.1f%5.1f%5.1f",
  1114. // rotorAtoms[0],rotorAtoms[1],rotorAtoms[2],rotorAtoms[3], dihedral, dihedral - deltaTheta,
  1115. // deltaTheta)+"\n");//deltaTheta should be less than 100 degrees so that dihedral-deltaTheta still fits
  1116. mm4roti.write(mm4optContents
  1117. + String.format(
  1118. " %3d %3d %3d %3d %5.1f%5.1f%5.1f",
  1119. rotorAtoms[0], rotorAtoms[1], rotorAtoms[2],
  1120. rotorAtoms[3], 0.0, 360.0 - deltaTheta,
  1121. deltaTheta) + "\n");// M1, M2, M3, M4, START, FINISH, DIFF (as described in MM4 manual);
  1122. // //this would require miminum to be stored and used to adjust actual angles before sending to CanTherm
  1123. mm4roti.close();
  1124. } catch (Exception e) {
  1125. String err = "Error in writing MM4 rotor input file\n";
  1126. err += e.toString();
  1127. Logger.logStackTrace(e);
  1128. System.exit(0);
  1129. }
  1130. }
  1131. return dihedralMinima;
  1132. }
  1133. // given x,y,z (cartesian) coordinates for dihedral1 atom, (rotor) atom 1, (rotor) atom 2, and dihedral2 atom,
  1134. // calculates the dihedral angle (in degrees, between +180 and -180) using the atan2 formula at
  1135. // http://en.wikipedia.org/w/index.php?title=Dihedral_angle&oldid=373614697
  1136. public double calculateDihedral(double[] dihedral1, double[] atom1,
  1137. double[] atom2, double[] dihedral2) {
  1138. // calculate the vectors between the atoms
  1139. double[] b1 = { atom1[0] - dihedral1[0], atom1[1] - dihedral1[1],
  1140. atom1[2] - dihedral1[2] };
  1141. double[] b2 = { atom2[0] - atom1[0], atom2[1] - atom1[1],
  1142. atom2[2] - atom1[2] };
  1143. double[] b3 = { dihedral2[0] - atom2[0], dihedral2[1] - atom2[1],
  1144. dihedral2[2] - atom2[2] };
  1145. // calculate norm of b2
  1146. double normb2 = Math
  1147. .sqrt(b2[0] * b2[0] + b2[1] * b2[1] + b2[2] * b2[2]);
  1148. // calculate necessary cross products
  1149. double[] b1xb2 = { b1[1] * b2[2] - b1[2] * b2[1],
  1150. b2[0] * b1[2] - b1[0] * b2[2], b1[0] * b2[1] - b1[1] * b2[0] };
  1151. double[] b2xb3 = { b2[1] * b3[2] - b2[2] * b3[1],
  1152. b3[0] * b2[2] - b2[0] * b3[2], b2[0] * b3[1] - b2[1] * b3[0] };
  1153. // compute arguments for atan2 function (includes dot products)
  1154. double y = normb2
  1155. * (b1[0] * b2xb3[0] + b1[1] * b2xb3[1] + b1[2] * b2xb3[2]);// |b2|*b1.(b2xb3)
  1156. double x = b1xb2[0] * b2xb3[0] + b1xb2[1] * b2xb3[1] + b1xb2[2]
  1157. * b2xb3[2];// (b1xb2).(b2xb3)
  1158. double dihedral = Math.atan2(y, x);
  1159. // return dihedral*180.0/Math.PI;//converts from radians to degrees
  1160. return Math.toDegrees(dihedral);// converts from radians to degrees
  1161. }
  1162. // returns the extra Mopac keywords to use for radical species, given the spin multiplicity (radical number + 1)
  1163. public String getMopacRadicalString(int multiplicity) {
  1164. if (multiplicity == 1)
  1165. return "";
  1166. else if (multiplicity == 2)
  1167. return "uhf doublet";
  1168. else if (multiplicity == 3)
  1169. return "uhf triplet";
  1170. else if (multiplicity == 4)
  1171. return "uhf quartet";
  1172. else if (multiplicity == 5)
  1173. return "uhf quintet";
  1174. else if (multiplicity == 6)
  1175. return "uhf sextet";
  1176. else if (multiplicity == 7)
  1177. return "uhf septet";
  1178. else if (multiplicity == 8)
  1179. return "uhf octet";
  1180. else if (multiplicity == 9)
  1181. return "uhf nonet";
  1182. else {
  1183. Logger.critical("Invalid multiplicity encountered: " + multiplicity);
  1184. System.exit(0);
  1185. }
  1186. return "this should not be returned: error associated with getMopacRadicalString()";
  1187. }
  1188. // creates MOPAC input file in directory with filename name.mop by using OpenBabel to convert p_molfile
  1189. // attemptNumber determines which keywords to try
  1190. // the function returns the maximum number of keywords that can be attempted; this will be the same throughout the
  1191. // evaluation of the code, so it may be more appropriate to have this as a "constant" attribute of some sort
  1192. // unlike createGaussianPM3 input, this requires an additional input specifying the spin multiplicity (radical
  1193. // number + 1) for the species
  1194. public int createMopacInput(String name, String directory,
  1195. molFile p_molfile, int attemptNumber, String InChIaug,
  1196. int multiplicity, String qmMethod) {
  1197. // write a file with the input keywords
  1198. int scriptAttempts = 5;// the number of keyword permutations available; update as additional options are added
  1199. int maxAttemptNumber = 2 * scriptAttempts;// we will try a second time with crude coordinates if the UFF refined
  1200. // coordinates do not work
  1201. String inpKeyStrBoth = "";// this string will be written at both the top (for optimization) and the bottom (for
  1202. // thermo/force calc)
  1203. String inpKeyStrTop = "";// this string will be written only at the top
  1204. String inpKeyStrBottom = "";// this string will be written at the bottom
  1205. String radicalString = getMopacRadicalString(multiplicity);
  1206. if (attemptNumber % scriptAttempts == 1) {
  1207. inpKeyStrBoth = qmMethod + " " + radicalString;
  1208. inpKeyStrTop = " precise nosym";
  1209. inpKeyStrBottom = "oldgeo thermo nosym precise ";// 7/10/09: based on a quick review of recent results,
  1210. // keyword combo #1 rarely works, and when it did (CJAINEUZFLXGFA-UHFFFAOYAUmult3
  1211. // (InChI=1/C8H16O5Si/c1-4-11-14(9,12-5-2)13-8-6-10-7(8)3/h7-8H,3-6H2,1-2H3/mult3)), the grad. norm on the force step
  1212. // was about 1.7 (too large); I manually removed this result and re-ran...the entropy was increased by nearly 20
  1213. // cal/mol-K...perhaps we should add a check for the "WARNING" that MOPAC prints out when the gradient is high; 7/22/09:
  1214. // for the case of FUGDBSHZYPTWLG-UHFFFAOYADmult3 (InChI=1/C5H8/c1-4-3-5(4)2/h4-5H,1-3H2/mult3), adding nosym seemed to
  1215. // resolve 1. large discrepancies from Gaussian and 2. negative frequencies in mass-weighted coordinates and possibly
  1216. // related issue in discrepancies between regular and mass-weighted coordinate frequencies
  1217. } else if (attemptNumber % scriptAttempts == 2) {// 7/9/09: used for VCSJVABXVCFDRA-UHFFFAOYAI
  1218. // (InChI=1/C8H19O5Si/c1-5-10-8(4)13-14(9,11-6-2)12-7-3/h8H,5-7H2,1-4H3); all existing Gaussian keywords also failed;
  1219. // the Gaussian result was also rectified, but the resulting molecule was over 70 kcal/mol less stable, probably due to
  1220. // a large amount of spin contamination (~1.75 in fixed Gaussian result vs. 0.754 for MOPAC)
  1221. inpKeyStrBoth = qmMethod + " " + radicalString;
  1222. inpKeyStrTop = " precise nosym gnorm=0.0 nonr";
  1223. inpKeyStrBottom = "oldgeo thermo nosym precise ";
  1224. } else if (attemptNumber % scriptAttempts == 3) {// 7/8/09: used for ADMPQLGIEMRGAT-UHFFFAOYAUmult3
  1225. // (InChI=1/C6H14O5Si/c1-4-9-12(8,10-5-2)11-6(3)7/h6-7H,3-5H2,1-2H3/mult3); all existing Gaussian keywords also failed;
  1226. // however, the Gaussian result was also rectified, and the resulting conformation was about 1.0 kcal/mol more stable
  1227. // than the one resulting from this, so fixed Gaussian result was manually copied to QMFiles folder
  1228. inpKeyStrBoth = qmMethod + " " + radicalString;
  1229. inpKeyStrTop = " precise nosym gnorm=0.0";
  1230. inpKeyStrBottom = "oldgeo thermo nosym precise "; // precise appeared to be necessary for the problematic
  1231. // case (to avoid negative frequencies);
  1232. } else if (attemptNumber % scriptAttempts == 4) {// 7/8/09: used for GYFVJYRUZAKGFA-UHFFFAOYALmult3
  1233. // (InChI=1/C6H14O6Si/c1-3-10-13(8,11-4-2)12-6-5-9-7/h6-7H,3-5H2,1-2H3/mult3) case (negative frequency issues in MOPAC)
  1234. // (also, none of the existing Gaussian combinations worked with it); note that the Gaussian result appears to be a
  1235. // different conformation as it is about 0.85 kcal/mol more stable, so the Gaussian result was manually copied to
  1236. // QMFiles directory; note that the MOPAC output included a very low frequency (4-5 cm^-1)
  1237. inpKeyStrBoth = qmMethod + " " + radicalString;
  1238. inpKeyStrTop = " precise nosym gnorm=0.0 bfgs";
  1239. inpKeyStrBottom = "oldgeo thermo nosym precise "; // precise appeared to be necessary for the problematic
  1240. // case (to avoid negative frequencies)
  1241. }
  1242. // else if(attemptNumber==5){
  1243. // inpKeyStrBoth="pm3 "+radicalString;
  1244. // inpKeyStrTop=" precise nosym gnorm=0.0 ddmin=0.0";
  1245. // inpKeyStrBottom="oldgeo thermo nosym precise ";
  1246. // }
  1247. // else if(attemptNumber==6){
  1248. // inpKeyStrBoth="pm3 "+radicalString;
  1249. // inpKeyStrTop=" precise nosym gnorm=0.0 nonr ddmin=0.0";
  1250. // inpKeyStrBottom="oldgeo thermo nosym precise ";
  1251. // }
  1252. // else if(attemptNumber==7){
  1253. // inpKeyStrBoth="pm3 "+radicalString;
  1254. // inpKeyStrTop=" precise nosym bfgs gnorm=0.0 ddmin=0.0";
  1255. // inpKeyStrBottom="oldgeo thermo nosym precise ";
  1256. // }
  1257. else if (attemptNumber % scriptAttempts == 0) {// used for troublesome HGRZRPHFLAXXBT-UHFFFAOYAVmult3
  1258. // (InChI=1/C3H2O4/c4-2(5)1-3(6)7/h1H2/mult3) case (negative frequency and large gradient issues)
  1259. inpKeyStrBoth = qmMethod + " " + radicalString;
  1260. inpKeyStrTop = " precise nosym recalc=10 dmax=0.10 nonr cycles=2000 t=2000";
  1261. inpKeyStrBottom = "oldgeo thermo nosym precise ";
  1262. }
  1263. // else if(attemptNumber==9){//used for troublesome CMARQPBQDRXBTN-UHFFFAOYAAmult3
  1264. // (InChI=1/C3H2O4/c1-3(5)7-6-2-4/h1H2/mult3) case (negative frequency issues)
  1265. // inpKeyStrBoth="pm3 "+radicalString;
  1266. // inpKeyStrTop=" precise nosym recalc=1 dmax=0.05 gnorm=0.0 cycles=1000 t=1000";
  1267. // inpKeyStrBottom="oldgeo thermo nosym precise ";
  1268. // }
  1269. // else if(attemptNumber==10){//used for ATCYLHQLTOSVFK-UHFFFAOYAMmult4
  1270. // (InChI=1/C4H5O5/c1-3(5)8-9-4(2)7-6/h6H,1-2H2/mult4) case (timeout issue; also, negative frequency issues); note that
  1271. // this is very similar to the keyword below, so we may want to consolidate
  1272. // inpKeyStrBoth="pm3 "+radicalString;
  1273. // inpKeyStrTop=" precise nosym recalc=1 dmax=0.05 gnorm=0.2 cycles=1000 t=1000";
  1274. // inpKeyStrBottom="oldgeo thermo nosym precise ";
  1275. // }
  1276. else {// this point should not be reached
  1277. Logger.error("Unexpected error in determining which MOPAC keyword set to use");
  1278. System.exit(0);
  1279. }
  1280. String polarString = "";
  1281. if (usePolar) {
  1282. if (multiplicity == 1)
  1283. polarString = System.getProperty("line.separator")
  1284. + System.getProperty("line.separator")
  1285. + System.getProperty("line.separator")
  1286. + "oldgeo polar nosym precise " + inpKeyStrBoth;
  1287. else
  1288. polarString = System.getProperty("line.separator")
  1289. + System.getProperty("line.separator")
  1290. + System.getProperty("line.separator")
  1291. + "oldgeo static nosym precise " + inpKeyStrBoth;
  1292. }
  1293. // call the OpenBabel process (note that this requires OpenBabel environment variable)
  1294. try {
  1295. File runningdir = new File(directory);
  1296. String inpKeyStrTopCombined = inpKeyStrBoth + inpKeyStrTop;
  1297. Process babelProc = null;
  1298. if (attemptNumber <= scriptAttempts) {// use UFF-refined coordinates
  1299. String[] command = { "babel", "-imol", p_molfile.getPath(),
  1300. "-xk", inpKeyStrTopCombined, "--title", InChIaug,
  1301. "-omop", name + ".mop" };
  1302. babelProc = Runtime.getRuntime()
  1303. .exec(command, null, runningdir);
  1304. } else {// use the crude coordinates
  1305. String[] command = { "babel", "-imol",
  1306. p_molfile.getCrudePath(), "-xk", inpKeyStrTopCombined,
  1307. "--title", InChIaug, "-omop", name + ".mop" };
  1308. babelProc = Runtime.getRuntime()
  1309. .exec(command, null, runningdir);
  1310. }
  1311. // read in output
  1312. InputStream is = babelProc.getInputStream();
  1313. InputStreamReader isr = new InputStreamReader(is);
  1314. BufferedReader br = new BufferedReader(isr);
  1315. String line = null;
  1316. while ((line = br.readLine()) != null) {
  1317. // do nothing
  1318. }
  1319. int exitValue = babelProc.waitFor();
  1320. babelProc.getErrorStream().close();
  1321. babelProc.getOutputStream().close();
  1322. br.close();
  1323. isr.close();
  1324. is.close();
  1325. // append the final keywords to the end of the file just written
  1326. // File mopacInpFile = new File(directory+"/"+name+".mop");
  1327. FileWriter fw = new FileWriter(directory + "/" + name + ".mop",
  1328. true);// filewriter with append = true
  1329. fw.write(System.getProperty("line.separator") + inpKeyStrBottom
  1330. + inpKeyStrBoth + polarString);// on Windows Vista, "\n" appears correctly in WordPad view, but not
  1331. // Notepad view (cf. http://forums.sun.com/thread.jspa?threadID=5386822)
  1332. fw.close();
  1333. } catch (Exception e) {
  1334. String err = "Error in running OpenBabel MOL to MOP process \n";
  1335. err += e.toString();
  1336. Logger.logStackTrace(e);
  1337. System.exit(0);
  1338. }
  1339. return maxAttemptNumber;
  1340. }
  1341. // name and directory are the name and directory for the input (and output) file;
  1342. // input is assumed to be preexisting and have the .gjf suffix
  1343. // returns an integer indicating success or failure of the Gaussian calculation: 1 for success, 0 for failure;
  1344. public int runGaussian(String name, String directory, String InChIaug) {
  1345. int flag = 0;
  1346. int successFlag = 0;
  1347. try {
  1348. String command = "g03 ";
  1349. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths
  1350. // can have spaces and are allowed to be surrounded by quotes
  1351. command = command.concat("\"" + qmfolder + "/" + name
  1352. + ".gjf\" ");// specify the input file; space is important
  1353. command = command.concat("\"" + qmfolder + "/" + name
  1354. + ".log\"");// specify the output file
  1355. } else {// non-Windows case
  1356. command = command.concat(qmfolder + "/" + name + ".gjf ");// specify the input file; space is important
  1357. command = command.concat(qmfolder + "/" + name + ".log");// specify the output file
  1358. }
  1359. Process gaussianProc = Runtime.getRuntime().exec(command);
  1360. // check for errors and display the error if there is one
  1361. InputStream is = gaussianProc.getErrorStream();
  1362. InputStreamReader isr = new InputStreamReader(is);
  1363. BufferedReader br = new BufferedReader(isr);
  1364. String line = null;
  1365. while ((line = br.readLine()) != null) {
  1366. line = line.trim();
  1367. Logger.error(line);
  1368. flag = 1;
  1369. }
  1370. // if there was an error, indicate that an error was obtained
  1371. if (flag == 1) {
  1372. Logger.info("Gaussian process received error (see above) on "
  1373. + name);
  1374. }
  1375. int exitValue = gaussianProc.waitFor();
  1376. gaussianProc.getInputStream().close();
  1377. gaussianProc.getOutputStream().close();
  1378. br.close();
  1379. isr.close();
  1380. is.close();
  1381. } catch (Exception e) {
  1382. String err = "Error in running Gaussian process \n";
  1383. err += e.toString();
  1384. Logger.logStackTrace(e);
  1385. System.exit(0);
  1386. }
  1387. // look in the output file to check for the successful termination of the Gaussian calculation
  1388. // failed jobs will contain the a line beginning with " Error termination" near the end of the file
  1389. int failureFlag = 0;
  1390. int completeFlag = 0;
  1391. String errorLine = "";// string to store the error
  1392. try {
  1393. FileReader in = new FileReader(directory + "/" + name + ".log");
  1394. BufferedReader reader = new BufferedReader(in);
  1395. String line = reader.readLine();
  1396. while (line != null) {
  1397. if (line.startsWith(" Error termination ")) {
  1398. failureFlag = 1;
  1399. errorLine = line.trim();
  1400. Logger.info("*****Error in Gaussian log file: " + errorLine);// print the error (note that in
  1401. // general, I think two lines will be printed)
  1402. } else if (line.startsWith(" ******")) {// also look for imaginary frequencies
  1403. if (line.contains("imaginary frequencies")) {
  1404. Logger.info("*****Imaginary freqencies found:");
  1405. failureFlag = 1;
  1406. }
  1407. } else if (line
  1408. .startsWith(" Normal termination of Gaussian 03 at")) {// check for completion notice at end of
  1409. // file
  1410. if (reader.readLine() == null) {// the above line will occur once in the middle of most files
  1411. // (except for monatomic species), but the following line is non-critical (e.g.
  1412. // "Link1: Proceeding to internal job step number 2.") so reading over this line should not cause a problem
  1413. completeFlag = 1;
  1414. }
  1415. }
  1416. line = reader.readLine();
  1417. }
  1418. reader.close();
  1419. in.close();
  1420. } catch (Exception e) {
  1421. String err = "Error in reading Gaussian log file \n";
  1422. err += e.toString();
  1423. Logger.logStackTrace(e);
  1424. System.exit(0);
  1425. }
  1426. // if the complete flag is still 0, the process did not complete and is a failure
  1427. if (completeFlag == 0)
  1428. failureFlag = 1;
  1429. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  1430. // requested by the user)
  1431. boolean connectivityMatch = connectivityMatchInPM3ResultQ(name,
  1432. directory, InChIaug, ".log", "g03");
  1433. if (!connectivityMatch)
  1434. if (connectivityCheck > 1) {// connectivityCheck=2
  1435. failureFlag = 1;
  1436. } else {// connectivityCheck=1; print warning and procede with "return true"
  1437. Logger.warning("Connectivity in quantum result for "
  1438. + name
  1439. + " ("
  1440. + InChIaug
  1441. + ") does not appear to match desired connectivity.");
  1442. }
  1443. }
  1444. // if the failure flag is still 0, the process should have been successful
  1445. if (failureFlag == 0)
  1446. successFlag = 1;
  1447. return successFlag;
  1448. }
  1449. // name and directory are the name and directory for the input (and output) file;
  1450. // input script is assumed to be preexisting and have the .com suffix
  1451. // returns an integer indicating success or failure of the calculation: 1 for success, 0 for failure
  1452. public int runMM4(String name, String directory, String InChIaug) {
  1453. int successFlag = 0;
  1454. // int flag = 0;
  1455. try {
  1456. File runningDirectory = new File(qmfolder);
  1457. String command = name + ".com";
  1458. // File script = new File(qmfolder+command);
  1459. // command = "./"+command;
  1460. // script.setExecutable(true);
  1461. command = "csh " + command;
  1462. Process mm4Proc = Runtime.getRuntime().exec(command, null,
  1463. runningDirectory);
  1464. // check for errors and display the error if there is one
  1465. // InputStream is = mm4Proc.getErrorStream();
  1466. // InputStreamReader isr = new InputStreamReader(is);
  1467. // BufferedReader br = new BufferedReader(isr);
  1468. // String line=null;
  1469. // while ( (line = br.readLine()) != null) {
  1470. // line = line.trim();
  1471. // if(!line.equals("STOP statement executed")){//string listed here seems to be typical
  1472. // Logger.error(line);
  1473. // flag=1;
  1474. // }
  1475. // }
  1476. InputStream is = mm4Proc.getInputStream();
  1477. InputStreamReader isr = new InputStreamReader(is);
  1478. BufferedReader br = new BufferedReader(isr);
  1479. String line = null;
  1480. while ((line = br.readLine()) != null) {
  1481. // do nothing
  1482. }
  1483. // if there was an error, indicate that an error was obtained
  1484. // if(flag==1){
  1485. // Logger.info("MM4 process received error (see above) on " + name);
  1486. // }
  1487. int exitValue = mm4Proc.waitFor();
  1488. mm4Proc.getErrorStream().close();
  1489. mm4Proc.getOutputStream().close();
  1490. br.close();
  1491. isr.close();
  1492. is.close();
  1493. } catch (Exception e) {
  1494. String err = "Error in running MM4 process \n";
  1495. err += e.toString();
  1496. Logger.logStackTrace(e);
  1497. System.exit(0);
  1498. }
  1499. // look in the output file to check for the successful termination of the MM4 calculation (cf.
  1500. // successfulMM4ResultExistsQ)
  1501. File file = new File(directory + "/" + name + ".mm4out");
  1502. int failureFlag = 1;// flag (1 or 0) indicating whether the MM4 job failed
  1503. int failureOverrideFlag = 0;// flag (1 or 0) to override success as measured by failureFlag
  1504. if (file.exists()) {// if the file exists, do further checks; otherwise, we will skip to final statement and
  1505. // return false
  1506. try {
  1507. FileReader in = new FileReader(file);
  1508. BufferedReader reader = new BufferedReader(in);
  1509. String line = reader.readLine();
  1510. while (line != null) {
  1511. String trimLine = line.trim();
  1512. if (trimLine.equals("STATISTICAL THERMODYNAMICS ANALYSIS")) {
  1513. failureFlag = 0;
  1514. } else if (trimLine.endsWith("imaginary frequencies,")) {// read the number of imaginary frequencies
  1515. // and make sure it is zero
  1516. String[] split = trimLine.split("\\s+");
  1517. if (Integer.parseInt(split[3]) > 0) {
  1518. Logger.info("*****Imaginary freqencies found:");
  1519. failureOverrideFlag = 1;
  1520. }
  1521. } else if (trimLine.contains(" 0.0 (fir )")) {
  1522. if (useCanTherm) {// zero frequencies are only acceptable when CanTherm is used
  1523. Logger.info("*****Warning: zero freqencies found (values lower than 7.7 cm^-1 are rounded to zero in MM4 output); CanTherm should hopefully correct this:");
  1524. } else {
  1525. Logger.info("*****Zero freqencies found:");
  1526. failureOverrideFlag = 1;
  1527. }
  1528. }
  1529. line = reader.readLine();
  1530. }
  1531. reader.close();
  1532. in.close();
  1533. } catch (Exception e) {
  1534. String err = "Error in reading MM4 output file \n";
  1535. err += e.toString();
  1536. Logger.logStackTrace(e);
  1537. System.exit(0);
  1538. }
  1539. }
  1540. // if the failure flag is still 0, the process should have been successful
  1541. if (failureOverrideFlag == 1)
  1542. failureFlag = 1; // job will be considered a failure if there are imaginary frequencies or if job terminates
  1543. // to to excess time/cycles
  1544. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  1545. // requested by the user)
  1546. boolean connectivityMatch = connectivityMatchInMM4ResultQ(name,
  1547. directory, InChIaug);
  1548. if (!connectivityMatch)
  1549. if (connectivityCheck > 1) {// connectivityCheck=2
  1550. failureFlag = 1;
  1551. } else {// connectivityCheck=1; print warning and procede with "return true"
  1552. Logger.warning("Connectivity in MM4 result for "
  1553. + name
  1554. + " ("
  1555. + InChIaug
  1556. + ") does not appear to match desired connectivity.");
  1557. }
  1558. }
  1559. // if the failure flag is 0 and there are no negative frequencies (and connectivity matches, if requested by the
  1560. // user), the process should have been successful
  1561. if (failureFlag == 0)
  1562. successFlag = 1;
  1563. return successFlag;
  1564. }
  1565. // name and directory are the name and directory for the input (and output) file;
  1566. // input script is assumed to be preexisting and have the .comi suffix where i is a number between 1 and rotors
  1567. public void runMM4Rotor(String name, String directory, int rotors) {
  1568. for (int j = 1; j <= rotors; j++) {
  1569. try {
  1570. File runningDirectory = new File(qmfolder);
  1571. String command = name + ".com" + j;
  1572. // File script = new File(qmfolder+command);
  1573. // command = "./"+command;
  1574. // script.setExecutable(true);
  1575. command = "csh " + command;
  1576. Process mm4Proc = Runtime.getRuntime().exec(command, null,
  1577. runningDirectory);
  1578. // check for errors and display the error if there is one
  1579. // InputStream is = mm4Proc.getErrorStream();
  1580. // InputStreamReader isr = new InputStreamReader(is);
  1581. // BufferedReader br = new BufferedReader(isr);
  1582. // String line=null;
  1583. // while ( (line = br.readLine()) != null) {
  1584. // line = line.trim();
  1585. // if(!line.equals("STOP statement executed")){//string listed here seems to be typical
  1586. // Logger.error(line);
  1587. // flag=1;
  1588. // }
  1589. // }
  1590. InputStream is = mm4Proc.getInputStream();
  1591. InputStreamReader isr = new InputStreamReader(is);
  1592. BufferedReader br = new BufferedReader(isr);
  1593. String line = null;
  1594. while ((line = br.readLine()) != null) {
  1595. // do nothing
  1596. }
  1597. // if there was an error, indicate that an error was obtained
  1598. // if(flag==1){
  1599. // Logger.info("MM4 process received error (see above) on " + name);
  1600. // }
  1601. int exitValue = mm4Proc.waitFor();
  1602. mm4Proc.getErrorStream().close();
  1603. mm4Proc.getOutputStream().close();
  1604. br.close();
  1605. isr.close();
  1606. is.close();
  1607. } catch (Exception e) {
  1608. String err = "Error in running MM4 rotor process \n";
  1609. err += e.toString();
  1610. Logger.logStackTrace(e);
  1611. System.exit(0);
  1612. }
  1613. }
  1614. return;
  1615. }
  1616. // name and directory are the name and directory for the input (and output) file;
  1617. // input is assumed to be preexisting and have the .mop suffix
  1618. // returns an integer indicating success or failure of the MOPAC calculation: 1 for success, 0 for failure;
  1619. // this function is based on the Gaussian analogue
  1620. public int runMOPAC(String name, String directory, String InChIaug) {
  1621. if (!new File(System.getenv("MOPAC_LICENSE"), "MOPAC2009.exe").exists()) {
  1622. Logger.error("Please set your MOPAC_LICENSE environment variable to the directory containing MOPAC2009.exe");
  1623. System.exit(1);
  1624. }
  1625. int flag = 0;
  1626. int successFlag = 0;
  1627. try {
  1628. String command = new File(System.getenv("MOPAC_LICENSE"),
  1629. "MOPAC2009.exe").getAbsolutePath() + " ";
  1630. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths
  1631. // can have spaces and are allowed to be surrounded by quotes
  1632. command = command.concat("\"" + directory + "/" + name
  1633. + ".mop\" ");// specify the input file; space is important
  1634. command = command.concat("\"" + directory + "/" + name
  1635. + ".out\"");// specify the output file
  1636. } else {// non-Windows case
  1637. command = command.concat(directory + "/" + name + ".mop ");// specify the input file; space is important
  1638. command = command.concat(directory + "/" + name + ".out");// specify the output file
  1639. }
  1640. Process mopacProc = Runtime.getRuntime().exec(command);
  1641. // check for errors and display the error if there is one
  1642. InputStream is = mopacProc.getErrorStream();
  1643. InputStreamReader isr = new InputStreamReader(is);
  1644. BufferedReader br = new BufferedReader(isr);
  1645. String line = null;
  1646. while ((line = br.readLine()) != null) {
  1647. line = line.trim();
  1648. Logger.error(line);
  1649. flag = 1;
  1650. }
  1651. // if there was an error, indicate that an error was obtained
  1652. if (flag == 1) {
  1653. Logger.info("MOPAC process received error (see above) on "
  1654. + name);
  1655. }
  1656. int exitValue = mopacProc.waitFor();
  1657. mopacProc.getInputStream().close();
  1658. mopacProc.getOutputStream().close();
  1659. br.close();
  1660. isr.close();
  1661. is.close();
  1662. } catch (Exception e) {
  1663. String err = "Error in running MOPAC process \n";
  1664. err += e.toString();
  1665. Logger.logStackTrace(e);
  1666. System.exit(0);
  1667. }
  1668. // look in the output file to check for the successful termination of the calculation (this is a trimmed down
  1669. // version of what appears in successfulMOPACResultExistsQ (it doesn't have the InChI check)
  1670. File file = new File(directory + "/" + name + ".out");
  1671. int failureFlag = 1;// flag (1 or 0) indicating whether the MOPAC job failed
  1672. int failureOverrideFlag = 0;// flag (1 or 0) to override success as measured by failureFlag
  1673. if (file.exists()) {// if the file exists, do further checks; otherwise, we will skip to final statement and
  1674. // return false
  1675. try {
  1676. FileReader in = new FileReader(file);
  1677. BufferedReader reader = new BufferedReader(in);
  1678. String line = reader.readLine();
  1679. while (line != null) {
  1680. String trimLine = line.trim();
  1681. if (trimLine.equals("DESCRIPTION OF VIBRATIONS")) {// check for this line; if it is here, check for
  1682. // negative frequencies
  1683. // if(!MopacFileContainsNegativeFreqsQ(name, directory)) failureFlag=0;
  1684. failureFlag = 0;
  1685. }
  1686. // negative frequencies notice example:
  1687. // NOTE: SYSTEM IS NOT A GROUND STATE, THEREFORE ZERO POINT
  1688. // ENERGY IS NOT MEANINGFULL. ZERO POINT ENERGY PRINTED
  1689. // DOES NOT INCLUDE THE 2 IMAGINARY FREQUENCIES
  1690. else if (trimLine.endsWith("IMAGINARY FREQUENCIES")) {
  1691. Logger.info("*****Imaginary freqencies found:");
  1692. failureOverrideFlag = 1;
  1693. } else if (trimLine
  1694. .equals("EXCESS NUMBER OF OPTIMIZATION CYCLES")) {// exceeding max cycles error
  1695. failureOverrideFlag = 1;
  1696. } else if (trimLine
  1697. .equals("NOT ENOUGH TIME FOR ANOTHER CYCLE")) {// timeout error
  1698. failureOverrideFlag = 1;
  1699. }
  1700. line = reader.readLine();
  1701. }
  1702. reader.close();
  1703. in.close();
  1704. } catch (Exception e) {
  1705. String err = "Error in reading MOPAC output file \n";
  1706. err += e.toString();
  1707. Logger.logStackTrace(e);
  1708. System.exit(0);
  1709. }
  1710. }
  1711. if (failureOverrideFlag == 1)
  1712. failureFlag = 1; // job will be considered a failure if there are imaginary frequencies or if job terminates
  1713. // to to excess time/cycles
  1714. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  1715. // requested by the user)
  1716. boolean connectivityMatch = connectivityMatchInPM3ResultQ(name,
  1717. directory, InChIaug, ".out", "moo");
  1718. if (!connectivityMatch)
  1719. if (connectivityCheck > 1) {// connectivityCheck=2
  1720. failureFlag = 1;
  1721. } else {// connectivityCheck=1; print warning and procede with "return true"
  1722. Logger.warning("Connectivity in quantum result for "
  1723. + name
  1724. + " ("
  1725. + InChIaug
  1726. + ") does not appear to match desired connectivity.");
  1727. }
  1728. }
  1729. // if the failure flag is 0 (completed, there are no negative frequencies, and connectivity matches (if
  1730. // requested by user)), the process should have been successful
  1731. if (failureFlag == 0)
  1732. successFlag = 1;
  1733. return successFlag;
  1734. }
  1735. public String getGaussianPM3ParseCommand(String name, String directory) {
  1736. String command = null;
  1737. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths can
  1738. // have spaces and are allowed to be surrounded by quotes
  1739. command = "python \"" + System.getProperty("RMG.workingDirectory")
  1740. + "/scripts/GaussianPM3ParsingScript.py\" ";
  1741. String logfilepath = "\"" + directory + "/" + name + ".log\"";
  1742. command = command.concat(logfilepath);
  1743. command = command
  1744. .concat(" \"" + System.getenv("RMG") + "/source\"");// this will pass $RMG/source to the script (in
  1745. // order to get the appropriate path for importing
  1746. } else {// non-Windows case
  1747. command = "python " + System.getProperty("RMG.workingDirectory")
  1748. + "/scripts/GaussianPM3ParsingScript.py ";
  1749. String logfilepath = directory + "/" + name + ".log";
  1750. command = command.concat(logfilepath);
  1751. command = command.concat(" " + System.getenv("RMG") + "/source");// this will pass $RMG/source to the script
  1752. // (in order to get the appropriate path for importing
  1753. }
  1754. return command;
  1755. }
  1756. // parse the results using cclib and return a ThermoData object; name and directory indicate the location of the
  1757. // Gaussian .log file
  1758. public ThermoData parseGaussianPM3(String name, String directory,
  1759. ChemGraph p_chemGraph) {
  1760. // //parse the Gaussian file using cclib
  1761. String command = getGaussianPM3ParseCommand(name, directory);
  1762. ThermoData result = getPM3MM4ThermoDataUsingCCLib(name, directory,
  1763. p_chemGraph, command);
  1764. result.setSource("Gaussian PM3 calculation");
  1765. Logger.info("Thermo for " + name + ": " + result.toString());// print result, at least for debugging purposes
  1766. return result;
  1767. }
  1768. public String getMM4ParseCommand(String name, String directory) {
  1769. String command = "python " + System.getProperty("RMG.workingDirectory")
  1770. + "/scripts/MM4ParsingScript.py ";
  1771. String logfilepath = directory + "/" + name + ".mm4out";
  1772. command = command.concat(logfilepath);
  1773. command = command.concat(" " + System.getenv("RMG") + "/source");// this will pass $RMG/source to the script (in
  1774. // order to get the appropriate path for importing
  1775. return command;
  1776. }
  1777. // parse the results using cclib and return a ThermoData object; name and directory indicate the location of the MM4
  1778. // .mm4out file
  1779. public ThermoData parseMM4(String name, String directory,
  1780. ChemGraph p_chemGraph) {
  1781. String command = getMM4ParseCommand(name, directory);
  1782. ThermoData result = getPM3MM4ThermoDataUsingCCLib(name, directory,
  1783. p_chemGraph, command);
  1784. result.setSource("MM4 calculation");
  1785. Logger.info("Thermo for " + name + ": " + result.toString());// print result, at least for debugging purposes
  1786. return result;
  1787. }
  1788. // parse the results using cclib and CanTherm and return a ThermoData object; name and directory indicate the
  1789. // location of the MM4 .mm4out file
  1790. // formerly known as parseMM4withForceMat
  1791. public QMData performCanThermCalcs(String name, String directory,
  1792. ChemGraph p_chemGraph, double[] dihedralMinima, boolean forceRRHO) {
  1793. // 1. parse the MM4 file with cclib to get atomic number vector and geometry
  1794. QMData qmdata = getMM4QMDataWithCClib(name, directory, true);
  1795. // unpack the needed results
  1796. double energy = qmdata.energy;
  1797. double stericEnergy = qmdata.stericEnergy;
  1798. ArrayList freqs = qmdata.freqs;
  1799. // 2. compute H0/E0; note that we will compute H0 for CanTherm by H0=Hf298(harmonicMM4)-(H298-H0)harmonicMM4,
  1800. // where harmonicMM4 values come from cclib parsing; 298.16 K is the standard temperature used by MM4; also note that
  1801. // Hthermal should include the R*T contribution (R*298.16 (enthalpy vs. energy difference)) and H0=E0 (T=0)
  1802. double T_MM4 = 298.16;
  1803. energy *= Hartree_to_kcal;// convert from Hartree to kcal/mol
  1804. stericEnergy *= Hartree_to_kcal;// convert from Hartree to kcal/mol
  1805. double Hthermal = 5. / 2. * R * T_MM4 / 1000.;// enthalpy vs. energy difference(RT)+translation(3/2*R*T)
  1806. // contribution to thermal energy
  1807. // rotational contribution
  1808. if (p_chemGraph.getAtomNumber() == 1)
  1809. Hthermal += 0.0;
  1810. else if (p_chemGraph.isLinear())
  1811. Hthermal += R * T_MM4 / 1000.;
  1812. else
  1813. Hthermal += 3. / 2. * R * T_MM4 / 1000;
  1814. // vibrational contribution
  1815. if (p_chemGraph.getAtomNumber() != 1)
  1816. Hthermal += R * calcVibH(freqs, T_MM4, h, k, c) / 1000.;
  1817. energy = energy - Hthermal;
  1818. // 3. write CanTherm input file
  1819. // determine point group using the SYMMETRY Program
  1820. String geom = qmdata.getSYMMETRYinput();
  1821. String pointGroup = determinePointGroupUsingSYMMETRYProgram(geom, name);
  1822. double sigmaCorr = getSigmaCorr(pointGroup);
  1823. String canInp = "Calculation: Thermo\n";
  1824. canInp += "Trange: 300 100 13\n";// temperatures from 300 to 1500 in increments of 100
  1825. canInp += "Scale: 1.0\n";// scale factor of 1
  1826. canInp += "Mol 1\n";
  1827. if (p_chemGraph.getAtomNumber() == 1)
  1828. canInp += "ATOM\n";
  1829. else if (p_chemGraph.isLinear())
  1830. canInp += "LINEAR\n";
  1831. else
  1832. canInp += "NONLINEAR\n";
  1833. canInp += "GEOM MM4File " + name + ".mm4out\n";// geometry file; ***special MM4 treatment in CanTherm; another
  1834. // option would be to use mm4opt file, but CanTherm code would need to be modified accordingly
  1835. canInp += "FORCEC MM4File " + name + ".fmat\n";// force constant file; ***special MM4 treatment in CanTherm
  1836. if (forceRRHO)
  1837. name = name + "_RRHO"; // "_RRHO" will be appended to InChIKey for RRHO calcs (though we want to use
  1838. // unmodified name in getQMDataWithCClib above, and in GEOM and FORCEC sections
  1839. canInp += "ENERGY " + energy + " MM4\n";// ***special MM4 treatment in CanTherm
  1840. canInp += "EXTSYM " + Math.exp(-sigmaCorr) + "\n";// ***modified treatment in CanTherm; traditional EXTSYM
  1841. // integer replaced by EXTSYM double, to allow fractional values that take chirality into account
  1842. canInp += "NELEC 1\n";// multiplicity = 1; all cases we consider will be non-radicals
  1843. int rotors = p_chemGraph.getInternalRotor();
  1844. String rotInput = null;
  1845. if (!useHindRot || rotors == 0 || forceRRHO)
  1846. canInp += "ROTORS 0\n";// do not consider hindered rotors
  1847. else {
  1848. int rotorCount = 0;
  1849. canInp += "ROTORS " + rotors + " " + name + ".rotinfo\n";
  1850. canInp += "POTENTIAL separable mm4files_inertia\n";// ***special MM4 treatment in canTherm;
  1851. String rotNumbersLine = "" + stericEnergy;// the line that will contain V0 (kcal/mol), and all the dihedral
  1852. // minima (degrees)
  1853. rotInput = "L1: 1 2 3\n";
  1854. LinkedHashMap rotorInfo = p_chemGraph.getInternalRotorInformation();
  1855. Iterator iter = rotorInfo.keySet().iterator();
  1856. while (iter.hasNext()) {
  1857. rotorCount++;
  1858. int[] rotorAtoms = (int[]) iter.next();
  1859. LinkedHashSet rotatingGroup = (LinkedHashSet) rotorInfo
  1860. .get(rotorAtoms);
  1861. Iterator iterGroup = rotatingGroup.iterator();
  1862. rotInput += "L2: " + rotorAtoms[4] + " " + rotorAtoms[1] + " "
  1863. + rotorAtoms[2];// note: rotorAtoms[4] is the rotorSymmetry number as estimated by
  1864. // calculateRotorSymmetryNumber; this will be taken into account elsewhere
  1865. while (iterGroup.hasNext()) {// print the atoms associated with rotorAtom 2
  1866. Integer id = (Integer) iterGroup.next();
  1867. if (id != rotorAtoms[2])
  1868. rotInput += " " + id;// don't print atom2
  1869. }
  1870. rotInput += "\n";
  1871. canInp += name + ".mm4rotopt" + rotorCount + " ";// potential files will be named as name.mm4rotopti
  1872. rotNumbersLine += " " + dihedralMinima[rotorCount - 1];
  1873. }
  1874. canInp += "\n" + rotNumbersLine + "\n";
  1875. }
  1876. canInp += "0\n";// no bond-additivity corrections
  1877. try {
  1878. File canFile = new File(directory + "/" + name + ".can");
  1879. FileWriter fw = new FileWriter(canFile);
  1880. fw.write(canInp);
  1881. fw.close();
  1882. if (rotInput != null) {// write the rotor information
  1883. File rotFile = new File(directory + "/" + name + ".rotinfo");
  1884. FileWriter fwr = new FileWriter(rotFile);
  1885. fwr.write(rotInput);
  1886. fwr.close();
  1887. }
  1888. } catch (Exception e) {
  1889. String err = "Error in writing CanTherm input \n";
  1890. err += e.toString();
  1891. Logger.logStackTrace(e);
  1892. System.exit(0);
  1893. }
  1894. // 4 call CanTherm
  1895. try {
  1896. File runningDirectory = new File(qmfolder);
  1897. String canCommand = "python " + System.getenv("RMG")
  1898. + "/source/CanTherm/source/CanTherm.py " + name + ".can";
  1899. Process canProc = Runtime.getRuntime().exec(canCommand, null,
  1900. runningDirectory);
  1901. InputStream is = canProc.getInputStream();
  1902. InputStreamReader isr = new InputStreamReader(is);
  1903. BufferedReader br = new BufferedReader(isr);
  1904. String line = null;
  1905. while ((line = br.readLine()) != null) {
  1906. // do nothing
  1907. }
  1908. int exitValue = canProc.waitFor();
  1909. canProc.getErrorStream().close();
  1910. canProc.getOutputStream().close();
  1911. br.close();
  1912. isr.close();
  1913. is.close();
  1914. } catch (Exception e) {
  1915. String err = "Error in running CanTherm process \n";
  1916. err += e.toString();
  1917. Logger.logStackTrace(e);
  1918. System.exit(0);
  1919. }
  1920. return qmdata;
  1921. }
  1922. public ThermoData parseCanThermFile(String name, String directory,
  1923. ChemGraph p_chemGraph) {
  1924. // 5. read CanTherm output
  1925. Double Hf298 = null;
  1926. Double S298 = null;
  1927. Double Cp300 = null;
  1928. Double Cp400 = null;
  1929. Double Cp500 = null;
  1930. Double Cp600 = null;
  1931. Double Cp800 = null;
  1932. Double Cp1000 = null;
  1933. Double Cp1500 = null;
  1934. File file = new File(directory + "/" + name + ".canout");
  1935. try {
  1936. FileReader in = new FileReader(file);
  1937. BufferedReader reader = new BufferedReader(in);
  1938. String line = reader.readLine();
  1939. while (!line.startsWith("Hf298 S298 Cps:")) {// get to the end of the file with the data we want
  1940. line = reader.readLine();
  1941. }
  1942. String[] split = reader.readLine().trim().split("\\s+");// read the next line, which contains the
  1943. // information we want
  1944. Hf298 = Double.parseDouble(split[0]);
  1945. S298 = Double.parseDouble(split[1]);
  1946. Cp300 = Double.parseDouble(split[2]);
  1947. Cp400 = Double.parseDouble(split[3]);
  1948. Cp500 = Double.parseDouble(split[4]);
  1949. Cp600 = Double.parseDouble(split[5]);
  1950. Cp800 = Double.parseDouble(split[7]);
  1951. Cp1000 = Double.parseDouble(split[9]);
  1952. Cp1500 = Double.parseDouble(split[14]);
  1953. reader.close();
  1954. in.close();
  1955. } catch (Exception e) {
  1956. String err = "Error in reading CanTherm .canout file \n";
  1957. err += e.toString();
  1958. Logger.logStackTrace(e);
  1959. System.exit(0);
  1960. }
  1961. ThermoData result = new ThermoData(Hf298, S298, Cp300, Cp400, Cp500,
  1962. Cp600, Cp800, Cp1000, Cp1500, 3, 1, 1,
  1963. "MM4 calculation; includes CanTherm analysis of force-constant matrix");// this includes rough estimates
  1964. // of uncertainty
  1965. result.setSource("MM4 calculation with CanTherm analysis");
  1966. Logger.info("Thermo for " + name + ": " + result.toString());// print result, at least for debugging purposes
  1967. return result;
  1968. }
  1969. public String getMopacPM3ParseCommand(String name, String directory) {
  1970. String command = null;
  1971. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths can
  1972. // have spaces and are allowed to be surrounded by quotes
  1973. command = "python \"" + System.getProperty("RMG.workingDirectory")
  1974. + "/scripts/MopacPM3ParsingScript.py\" ";
  1975. String logfilepath = "\"" + directory + "/" + name + ".out\"";
  1976. command = command.concat(logfilepath);
  1977. command = command
  1978. .concat(" \"" + System.getenv("RMG") + "/source\"");// this will pass $RMG/source to the script (in
  1979. // order to get the appropriate path for importing
  1980. } else {// non-Windows case
  1981. command = "python " + System.getProperty("RMG.workingDirectory")
  1982. + "/scripts/MopacPM3ParsingScript.py ";
  1983. String logfilepath = directory + "/" + name + ".out";
  1984. command = command.concat(logfilepath);
  1985. command = command.concat(" " + System.getenv("RMG") + "/source");// this will pass $RMG/source to the script
  1986. // (in order to get the appropriate path for importing
  1987. }
  1988. return command;
  1989. }
  1990. // parse the results using cclib and return a ThermoData object; name and directory indicate the location of the
  1991. // MOPAC .out file
  1992. public ThermoData parseMopac(String name, String directory,
  1993. ChemGraph p_chemGraph, String qmMethod) {
  1994. String command = getMopacPM3ParseCommand(name, directory);
  1995. ThermoData result = getPM3MM4ThermoDataUsingCCLib(name, directory,
  1996. p_chemGraph, command);
  1997. result.setSource("MOPAC "+qmMethod+" calculation");
  1998. Logger.info("Thermo for " + name + ": " + result.toString());// print result, at least for debugging purposes
  1999. return result;
  2000. }
  2001. // separated from parseMopacPM3, since the function was originally based off of parseGaussianPM3 and was very
  2002. // similar (differences being command and logfilepath variables);
  2003. public ThermoData getPM3MM4ThermoDataUsingCCLib(String name,
  2004. String directory, ChemGraph p_chemGraph, String command) {
  2005. // parse the file using cclib
  2006. int gdStateDegen = p_chemGraph.getRadicalNumber() + 1;// calculate ground state degeneracy from the number of
  2007. // radicals; this should give the same result as spin multiplicity in Gaussian input file (and output file), but we do
  2008. // not explicitly check this (we could use "mult" which cclib reads in if we wanted to do so); also, note that this is
  2009. // not always correct, as there can apparently be additional spatial degeneracy for non-symmetric linear molecules like
  2010. // OH radical (cf. http://cccbdb.nist.gov/thermo.asp)
  2011. QMData qmdata = getQMDataWithCClib(name, directory, command, false);
  2012. ThermoData result = calculateThermoFromPM3MM4Calc(qmdata, gdStateDegen,
  2013. name);
  2014. return result;
  2015. }
  2016. // returns a thermo result, given results from quantum PM3 calculation or MM4 calculation (originally, this was in
  2017. // parseGaussianPM3 function
  2018. public ThermoData calculateThermoFromPM3MM4Calc(QMData qmdata,
  2019. int gdStateDegen, String name) {
  2020. // unpack qmdata
  2021. int natoms = qmdata.natoms;
  2022. ArrayList atomicNumber = qmdata.atomicNumber; // vector of atomic numbers (integers) (apparently Vector is
  2023. // thread-safe; cf. http://answers.yahoo.com/question/index?qid=20081214065127AArZDT3; ...should I be using this
  2024. // instead?)
  2025. ArrayList x_coor = qmdata.x_coor; // vectors of x-, y-, and z-coordinates (doubles) (Angstroms) (in order
  2026. // corresponding to above atomic numbers)
  2027. ArrayList y_coor = qmdata.y_coor;
  2028. ArrayList z_coor = qmdata.z_coor;
  2029. double energy = qmdata.energy; // PM3 energy (Hf298) in Hartree (***note: in the case of MOPAC, the MOPAC file
  2030. // will contain in units of kcal/mol, but modified ccLib will return in Hartree)
  2031. double molmass = qmdata.molmass; // molecular mass in amu
  2032. ArrayList freqs = qmdata.freqs; // list of frequencies in units of cm^-1
  2033. double rotCons_1 = qmdata.rotCons_1;// rotational constants in (1/s)
  2034. double rotCons_2 = qmdata.rotCons_2;
  2035. double rotCons_3 = qmdata.rotCons_3;
  2036. // determine point group using the SYMMETRY Program
  2037. String geom = natoms + "\n";
  2038. for (int i = 0; i < natoms; i++) {
  2039. geom += atomicNumber.get(i) + " " + x_coor.get(i) + " "
  2040. + y_coor.get(i) + " " + z_coor.get(i) + "\n";
  2041. }
  2042. // String pointGroup = determinePointGroupUsingSYMMETRYProgram(geom, 0.01);
  2043. String pointGroup = determinePointGroupUsingSYMMETRYProgram(geom, name);
  2044. // calculate thermo quantities using stat. mech. equations
  2045. // boolean linearity = p_chemGraph.isLinear();//determine linearity (perhaps it would be more appropriate to
  2046. // determine this from point group?)
  2047. boolean linearity = false;
  2048. if (pointGroup.equals("Cinfv") || pointGroup.equals("Dinfh"))
  2049. linearity = true;// determine linearity from 3D-geometry; changed to correctly consider linear ketene
  2050. // radical case
  2051. // we will use number of atoms from above (alternatively, we could use the chemGraph); this is needed to test
  2052. // whether the species is monoatomic
  2053. double Hf298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500;
  2054. double sigmaCorr = getSigmaCorr(pointGroup);
  2055. Hf298 = energy * Hartree_to_kcal;
  2056. S298 = R
  2057. * Math.log(gdStateDegen)
  2058. + R
  2059. * (3.
  2060. / 2.
  2061. * Math.log(2. * Math.PI * molmass
  2062. / (1000. * Na * Math.pow(h, 2.))) + 5. / 2.
  2063. * Math.log(k * 298.15) - Math.log(100000.) + 5. / 2.);// electronic + translation; note use of
  2064. // 10^5 Pa for standard pressure; also note that molecular mass needs to be divided by 1000 for kg units
  2065. Cp300 = 5. / 2. * R;
  2066. Cp400 = 5. / 2. * R;
  2067. Cp500 = 5. / 2. * R;
  2068. Cp600 = 5. / 2. * R;
  2069. Cp800 = 5. / 2. * R;
  2070. Cp1000 = 5. / 2. * R;
  2071. Cp1500 = 5. / 2. * R;
  2072. if (natoms > 1) {// include statistical correction and rotational (without symmetry number, vibrational
  2073. // contributions if species is polyatomic
  2074. if (linearity) {// linear case
  2075. // determine the rotational constant (note that one of the rotcons will be zero)
  2076. double rotCons;
  2077. if (rotCons_1 > 0.0001)
  2078. rotCons = rotCons_1;
  2079. else
  2080. rotCons = rotCons_2;
  2081. S298 += R * sigmaCorr + R
  2082. * (Math.log(k * 298.15 / (h * rotCons)) + 1) + R
  2083. * calcVibS(freqs, 298.15, h, k, c);
  2084. Cp300 += R + R * calcVibCp(freqs, 300., h, k, c);
  2085. Cp400 += R + R * calcVibCp(freqs, 400., h, k, c);
  2086. Cp500 += R + R * calcVibCp(freqs, 500., h, k, c);
  2087. Cp600 += R + R * calcVibCp(freqs, 600., h, k, c);
  2088. Cp800 += R + R * calcVibCp(freqs, 800., h, k, c);
  2089. Cp1000 += R + R * calcVibCp(freqs, 1000., h, k, c);
  2090. Cp1500 += R + R * calcVibCp(freqs, 1500., h, k, c);
  2091. } else {// nonlinear case
  2092. S298 += R
  2093. * sigmaCorr
  2094. + R
  2095. * (3.
  2096. / 2.
  2097. * Math.log(k * 298.15 / h)
  2098. - 1.
  2099. / 2.
  2100. * Math.log(rotCons_1 * rotCons_2 * rotCons_3
  2101. / Math.PI) + 3. / 2.) + R
  2102. * calcVibS(freqs, 298.15, h, k, c);
  2103. Cp300 += 3. / 2. * R + R * calcVibCp(freqs, 300., h, k, c);
  2104. Cp400 += 3. / 2. * R + R * calcVibCp(freqs, 400., h, k, c);
  2105. Cp500 += 3. / 2. * R + R * calcVibCp(freqs, 500., h, k, c);
  2106. Cp600 += 3. / 2. * R + R * calcVibCp(freqs, 600., h, k, c);
  2107. Cp800 += 3. / 2. * R + R * calcVibCp(freqs, 800., h, k, c);
  2108. Cp1000 += 3. / 2. * R + R * calcVibCp(freqs, 1000., h, k, c);
  2109. Cp1500 += 3. / 2. * R + R * calcVibCp(freqs, 1500., h, k, c);
  2110. }
  2111. }
  2112. ThermoData result = new ThermoData(Hf298, S298, Cp300, Cp400, Cp500,
  2113. Cp600, Cp800, Cp1000, Cp1500, 5, 1, 1, "PM3 or MM4 calculation");// this includes rough estimates of
  2114. // uncertainty
  2115. return result;
  2116. }
  2117. // gets the statistical correction for S in dimensionless units (divided by R)
  2118. public double getSigmaCorr(String pointGroup) {
  2119. double sigmaCorr = 0;
  2120. // determine statistical correction factor for 1. external rotational symmetry (affects rotational partition
  2121. // function) and 2. chirality (will add R*ln2 to entropy) based on point group
  2122. // ref: http://cccbdb.nist.gov/thermo.asp
  2123. // assumptions below for Sn, T, Th, O, I seem to be in line with expectations based on order reported at:
  2124. // http://en.wikipedia.org/w/index.php?title=List_of_character_tables_for_chemically_important_3D_point_groups&oldid=287261611
  2125. // (assuming order = symmetry number * 2 (/2 if chiral))...this appears to be true for all point groups I "know" to be
  2126. // correct
  2127. // minor concern: does SYMMETRY appropriately calculate all Sn groups considering 2007 discovery of previous
  2128. // errors in character tables (cf. Wikipedia article above)
  2129. if (pointGroup.equals("C1"))
  2130. sigmaCorr = +Math.log(2.);// rot. sym. = 1, chiral
  2131. else if (pointGroup.equals("Cs"))
  2132. sigmaCorr = 0; // rot. sym. = 1
  2133. else if (pointGroup.equals("Ci"))
  2134. sigmaCorr = 0; // rot. sym. = 1
  2135. else if (pointGroup.equals("C2"))
  2136. sigmaCorr = 0;// rot. sym. = 2, chiral (corrections cancel)
  2137. else if (pointGroup.equals("C3"))
  2138. sigmaCorr = +Math.log(2.) - Math.log(3.);// rot. sym. = 3, chiral
  2139. else if (pointGroup.equals("C4"))
  2140. sigmaCorr = +Math.log(2.) - Math.log(4.);// rot. sym. = 4, chiral
  2141. else if (pointGroup.equals("C5"))
  2142. sigmaCorr = +Math.log(2.) - Math.log(5.);// rot. sym. = 5, chiral
  2143. else if (pointGroup.equals("C6"))
  2144. sigmaCorr = +Math.log(2.) - Math.log(6.);// rot. sym. = 6, chiral
  2145. else if (pointGroup.equals("C7"))
  2146. sigmaCorr = +Math.log(2.) - Math.log(7.);// rot. sym. = 7, chiral
  2147. else if (pointGroup.equals("C8"))
  2148. sigmaCorr = +Math.log(2.) - Math.log(8.);// rot. sym. = 8, chiral
  2149. else if (pointGroup.equals("D2"))
  2150. sigmaCorr = +Math.log(2.) - Math.log(4.);// rot. sym. = 4, chiral
  2151. else if (pointGroup.equals("D3"))
  2152. sigmaCorr = +Math.log(2.) - Math.log(6.);// rot. sym. = 6, chiral
  2153. else if (pointGroup.equals("D4"))
  2154. sigmaCorr = +Math.log(2.) - Math.log(8.);// rot. sym. = 8, chiral
  2155. else if (pointGroup.equals("D5"))
  2156. sigmaCorr = +Math.log(2.) - Math.log(10.);// rot. sym. = 10, chiral
  2157. else if (pointGroup.equals("D6"))
  2158. sigmaCorr = +Math.log(2.) - Math.log(12.);// rot. sym. = 12, chiral
  2159. else if (pointGroup.equals("D7"))
  2160. sigmaCorr = +Math.log(2.) - Math.log(14.);// rot. sym. = 14, chiral
  2161. else if (pointGroup.equals("D8"))
  2162. sigmaCorr = +Math.log(2.) - Math.log(16.);// rot. sym. = 16, chiral
  2163. else if (pointGroup.equals("C2v"))
  2164. sigmaCorr = -Math.log(2.);// rot. sym. = 2
  2165. else if (pointGroup.equals("C3v"))
  2166. sigmaCorr = -Math.log(3.);// rot. sym. = 3
  2167. else if (pointGroup.equals("C4v"))
  2168. sigmaCorr = -Math.log(4.);// rot. sym. = 4
  2169. else if (pointGroup.equals("C5v"))
  2170. sigmaCorr = -Math.log(5.);// rot. sym. = 5
  2171. else if (pointGroup.equals("C6v"))
  2172. sigmaCorr = -Math.log(6.);// rot. sym. = 6
  2173. else if (pointGroup.equals("C7v"))
  2174. sigmaCorr = -Math.log(7.);// rot. sym. = 7
  2175. else if (pointGroup.equals("C8v"))
  2176. sigmaCorr = -Math.log(8.);// rot. sym. = 8
  2177. else if (pointGroup.equals("C2h"))
  2178. sigmaCorr = -Math.log(2.);// rot. sym. = 2
  2179. else if (pointGroup.equals("C3h"))
  2180. sigmaCorr = -Math.log(3.);// rot. sym. = 3
  2181. else if (pointGroup.equals("C4h"))
  2182. sigmaCorr = -Math.log(4.);// rot. sym. = 4
  2183. else if (pointGroup.equals("C5h"))
  2184. sigmaCorr = -Math.log(5.);// rot. sym. = 5
  2185. else if (pointGroup.equals("C6h"))
  2186. sigmaCorr = -Math.log(6.);// rot. sym. = 6
  2187. else if (pointGroup.equals("C7h"))
  2188. sigmaCorr = -Math.log(7.);// rot. sym. = 7
  2189. else if (pointGroup.equals("C8h"))
  2190. sigmaCorr = -Math.log(8.);// rot. sym. = 8
  2191. else if (pointGroup.equals("D2h"))
  2192. sigmaCorr = -Math.log(4.);// rot. sym. = 4
  2193. else if (pointGroup.equals("D3h"))
  2194. sigmaCorr = -Math.log(6.);// rot. sym. = 6
  2195. else if (pointGroup.equals("D4h"))
  2196. sigmaCorr = -Math.log(8.);// rot. sym. = 8
  2197. else if (pointGroup.equals("D5h"))
  2198. sigmaCorr = -Math.log(10.);// rot. sym. = 10
  2199. else if (pointGroup.equals("D6h"))
  2200. sigmaCorr = -Math.log(12.);// rot. sym. = 12
  2201. else if (pointGroup.equals("D7h"))
  2202. sigmaCorr = -Math.log(14.);// rot. sym. = 14
  2203. else if (pointGroup.equals("D8h"))
  2204. sigmaCorr = -Math.log(16.);// rot. sym. = 16
  2205. else if (pointGroup.equals("D2d"))
  2206. sigmaCorr = -Math.log(4.);// rot. sym. = 4
  2207. else if (pointGroup.equals("D3d"))
  2208. sigmaCorr = -Math.log(6.);// rot. sym. = 6
  2209. else if (pointGroup.equals("D4d"))
  2210. sigmaCorr = -Math.log(8.);// rot. sym. = 8
  2211. else if (pointGroup.equals("D5d"))
  2212. sigmaCorr = -Math.log(10.);// rot. sym. = 10
  2213. else if (pointGroup.equals("D6d"))
  2214. sigmaCorr = -Math.log(12.);// rot. sym. = 12
  2215. else if (pointGroup.equals("D7d"))
  2216. sigmaCorr = -Math.log(14.);// rot. sym. = 14
  2217. else if (pointGroup.equals("D8d"))
  2218. sigmaCorr = -Math.log(16.);// rot. sym. = 16
  2219. else if (pointGroup.equals("S4"))
  2220. sigmaCorr = -Math.log(2.);// rot. sym. = 2 ;*** assumed achiral
  2221. else if (pointGroup.equals("S6"))
  2222. sigmaCorr = -Math.log(3.);// rot. sym. = 3 ;*** assumed achiral
  2223. else if (pointGroup.equals("S8"))
  2224. sigmaCorr = -Math.log(4.);// rot. sym. = 4 ;*** assumed achiral
  2225. else if (pointGroup.equals("T"))
  2226. sigmaCorr = +Math.log(2.) - Math.log(12.);// rot. sym. = 12, *** assumed chiral
  2227. else if (pointGroup.equals("Th"))
  2228. sigmaCorr = -Math.log(12.);// ***assumed rot. sym. = 12
  2229. else if (pointGroup.equals("Td"))
  2230. sigmaCorr = -Math.log(12.);// rot. sym. = 12
  2231. else if (pointGroup.equals("O"))
  2232. sigmaCorr = +Math.log(2.) - Math.log(24.);// ***assumed rot. sym. = 24, chiral
  2233. else if (pointGroup.equals("Oh"))
  2234. sigmaCorr = -Math.log(24.);// rot. sym. = 24
  2235. else if (pointGroup.equals("Cinfv"))
  2236. sigmaCorr = 0;// rot. sym. = 1
  2237. else if (pointGroup.equals("Dinfh"))
  2238. sigmaCorr = -Math.log(2.);// rot. sym. = 2
  2239. else if (pointGroup.equals("I"))
  2240. sigmaCorr = +Math.log(2.) - Math.log(60.);// ***assumed rot. sym. = 60, chiral
  2241. else if (pointGroup.equals("Ih"))
  2242. sigmaCorr = -Math.log(60.);// rot. sym. = 60
  2243. else if (pointGroup.equals("Kh"))
  2244. sigmaCorr = 0;// arbitrarily set to zero...one could argue that it is infinite; apparently this is the point
  2245. // group of a single atom (cf. http://www.cobalt.chem.ucalgary.ca/ps/symmetry/tests/G_Kh); this should not have a
  2246. // rotational partition function, and we should not use the symmetry correction in this case
  2247. else {// this point should not be reached, based on checks performed in determinePointGroupUsingSYMMETRYProgram
  2248. Logger.critical("Unrecognized point group: " + pointGroup);
  2249. System.exit(0);
  2250. }
  2251. return sigmaCorr;
  2252. }
  2253. // determine the point group using the SYMMETRY program (http://www.cobalt.chem.ucalgary.ca/ps/symmetry/)
  2254. // required input is a line with number of atoms followed by lines for each atom including atom number and x,y,z
  2255. // coordinates
  2256. // finalTol determines how loose the point group criteria are; values are comparable to those specifed in the
  2257. // GaussView point group interface
  2258. // public String determinePointGroupUsingSYMMETRYProgram(String geom, double finalTol){
  2259. public String determinePointGroupUsingSYMMETRYProgram(String geom,
  2260. String name) {
  2261. int attemptNumber = 1;
  2262. int maxAttemptNumber = 4;
  2263. boolean pointGroupFound = false;
  2264. // write the input file
  2265. File inputFile = new File(qmfolder, name + ".symm");
  2266. try {
  2267. FileWriter fw = new FileWriter(inputFile);
  2268. fw.write(geom);
  2269. fw.close();
  2270. } catch (IOException e) {
  2271. String err = "Error writing input file for point group calculation";
  2272. err += e.toString();
  2273. Logger.critical(err);
  2274. System.exit(0);
  2275. }
  2276. String result = "";
  2277. String command = "";
  2278. while (attemptNumber <= maxAttemptNumber && !pointGroupFound) {
  2279. // call the program and read the result
  2280. result = "";
  2281. String[] lineArray;
  2282. try {
  2283. if (System.getProperty("os.name").toLowerCase()
  2284. .contains("windows")) {// the Windows case where the precompiled executable seems to need to be
  2285. // called from a batch script
  2286. if (attemptNumber == 1)
  2287. command = "\""
  2288. + System.getProperty("RMG.workingDirectory")
  2289. + "/scripts/symmetryDefault2.bat\" \""
  2290. + inputFile.getAbsolutePath() + "\"";// 12/1/09 gmagoon: switched to use slightly looser
  2291. // criteria of 0.02 rather than 0.01 to handle methylperoxyl radical result from MOPAC
  2292. else if (attemptNumber == 2)
  2293. command = "\""
  2294. + System.getProperty("RMG.workingDirectory")
  2295. + "/scripts/symmetryLoose.bat\" \""
  2296. + inputFile.getAbsolutePath() + "\"";// looser criteria (0.1 instead of 0.01) to
  2297. // properly identify C2v group in VBURLMBUVWIEMQ-UHFFFAOYAVmult5 (InChI=1/C3H4O2/c1-3(2,4)5/h1-2H2/mult5) MOPAC result;
  2298. // C2 and sigma were identified with default, but it should be C2 and sigma*2
  2299. else if (attemptNumber == 3)
  2300. command = "\""
  2301. + System.getProperty("RMG.workingDirectory")
  2302. + "/scripts/symmetryLoose2.bat\" \""
  2303. + inputFile.getAbsolutePath() + "\"";// looser criteria to properly identify D2d group
  2304. // in XXHDHKZTASMVSX-UHFFFAOYAM
  2305. else if (attemptNumber == 4) {
  2306. Logger.error("*****WARNING****: Using last-resort symmetry estimation options; symmetry may be underestimated");
  2307. command = "\""
  2308. + System.getProperty("RMG.workingDirectory")
  2309. + "/scripts/symmetryLastResort.bat\" \""
  2310. + inputFile.getAbsolutePath() + "\"";// last resort criteria to avoid crashing (this
  2311. // will likely result in identification of C1 point group)
  2312. } else {
  2313. Logger.critical("Invalid attemptNumber: "
  2314. + attemptNumber);
  2315. System.exit(0);
  2316. }
  2317. } else {// in other (non-Windows) cases, where it is compiled from scratch, we should be able to run
  2318. // this directly
  2319. if (attemptNumber == 1)
  2320. command = System.getProperty("RMG.workingDirectory")
  2321. + "/bin/SYMMETRY.EXE -final 0.02 "
  2322. + inputFile.getAbsolutePath();// 12/1/09 gmagoon: switched to use slightly looser
  2323. // criteria of 0.02 rather than 0.01 to handle methylperoxyl radical result from MOPAC
  2324. else if (attemptNumber == 2)
  2325. command = System.getProperty("RMG.workingDirectory")
  2326. + "/bin/SYMMETRY.EXE -final 0.1 "
  2327. + inputFile.getAbsolutePath();// looser criteria (0.1 instead of 0.01) to properly
  2328. // identify C2v group in VBURLMBUVWIEMQ-UHFFFAOYAVmult5 (InChI=1/C3H4O2/c1-3(2,4)5/h1-2H2/mult5) MOPAC result; C2 and
  2329. // sigma were identified with default, but it should be C2 and sigma*2
  2330. else if (attemptNumber == 3)
  2331. command = System.getProperty("RMG.workingDirectory")
  2332. + "/bin/SYMMETRY.EXE -primary 0.2 -final 0.1 "
  2333. + inputFile.getAbsolutePath();// looser criteria to identify D2d group in
  2334. // XXHDHKZTASMVSX-UHFFFAOYAM (InChI=1/C12H16/c1-5-9-10(6-2)12(8-4)11(9)7-3/h5-12H,1-4H2)
  2335. else if (attemptNumber == 4) {
  2336. Logger.warning("*****WARNING****: Using last-resort symmetry estimation options; symmetry may be underestimated");
  2337. command = System.getProperty("RMG.workingDirectory")
  2338. + "/bin/SYMMETRY.EXE -final 0.0 "
  2339. + inputFile.getAbsolutePath();// last resort criteria to avoid crashing (this will
  2340. // likely result in identification of C1 point group)
  2341. } else {
  2342. Logger.critical("Invalid attemptNumber: "
  2343. + attemptNumber);
  2344. System.exit(0);
  2345. }
  2346. }
  2347. Process symmProc = Runtime.getRuntime().exec(command);
  2348. // check for errors and display the error if there is one
  2349. InputStream is = symmProc.getInputStream();
  2350. InputStreamReader isr = new InputStreamReader(is);
  2351. BufferedReader br = new BufferedReader(isr);
  2352. String line = null;
  2353. while ((line = br.readLine()) != null) {
  2354. if (line.startsWith("It seems to be the ")) {// last line, ("It seems to be the [x] point group")
  2355. // indicates point group
  2356. lineArray = line.split(" ");// split the line around spaces
  2357. result = lineArray[5];// point group string should be the 6th word
  2358. }
  2359. }
  2360. int exitValue = symmProc.waitFor();
  2361. symmProc.getErrorStream().close();
  2362. symmProc.getOutputStream().close();
  2363. br.close();
  2364. isr.close();
  2365. is.close();
  2366. } catch (Exception e) {
  2367. String err = "Error in running point group calculation process using SYMMETRY \n";
  2368. err += e.toString();
  2369. Logger.logStackTrace(e);
  2370. System.exit(0);
  2371. }
  2372. // check for a recognized point group
  2373. if (result.equals("C1") || result.equals("Cs")
  2374. || result.equals("Ci") || result.equals("C2")
  2375. || result.equals("C3") || result.equals("C4")
  2376. || result.equals("C5") || result.equals("C6")
  2377. || result.equals("C7") || result.equals("C8")
  2378. || result.equals("D2") || result.equals("D3")
  2379. || result.equals("D4") || result.equals("D5")
  2380. || result.equals("D6") || result.equals("D7")
  2381. || result.equals("D8") || result.equals("C2v")
  2382. || result.equals("C3v") || result.equals("C4v")
  2383. || result.equals("C5v") || result.equals("C6v")
  2384. || result.equals("C7v") || result.equals("C8v")
  2385. || result.equals("C2h") || result.equals("C3h")
  2386. || result.equals("C4h") || result.equals("C5h")
  2387. || result.equals("C6h") || result.equals("C7h")
  2388. || result.equals("C8h") || result.equals("D2h")
  2389. || result.equals("D3h") || result.equals("D4h")
  2390. || result.equals("D5h") || result.equals("D6h")
  2391. || result.equals("D7h") || result.equals("D8h")
  2392. || result.equals("D2d") || result.equals("D3d")
  2393. || result.equals("D4d") || result.equals("D5d")
  2394. || result.equals("D6d") || result.equals("D7d")
  2395. || result.equals("D8d") || result.equals("S4")
  2396. || result.equals("S6") || result.equals("S8")
  2397. || result.equals("T") || result.equals("Th")
  2398. || result.equals("Td") || result.equals("O")
  2399. || result.equals("Oh") || result.equals("Cinfv")
  2400. || result.equals("Dinfh") || result.equals("I")
  2401. || result.equals("Ih") || result.equals("Kh"))
  2402. pointGroupFound = true;
  2403. else {
  2404. if (attemptNumber < maxAttemptNumber)
  2405. Logger.info("Attempt number " + attemptNumber
  2406. + " did not identify a recognized point group ("
  2407. + result
  2408. + "). Will retry with looser point group criteria.");
  2409. else {
  2410. Logger.critical("Final attempt number " + attemptNumber
  2411. + " did not identify a recognized point group ("
  2412. + result + "). Exiting.");
  2413. System.exit(0);
  2414. }
  2415. attemptNumber++;
  2416. }
  2417. }
  2418. Logger.info("Point group: " + result);// print result, at least for debugging purposes
  2419. return result;
  2420. }
  2421. // gmagoon 6/8/09
  2422. // calculate the vibrational contribution (divided by R, dimensionless) at temperature, T, in Kelvin to entropy
  2423. // p_freqs in cm^-1; c in cm/s; k in J/K; h in J-s
  2424. // ref.: http://cccbdb.nist.gov/thermo.asp
  2425. public double calcVibS(ArrayList p_freqs, double p_T, double h, double k,
  2426. double c) {
  2427. double Scontrib = 0;
  2428. double dr;
  2429. for (int i = 0; i < p_freqs.size(); i++) {
  2430. double freq = (Double) p_freqs.get(i);
  2431. dr = h * c * freq / (k * p_T); // frequently used dimensionless ratio
  2432. Scontrib = Scontrib - Math.log(1. - Math.exp(-dr)) + dr
  2433. * Math.exp(-dr) / (1. - Math.exp(-dr));
  2434. }
  2435. return Scontrib;
  2436. }
  2437. // gmagoon 6/8/09
  2438. // calculate the vibrational contribution (divided by R, dimensionless) at temperature, T, in Kelvin to heat
  2439. // capacity, Cp
  2440. // p_freqs in cm^-1; c in cm/s; k in J/K; h in J-s
  2441. // ref.: http://cccbdb.nist.gov/thermo.asp
  2442. public double calcVibCp(ArrayList p_freqs, double p_T, double h, double k,
  2443. double c) {
  2444. double Cpcontrib = 0;
  2445. double dr;
  2446. for (int i = 0; i < p_freqs.size(); i++) {
  2447. double freq = (Double) p_freqs.get(i);
  2448. dr = h * c * freq / (k * p_T); // frequently used dimensionless ratio
  2449. Cpcontrib = Cpcontrib + Math.pow(dr, 2.) * Math.exp(-dr)
  2450. / Math.pow(1. - Math.exp(-dr), 2.);
  2451. }
  2452. return Cpcontrib;
  2453. }
  2454. // gmagoon 6/23/10
  2455. // calculate the vibrational contribution (divided by R, units of K) at temperature, T, in Kelvin to Hthermal (ZPE
  2456. // not included)
  2457. // p_freqs in cm^-1; c in cm/s; k in J/K; h in J-s
  2458. // we need to ignore zero frequencies, as MM4 does, to avoid dividing by zero; based on equation for Ev in
  2459. // http://www.gaussian.com/g_whitepap/thermo.htm; however, we ignore zero point contribution to be consistent with the
  2460. // input that CanTherm currently takes; note, however, that this could introduce some small inaccuracies, as the
  2461. // frequencies may be slightly different in CanTherm vs. MM4, particularly for a frequency < 7.7 cm^-1 (treated as zero
  2462. // in MM4)
  2463. public double calcVibH(ArrayList p_freqs, double p_T, double h, double k,
  2464. double c) {
  2465. double Hcontrib = 0;
  2466. double dr;
  2467. for (int i = 0; i < p_freqs.size(); i++) {
  2468. double freq = (Double) p_freqs.get(i);
  2469. if (freq > 0.0) {// ignore zero frequencies as MM4 does
  2470. dr = h * c * freq / (k * p_T); // frequently used dimensionless ratio
  2471. Hcontrib = Hcontrib + dr * p_T / (Math.exp(dr) - 1.);
  2472. }
  2473. }
  2474. return Hcontrib;
  2475. }
  2476. // determine the QM filename (element 0) and augmented InChI (element 1) for a ChemGraph
  2477. // QM filename is InChIKey appended with mult3, mult4, mult5, or mult6 for multiplicities of 3 or higher
  2478. // augmented InChI is InChI appended with /mult3, /mult4, /mult5, or /mult6 for multiplicities of 3 or higher
  2479. public String[] getQMFileName(ChemGraph p_chemGraph) {
  2480. String[] result = new String[2];
  2481. result[0] = p_chemGraph.getModifiedInChIKeyAnew();// need to generate InChI and key anew because ChemGraph may
  2482. // have changed (in particular, adding/removing hydrogens in HBI process)
  2483. result[1] = p_chemGraph.getModifiedInChIAnew();
  2484. return result;
  2485. }
  2486. public String getInChIFromModifiedInChI(String modInChI) {
  2487. int pos = modInChI.indexOf("/mult");
  2488. if (pos < 0)
  2489. return modInChI;// no mult component
  2490. else
  2491. return modInChI.substring(0, pos); // return the component before the mult section
  2492. }
  2493. // removes b, t, m, and s layers from InChI
  2494. public String stripStereochemLayersFromInChI(String InChI) {
  2495. // String result1=InChI.replaceFirst("/b[^/]*", "");//replaces forward slash followed by b followed by anything
  2496. // except forward slash with an empty string
  2497. // String result2=result1.replaceFirst("/t[^/]*", "");//replaces forward slash followed by t followed by
  2498. // anything except forward slash with an empty string
  2499. // String result3=result2.replaceFirst("/m[^/]*", "");//replaces forward slash followed by m followed by
  2500. // anything except forward slash with an empty string
  2501. // String result4=result3.replaceFirst("/s[^/]*", "");//replaces forward slash followed by s followed by
  2502. // anything except forward slash with an empty string
  2503. return InChI.replaceFirst("/b[^/]*", "").replaceFirst("/t[^/]*", "")
  2504. .replaceFirst("/m[^/]*", "").replaceFirst("/s[^/]*", "");// a shorted version of the commented out code
  2505. // above
  2506. }
  2507. // returns true if a Gaussian file for the given name and directory (.log suffix) exists and indicates successful
  2508. // completion (same criteria as used after calculation runs); terminates if the InChI doesn't match the InChI in the
  2509. // file or if there is no InChI in the file; returns false otherwise
  2510. public boolean successfulGaussianResultExistsQ(String name,
  2511. String directory, String InChIaug) {
  2512. // part of the code is taken from runGaussian code above
  2513. // look in the output file to check for the successful termination of the Gaussian calculation
  2514. // failed jobs will contain the a line beginning with " Error termination" near the end of the file
  2515. File file = new File(directory + "/" + name + ".log");
  2516. if (file.exists()) {// if the file exists, do further checks; otherwise, we will skip to final statement and
  2517. // return false
  2518. int failureFlag = 0;// flag (1 or 0) indicating whether the Gaussian job failed
  2519. int InChIMatch = 0;// flag (1 or 0) indicating whether the InChI in the file matches InChIaug; this can only
  2520. // be 1 if InChIFound is also 1;
  2521. int InChIFound = 0;// flag (1 or 0) indicating whether an InChI was found in the log file
  2522. int InChIPartialMatch = 0;// flag (1 or 0) indicating whether the InChI in the log file is a substring of
  2523. // the InChI in RMG's memory
  2524. int completeFlag = 0;
  2525. String logFileInChI = "";
  2526. try {
  2527. FileReader in = new FileReader(file);
  2528. BufferedReader reader = new BufferedReader(in);
  2529. String line = reader.readLine();
  2530. while (line != null) {
  2531. if (line.startsWith(" Error termination "))
  2532. failureFlag = 1;
  2533. else if (line.startsWith(" ******")) {// also look for imaginary frequencies
  2534. if (line.contains("imaginary frequencies"))
  2535. failureFlag = 1;
  2536. } else if (line.startsWith(" InChI=")) {
  2537. logFileInChI = line.trim();
  2538. // continue reading lines until a line of dashes is found (in this way, we can read InChIs that
  2539. // span multiple lines)
  2540. line = reader.readLine();
  2541. while (!line.startsWith(" --------")) {
  2542. logFileInChI += line.trim();
  2543. line = reader.readLine();
  2544. }
  2545. InChIFound = 1;
  2546. if (logFileInChI.equals(InChIaug))
  2547. InChIMatch = 1;
  2548. else if (InChIaug.startsWith(logFileInChI))
  2549. InChIPartialMatch = 1;
  2550. } else if (line
  2551. .startsWith(" Normal termination of Gaussian 03 at")) {// check for completion notice at end
  2552. // of file
  2553. if (reader.readLine() == null) {// the above line will occur once in the middle of most files
  2554. // (except for monatomic species), but the following line is non-critical (e.g.
  2555. // "Link1: Proceeding to internal job step number 2.") so reading over this line should not cause a problem
  2556. completeFlag = 1;
  2557. }
  2558. }
  2559. line = reader.readLine();
  2560. }
  2561. reader.close();
  2562. in.close();
  2563. } catch (Exception e) {
  2564. String err = "Error in reading preexisting Gaussian log file \n";
  2565. err += e.toString();
  2566. Logger.logStackTrace(e);
  2567. System.exit(0);
  2568. }
  2569. // if the complete flag is still 0, the process did not complete and is a failure
  2570. if (completeFlag == 0)
  2571. failureFlag = 1;
  2572. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  2573. // requested by the user)
  2574. boolean connectivityMatch = connectivityMatchInPM3ResultQ(name,
  2575. directory, InChIaug, ".log", "g03");
  2576. if (!connectivityMatch)
  2577. if (connectivityCheck > 1) {// connectivityCheck=2
  2578. failureFlag = 1;
  2579. } else {// connectivityCheck=1; print warning and procede with "return true"
  2580. Logger.warning("Connectivity in quantum result for "
  2581. + name
  2582. + " ("
  2583. + InChIaug
  2584. + ") does not appear to match desired connectivity.");
  2585. }
  2586. }
  2587. // if the failure flag is still 0, the process should have been successful
  2588. if (failureFlag == 0 && InChIMatch == 1) {
  2589. Logger.info("Pre-existing successful quantum result for "
  2590. + name + " (" + InChIaug
  2591. + ") has been found. This log file will be used.");
  2592. return true;
  2593. } else if (InChIFound == 1 && InChIMatch == 0) {// InChIs do not match (most likely due to limited name
  2594. // length mirrored in log file (79 characters), but possibly due to a collision)
  2595. if (InChIPartialMatch == 1) {// case where the InChI in memory begins with the InChI in the log file; we
  2596. // will continue and check the input file, printing a warning if there is no match
  2597. File inputFile = new File(directory + "/" + name + ".gjf");
  2598. if (inputFile.exists()) {// read the Gaussian inputFile
  2599. String inputFileInChI = "";
  2600. try {
  2601. FileReader inI = new FileReader(inputFile);
  2602. BufferedReader readerI = new BufferedReader(inI);
  2603. String lineI = readerI.readLine();
  2604. while (lineI != null) {
  2605. if (lineI.startsWith(" InChI=")) {
  2606. inputFileInChI = lineI.trim();
  2607. }
  2608. lineI = readerI.readLine();
  2609. }
  2610. readerI.close();
  2611. inI.close();
  2612. } catch (Exception e) {
  2613. String err = "Error in reading preexisting Gaussian gjf file \n";
  2614. err += e.toString();
  2615. Logger.logStackTrace(e);
  2616. System.exit(0);
  2617. }
  2618. if (inputFileInChI.equals(InChIaug)) {
  2619. if (failureFlag == 0) {
  2620. Logger.info("Pre-existing successful quantum result for "
  2621. + name
  2622. + " ("
  2623. + InChIaug
  2624. + ") has been found. This log file will be used. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than 79 characters)");
  2625. return true;
  2626. } else {// otherwise, failureFlag==1
  2627. Logger.info("Pre-existing quantum result for "
  2628. + name
  2629. + " ("
  2630. + InChIaug
  2631. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than 79 characters)");
  2632. return false;
  2633. }
  2634. } else {
  2635. if (inputFileInChI.equals("")) {// InChI was not found in input file
  2636. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  2637. + name
  2638. + " RMG Augmented InChI = "
  2639. + InChIaug
  2640. + " Log file Augmented InChI = "
  2641. + logFileInChI
  2642. + " . InChI could not be found in the Gaussian input file. You should manually check that the log file contains the intended species.");
  2643. return true;
  2644. } else {// InChI was found but doesn't match
  2645. Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  2646. + name
  2647. + " RMG Augmented InChI = "
  2648. + InChIaug
  2649. + " Gaussian input file Augmented InChI = "
  2650. + inputFileInChI);
  2651. System.exit(0);
  2652. }
  2653. }
  2654. } else {
  2655. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  2656. + name
  2657. + " RMG Augmented InChI = "
  2658. + InChIaug
  2659. + " Log file Augmented InChI = "
  2660. + logFileInChI
  2661. + " . Gaussian input file could not be found to check full InChI. You should manually check that the log file contains the intended species.");
  2662. return true;
  2663. }
  2664. } else {
  2665. Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  2666. + name
  2667. + " RMG Augmented InChI = "
  2668. + InChIaug
  2669. + " Log file Augmented InChI = " + logFileInChI);
  2670. System.exit(0);
  2671. }
  2672. } else if (InChIFound == 0) {
  2673. Logger.critical("An InChI was not found in file: " + name
  2674. + ".log");
  2675. System.exit(0);
  2676. } else if (failureFlag == 1) {// note these should cover all possible results for this block, and if the
  2677. // file.exists block is entered, it should return from within the block and should not reach the return statement below
  2678. Logger.info("Pre-existing quantum result for "
  2679. + name
  2680. + " ("
  2681. + InChIaug
  2682. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation.");
  2683. return false;
  2684. }
  2685. }
  2686. // we could print a line here for cases where the file doesn't exist, but this would probably be too verbose
  2687. return false;
  2688. }
  2689. // returns true if a successful result exists (either Gaussian or MOPAC)
  2690. // public boolean [] successfulResultExistsQ(String name, String directory, String InChIaug){
  2691. // boolean gaussianResult=successfulGaussianResultExistsQ(name, directory, InChIaug);
  2692. // boolean mopacResult=successfulMOPACResultExistsQ(name, directory, InChIaug);
  2693. // return (gaussianResult || mopacResult);// returns true if either a successful Gaussian or MOPAC result exists
  2694. // }
  2695. // returns true if a MOPAC output file for the given name and directory (.out suffix) exists and indicates
  2696. // successful completion (same criteria as used after calculation runs); terminates if the InChI doesn't match the InChI
  2697. // in the file or if there is no InChI in the file; returns false otherwise
  2698. public boolean successfulMopacResultExistsQ(String name, String directory,
  2699. String InChIaug) {
  2700. // part of the code is taken from analogous code for Gaussian
  2701. // look in the output file to check for the successful termination of the calculation (assumed to be successful
  2702. // if "description of vibrations appears)
  2703. File file = new File(directory + "/" + name + ".out");
  2704. if (file.exists()) {// if the file exists, do further checks; otherwise, we will skip to final statement and
  2705. // return false
  2706. int failureFlag = 1;// flag (1 or 0) indicating whether the MOPAC job failed
  2707. int failureOverrideFlag = 0;// flag (1 or 0) to override success as measured by failureFlag
  2708. int InChIMatch = 0;// flag (1 or 0) indicating whether the InChI in the file matches InChIaug; this can only
  2709. // be 1 if InChIFound is also 1;
  2710. int InChIFound = 0;// flag (1 or 0) indicating whether an InChI was found in the log file
  2711. int InChIPartialMatch = 0;// flag (1 or 0) indicating whether the InChI in the log file is a substring of
  2712. // the InChI in RMG's memory
  2713. String logFileInChI = "";
  2714. try {
  2715. FileReader in = new FileReader(file);
  2716. BufferedReader reader = new BufferedReader(in);
  2717. String line = reader.readLine();
  2718. while (line != null) {
  2719. String trimLine = line.trim();
  2720. if (trimLine.equals("DESCRIPTION OF VIBRATIONS")) {
  2721. // if(!MopacFileContainsNegativeFreqsQ(name, directory)) failureFlag=0;//check for this line; if
  2722. // it is here, check for negative frequencies
  2723. failureFlag = 0;
  2724. }
  2725. // negative frequencies notice example:
  2726. // NOTE: SYSTEM IS NOT A GROUND STATE, THEREFORE ZERO POINT
  2727. // ENERGY IS NOT MEANINGFULL. ZERO POINT ENERGY PRINTED
  2728. // DOES NOT INCLUDE THE 2 IMAGINARY FREQUENCIES
  2729. else if (trimLine.endsWith("IMAGINARY FREQUENCIES")) {
  2730. // Logger.info("*****Imaginary freqencies found:");
  2731. failureOverrideFlag = 1;
  2732. } else if (trimLine
  2733. .equals("EXCESS NUMBER OF OPTIMIZATION CYCLES")) {// exceeding max cycles error
  2734. failureOverrideFlag = 1;
  2735. } else if (trimLine
  2736. .equals("NOT ENOUGH TIME FOR ANOTHER CYCLE")) {// timeout error
  2737. failureOverrideFlag = 1;
  2738. } else if (line.startsWith(" InChI=")) {
  2739. logFileInChI = line.trim();// output files should take up to 240 characters of the name in the
  2740. // input file
  2741. InChIFound = 1;
  2742. if (logFileInChI.equals(InChIaug))
  2743. InChIMatch = 1;
  2744. else if (InChIaug.startsWith(logFileInChI))
  2745. InChIPartialMatch = 1;
  2746. }
  2747. line = reader.readLine();
  2748. }
  2749. reader.close();
  2750. in.close();
  2751. } catch (Exception e) {
  2752. String err = "Error in reading preexisting MOPAC output file \n";
  2753. err += e.toString();
  2754. Logger.logStackTrace(e);
  2755. System.exit(0);
  2756. }
  2757. if (failureOverrideFlag == 1)
  2758. failureFlag = 1; // job will be considered a failure if there are imaginary frequencies or if job
  2759. // terminates to to excess time/cycles
  2760. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  2761. // requested by the user)
  2762. boolean connectivityMatch = connectivityMatchInPM3ResultQ(name,
  2763. directory, InChIaug, ".out", "moo");
  2764. if (!connectivityMatch)
  2765. if (connectivityCheck > 1) {// connectivityCheck=2
  2766. failureFlag = 1;
  2767. } else {// connectivityCheck=1; print warning and procede with "return true"
  2768. Logger.warning("Connectivity in quantum result for "
  2769. + name
  2770. + " ("
  2771. + InChIaug
  2772. + ") does not appear to match desired connectivity.");
  2773. }
  2774. }
  2775. // if the failure flag is still 0, the process should have been successful
  2776. if (failureFlag == 0 && InChIMatch == 1) {
  2777. Logger.info("Pre-existing successful MOPAC quantum result for "
  2778. + name + " (" + InChIaug
  2779. + ") has been found. This log file will be used.");
  2780. return true;
  2781. } else if (InChIFound == 1 && InChIMatch == 0) {// InChIs do not match (most likely due to limited name
  2782. // length mirrored in log file (240 characters), but possibly due to a collision)
  2783. // if(InChIPartialMatch == 1){//case where the InChI in memory begins with the InChI in the log file; we
  2784. // will continue and check the input file, printing a warning if there is no match
  2785. // look in the input file if the InChI doesn't match (apparently, certain characters can be deleted in
  2786. // MOPAC output file for long InChIs)
  2787. File inputFile = new File(directory + "/" + name + ".mop");
  2788. if (inputFile.exists()) {// read the MOPAC inputFile
  2789. String inputFileInChI = "";
  2790. try {
  2791. FileReader inI = new FileReader(inputFile);
  2792. BufferedReader readerI = new BufferedReader(inI);
  2793. String lineI = readerI.readLine();
  2794. while (lineI != null) {
  2795. if (lineI.startsWith("InChI=")) {
  2796. inputFileInChI = lineI.trim();
  2797. }
  2798. lineI = readerI.readLine();
  2799. }
  2800. readerI.close();
  2801. inI.close();
  2802. } catch (Exception e) {
  2803. String err = "Error in reading preexisting MOPAC input file \n";
  2804. err += e.toString();
  2805. Logger.logStackTrace(e);
  2806. System.exit(0);
  2807. }
  2808. if (inputFileInChI.equals(InChIaug)) {
  2809. if (failureFlag == 0) {
  2810. Logger.info("Pre-existing successful MOPAC quantum result for "
  2811. + name
  2812. + " ("
  2813. + InChIaug
  2814. + ") has been found. This log file will be used. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than 240 characters or characters probably deleted from InChI in .out file)");
  2815. return true;
  2816. } else {// otherwise, failureFlag==1
  2817. Logger.info("Pre-existing MOPAC quantum result for "
  2818. + name
  2819. + " ("
  2820. + InChIaug
  2821. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation or Gaussian result (if available) will be used. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than 240 characters or characters probably deleted from InChI in .out file)");
  2822. return false;
  2823. }
  2824. } else {
  2825. if (inputFileInChI.equals("")) {// InChI was not found in input file
  2826. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  2827. + name
  2828. + " RMG Augmented InChI = "
  2829. + InChIaug
  2830. + " Log file Augmented InChI = "
  2831. + logFileInChI
  2832. + " . InChI could not be found in the MOPAC input file. You should manually check that the output file contains the intended species.");
  2833. return true;
  2834. } else {// InChI was found but doesn't match
  2835. Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  2836. + name
  2837. + " RMG Augmented InChI = "
  2838. + InChIaug
  2839. + " MOPAC input file Augmented InChI = "
  2840. + inputFileInChI
  2841. + " Log file Augmented InChI = "
  2842. + logFileInChI);
  2843. System.exit(0);
  2844. }
  2845. }
  2846. } else {
  2847. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  2848. + name
  2849. + " RMG Augmented InChI = "
  2850. + InChIaug
  2851. + " Log file Augmented InChI = "
  2852. + logFileInChI
  2853. + " . MOPAC input file could not be found to check full InChI. You should manually check that the log file contains the intended species.");
  2854. return true;
  2855. }
  2856. // }
  2857. // else{
  2858. // Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  2859. // + name + " RMG Augmented InChI = "+ InChIaug + " MOPAC output file Augmented InChI = "+logFileInChI);
  2860. // System.exit(0);
  2861. // }
  2862. } else if (InChIFound == 0) {
  2863. Logger.critical("An InChI was not found in file: " + name
  2864. + ".out");
  2865. System.exit(0);
  2866. } else if (failureFlag == 1) {// note these should cover all possible results for this block, and if the
  2867. // file.exists block is entered, it should return from within the block and should not reach the return statement below
  2868. Logger.info("Pre-existing MOPAC quantum result for "
  2869. + name
  2870. + " ("
  2871. + InChIaug
  2872. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation or Gaussian result (if available) will be used.");
  2873. return false;
  2874. }
  2875. }
  2876. // we could print a line here for cases where the file doesn't exist, but this would probably be too verbose
  2877. return false;
  2878. }
  2879. // returns true if an MM4 output file for the given name and directory (.mm4out suffix) exists and indicates
  2880. // successful completion (same criteria as used after calculation runs); terminates if the InChI doesn't match the InChI
  2881. // in the file or if there is no InChI in the file; returns false otherwise
  2882. public boolean successfulMM4ResultExistsQ(String name, String directory,
  2883. String InChIaug) {
  2884. // part of the code is taken from analogous code for MOPAC (first ~half) and Gaussian (second ~half)
  2885. // look in the output file to check for the successful termination of the calculation (assumed to be successful
  2886. // if "description of vibrations appears)
  2887. int failureFlag = 1;// flag (1 or 0) indicating whether the MM4 job failed
  2888. int failureOverrideFlag = 0;// flag (1 or 0) to override success as measured by failureFlag
  2889. File file = new File(directory + "/" + name + ".mm4out");
  2890. File canFile = new File(directory + "/" + name + ".canout");
  2891. int InChIMatch = 0;// flag (1 or 0) indicating whether the InChI in the file matches InChIaug; this can only be
  2892. // 1 if InChIFound is also 1;
  2893. int InChIFound = 0;// flag (1 or 0) indicating whether an InChI was found in the log file
  2894. int InChIPartialMatch = 0;// flag (1 or 0) indicating whether the InChI in the log file is a substring of the
  2895. // InChI in RMG's memory
  2896. if (useCanTherm) {// if we are using CanTherm, check whether a CanTherm output file exists...if it does, we will
  2897. // continue on, otherwise, we will rerun calculations (including MM4 calculation) from scratch to ensure our atom
  2898. // numbering is consistent; note: if the .canout file exists, we still want to check for the actual MM4 file even if we
  2899. // are using CanTherm and reading CanTherm output because 1. it ensures we have the correct species and don't have an
  2900. // InChI collision 2. it is needed for getting the geometry which is needed for the symmetry number corrections applied
  2901. // to CanTherm output (which doesn't include symmetry number considerations)
  2902. if (!canFile.exists())
  2903. return false;
  2904. }
  2905. if (file.exists()) {// if the file exists, do further checks; otherwise, we will skip to final statement and
  2906. // return false
  2907. String logFileInChI = "";
  2908. try {
  2909. FileReader in = new FileReader(file);
  2910. BufferedReader reader = new BufferedReader(in);
  2911. String line = reader.readLine();
  2912. while (line != null) {
  2913. String trimLine = line.trim();
  2914. if (trimLine.equals("STATISTICAL THERMODYNAMICS ANALYSIS")) {
  2915. failureFlag = 0;
  2916. } else if (trimLine.endsWith("imaginary frequencies,")) {// read the number of imaginary frequencies
  2917. // and make sure it is zero
  2918. String[] split = trimLine.split("\\s+");
  2919. if (Integer.parseInt(split[3]) > 0) {
  2920. Logger.info("*****Imaginary freqencies found:");
  2921. failureOverrideFlag = 1;
  2922. }
  2923. } else if (trimLine.contains(" 0.0 (fir )")) {
  2924. if (useCanTherm) {// zero frequencies are only acceptable when CanTherm is used
  2925. Logger.info("*****Warning: zero freqencies found (values lower than 7.7 cm^-1 are rounded to zero in MM4 output); CanTherm should hopefully correct this:");
  2926. } else {
  2927. Logger.info("*****Zero freqencies found:");
  2928. failureOverrideFlag = 1;
  2929. }
  2930. } else if (trimLine.startsWith("InChI=")) {
  2931. logFileInChI = line.trim();// output files should take up to about 60 (?) characters of the name
  2932. // in the input file
  2933. InChIFound = 1;
  2934. if (logFileInChI.equals(InChIaug))
  2935. InChIMatch = 1;
  2936. else if (InChIaug.startsWith(logFileInChI))
  2937. InChIPartialMatch = 1;
  2938. }
  2939. line = reader.readLine();
  2940. }
  2941. reader.close();
  2942. in.close();
  2943. } catch (Exception e) {
  2944. String err = "Error in reading preexisting MM4 output file \n";
  2945. err += e.toString();
  2946. Logger.logStackTrace(e);
  2947. System.exit(0);
  2948. }
  2949. if (failureOverrideFlag == 1)
  2950. failureFlag = 1; // job will be considered a failure if there are imaginary frequencies or if job
  2951. // terminates to to excess time/cycles
  2952. if (failureFlag == 0 && connectivityCheck > 0) {// if the process was successful, check the connectivity (if
  2953. // requested by the user)
  2954. boolean connectivityMatch = connectivityMatchInMM4ResultQ(name,
  2955. directory, InChIaug);
  2956. if (!connectivityMatch)
  2957. if (connectivityCheck > 1) {// connectivityCheck=2
  2958. failureFlag = 1;
  2959. } else {// connectivityCheck=1; print warning and procede with "return true"
  2960. Logger.warning("Connectivity in MM4 result for "
  2961. + name
  2962. + " ("
  2963. + InChIaug
  2964. + ") does not appear to match desired connectivity.");
  2965. }
  2966. }
  2967. // if the failure flag is still 0, the process should have been successful
  2968. if (failureFlag == 0 && InChIMatch == 1) {
  2969. Logger.info("Pre-existing successful MM4 result for " + name
  2970. + " (" + InChIaug
  2971. + ") has been found. This log file will be used.");
  2972. return true;
  2973. } else if (InChIFound == 1 && InChIMatch == 0) {// InChIs do not match (most likely due to limited name
  2974. // length mirrored in log file (79 characters), but possibly due to a collision)
  2975. if (InChIPartialMatch == 1) {// case where the InChI in memory begins with the InChI in the log file; we
  2976. // will continue and check the input file, printing a warning if there is no match
  2977. File inputFile = new File(directory + "/" + name + ".mm4");
  2978. if (inputFile.exists()) {// read the MM4 inputFile
  2979. String inputFileInChI = "";
  2980. try {
  2981. FileReader inI = new FileReader(inputFile);
  2982. BufferedReader readerI = new BufferedReader(inI);
  2983. String lineI = readerI.readLine();
  2984. // InChI should be repeated after in the first line of the input file
  2985. inputFileInChI = lineI.trim().substring(80);// extract the string starting with character 81
  2986. readerI.close();
  2987. inI.close();
  2988. } catch (Exception e) {
  2989. String err = "Error in reading preexisting MM4 .mm4 file \n";
  2990. err += e.toString();
  2991. Logger.logStackTrace(e);
  2992. System.exit(0);
  2993. }
  2994. if (inputFileInChI.equals(InChIaug)) {
  2995. if (failureFlag == 0) {
  2996. Logger.info("Pre-existing successful MM4 result for "
  2997. + name
  2998. + " ("
  2999. + InChIaug
  3000. + ") has been found. This log file will be used. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than ~60 characters)");
  3001. return true;
  3002. } else {// otherwise, failureFlag==1
  3003. Logger.info("Pre-existing MM4 result for "
  3004. + name
  3005. + " ("
  3006. + InChIaug
  3007. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation. *Note that input file was read to confirm lack of InChIKey collision (InChI probably more than ~60 characters)");
  3008. return false;
  3009. }
  3010. } else {
  3011. if (inputFileInChI.equals("")) {// InChI was not found in input file
  3012. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  3013. + name
  3014. + " RMG Augmented InChI = "
  3015. + InChIaug
  3016. + " Log file Augmented InChI = "
  3017. + logFileInChI
  3018. + " . InChI could not be found in the MM4 input file. You should manually check that the log file contains the intended species.");
  3019. return true;
  3020. } else {// InChI was found but doesn't match
  3021. Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  3022. + name
  3023. + " RMG Augmented InChI = "
  3024. + InChIaug
  3025. + " MM4 input file Augmented InChI = "
  3026. + inputFileInChI);
  3027. System.exit(0);
  3028. }
  3029. }
  3030. } else {
  3031. Logger.info("*****Warning: potential InChIKey collision: InChIKey(augmented) = "
  3032. + name
  3033. + " RMG Augmented InChI = "
  3034. + InChIaug
  3035. + " Log file Augmented InChI = "
  3036. + logFileInChI
  3037. + " . MM4 input file could not be found to check full InChI. You should manually check that the log file contains the intended species.");
  3038. return true;
  3039. }
  3040. } else {
  3041. Logger.critical("Congratulations! You may have discovered one of the first recorded instances of an InChIKey collision: InChIKey(augmented) = "
  3042. + name
  3043. + " RMG Augmented InChI = "
  3044. + InChIaug
  3045. + " Log file Augmented InChI = " + logFileInChI);
  3046. System.exit(0);
  3047. }
  3048. } else if (InChIFound == 0) {
  3049. Logger.critical("An InChI was not found in file: " + name
  3050. + ".mm4out");
  3051. System.exit(0);
  3052. } else if (failureFlag == 1) {// note these should cover all possible results for this block, and if the
  3053. // file.exists block is entered, it should return from within the block and should not reach the return statement below
  3054. Logger.info("Pre-existing MM4 result for "
  3055. + name
  3056. + " ("
  3057. + InChIaug
  3058. + ") has been found, but the result was apparently unsuccessful. The file will be overwritten with a new calculation.");
  3059. return false;
  3060. }
  3061. }
  3062. // we could print a line here for cases where the file doesn't exist, but this would probably be too verbose
  3063. return false;
  3064. }
  3065. // //checks the MOPAC file for negative frequencies
  3066. // public boolean MopacFileContainsNegativeFreqsQ(String name, String directory){
  3067. // boolean negativeFreq=false;
  3068. //
  3069. // //code below copied from parseMopacPM3()
  3070. // String command = "c:/Python25/python.exe c:/Python25/MopacPM3ParsingScript.py ";//this should eventually be modified
  3071. // for added generality
  3072. // String logfilepath=directory+"/"+name+".out";
  3073. // command=command.concat(logfilepath);
  3074. //
  3075. // //much of code below is copied from calculateThermoFromPM3Calc()
  3076. // //parse the Mopac file using cclib
  3077. // int natoms = 0; //number of atoms from Mopac file; in principle, this should agree with number of chemGraph atoms
  3078. // ArrayList atomicNumber = new ArrayList(); //vector of atomic numbers (integers) (apparently Vector is thread-safe;
  3079. // cf. http://answers.yahoo.com/question/index?qid=20081214065127AArZDT3; ...should I be using this instead?)
  3080. // ArrayList x_coor = new ArrayList(); //vectors of x-, y-, and z-coordinates (doubles) (Angstroms) (in order
  3081. // corresponding to above atomic numbers)
  3082. // ArrayList y_coor = new ArrayList();
  3083. // ArrayList z_coor = new ArrayList();
  3084. // double energy = 0; //PM3 energy (Hf298) in Hartree (***note: in the case of MOPAC, the MOPAC file will contain in
  3085. // units of kcal/mol, but modified ccLib will return in Hartree)
  3086. // double molmass = 0; //molecular mass in amu
  3087. // ArrayList freqs = new ArrayList(); //list of frequencies in units of cm^-1
  3088. // double rotCons_1 = 0;//rotational constants in (1/s)
  3089. // double rotCons_2 = 0;
  3090. // double rotCons_3 = 0;
  3091. // //int gdStateDegen = p_chemGraph.getRadicalNumber()+1;//calculate ground state degeneracy from the number of
  3092. // radicals; this should give the same result as spin multiplicity in Gaussian input file (and output file), but we do
  3093. // not explicitly check this (we could use "mult" which cclib reads in if we wanted to do so); also, note that this is
  3094. // not always correct, as there can apparently be additional spatial degeneracy for non-symmetric linear molecules like
  3095. // OH radical (cf. http://cccbdb.nist.gov/thermo.asp)
  3096. // try{
  3097. // File runningdir=new File(directory);
  3098. // Process cclibProc = Runtime.getRuntime().exec(command, null, runningdir);
  3099. // //read the stdout of the process, which should contain the desired information in a particular format
  3100. // InputStream is = cclibProc.getInputStream();
  3101. // InputStreamReader isr = new InputStreamReader(is);
  3102. // BufferedReader br = new BufferedReader(isr);
  3103. // String line=null;
  3104. // //example output:
  3105. // // C:\Python25>python.exe GaussianPM3ParsingScript.py TEOS.out
  3106. // // 33
  3107. // // [ 6 6 8 14 8 6 6 8 6 6 8 6 6 1 1 1 1 1 1 1 1 1 1 1 1
  3108. // // 1 1 1 1 1 1 1 1]
  3109. // // [[ 2.049061 -0.210375 3.133106]
  3110. // // [ 1.654646 0.321749 1.762752]
  3111. // // [ 0.359284 -0.110429 1.471465]
  3112. // // [-0.201871 -0.013365 -0.12819 ]
  3113. // // [ 0.086307 1.504918 -0.82893 ]
  3114. // // [-0.559186 2.619928 -0.284003]
  3115. // // [-0.180246 3.839463 -1.113029]
  3116. // // [ 0.523347 -1.188305 -1.112765]
  3117. // // [ 1.857584 -1.018167 -1.495088]
  3118. // // [ 2.375559 -2.344392 -2.033403]
  3119. // // [-1.870397 -0.297297 -0.075427]
  3120. // // [-2.313824 -1.571765 0.300245]
  3121. // // [-3.83427 -1.535927 0.372171]
  3122. // // [ 1.360346 0.128852 3.917699]
  3123. // // [ 2.053945 -1.307678 3.160474]
  3124. // // [ 3.055397 0.133647 3.403037]
  3125. // // [ 1.677262 1.430072 1.750899]
  3126. // // [ 2.372265 -0.029237 0.985204]
  3127. // // [-0.245956 2.754188 0.771433]
  3128. // // [-1.656897 2.472855 -0.287156]
  3129. // // [-0.664186 4.739148 -0.712606]
  3130. // // [-0.489413 3.734366 -2.161038]
  3131. // // [ 0.903055 4.016867 -1.112198]
  3132. // // [ 1.919521 -0.229395 -2.269681]
  3133. // // [ 2.474031 -0.680069 -0.629949]
  3134. // // [ 2.344478 -3.136247 -1.273862]
  3135. // // [ 1.786854 -2.695974 -2.890647]
  3136. // // [ 3.41648 -2.242409 -2.365094]
  3137. // // [-1.884889 -1.858617 1.28054 ]
  3138. // // [-1.976206 -2.322432 -0.440995]
  3139. // // [-4.284706 -1.26469 -0.591463]
  3140. // // [-4.225999 -2.520759 0.656131]
  3141. // // [-4.193468 -0.809557 1.112677]]
  3142. // // -14.1664924726
  3143. // // [ 9.9615 18.102 27.0569 31.8459 39.0096 55.0091
  3144. // // 66.4992 80.4552 86.4912 123.3551 141.6058 155.5448
  3145. // // 159.4747 167.0013 178.5676 207.3738 237.3201 255.3487
  3146. // // 264.5649 292.867 309.4248 344.6503 434.8231 470.2074
  3147. // // 488.9717 749.1722 834.257 834.6594 837.7292 839.6352
  3148. // // 887.9767 892.9538 899.5374 992.1851 1020.6164 1020.8671
  3149. // // 1028.3897 1046.7945 1049.1768 1059.4704 1065.1505 1107.4001
  3150. // // 1108.1567 1109.0466 1112.6677 1122.7785 1124.4315 1128.4163
  3151. // // 1153.3438 1167.6705 1170.9627 1174.9613 1232.1826 1331.8459
  3152. // // 1335.3932 1335.8677 1343.9556 1371.37 1372.8127 1375.5428
  3153. // // 1396.0344 1402.4082 1402.7554 1403.2463 1403.396 1411.6946
  3154. // // 1412.2456 1412.3519 1414.5982 1415.3613 1415.5698 1415.7993
  3155. // // 1418.5409 2870.7446 2905.3132 2907.0361 2914.1662 2949.2646
  3156. // // 2965.825 2967.7667 2971.5223 3086.3849 3086.3878 3086.6448
  3157. // // 3086.687 3089.2274 3089.4105 3089.4743 3089.5841 3186.0753
  3158. // // 3186.1375 3186.3511 3186.365 ]
  3159. // // [ 0.52729 0.49992 0.42466]
  3160. // //note: above example has since been updated to print molecular mass; also frequency and atomic number format has
  3161. // been updated
  3162. // String [] stringArray;
  3163. // natoms = Integer.parseInt(br.readLine());//read line 1: number of atoms
  3164. // stringArray = br.readLine().replace("[", "").replace("]","").trim().split(",\\s+");//read line 2: the atomic numbers
  3165. // (first removing braces)
  3166. // // line = br.readLine().replace("[", "").replace("]","");//read line 2: the atomic numbers (first removing braces)
  3167. // // StringTokenizer st = new StringTokenizer(line); //apprently the stringTokenizer class is deprecated, but I am
  3168. // having trouble getting the regular expressions to work properly
  3169. // for(int i=0; i < natoms; i++){
  3170. // // atomicNumber.add(i,Integer.parseInt(stringArray[i]));
  3171. // atomicNumber.add(i,Integer.parseInt(stringArray[i]));
  3172. // }
  3173. // for(int i=0; i < natoms; i++){
  3174. // stringArray = br.readLine().replace("[", "").replace("]","").trim().split("\\s+");//read line 3+: coordinates for
  3175. // atom i; used /s+ for split; using spaces with default limit of 0 was giving empty string
  3176. // x_coor.add(i,Double.parseDouble(stringArray[0]));
  3177. // y_coor.add(i,Double.parseDouble(stringArray[1]));
  3178. // z_coor.add(i,Double.parseDouble(stringArray[2]));
  3179. // }
  3180. // energy = Double.parseDouble(br.readLine());//read next line: energy
  3181. // molmass = Double.parseDouble(br.readLine());//read next line: molecular mass (in amu)
  3182. // if (natoms>1){//read additional info for non-monoatomic species
  3183. // stringArray = br.readLine().replace("[", "").replace("]","").trim().split(",\\s+");//read next line: frequencies
  3184. // for(int i=0; i < stringArray.length; i++){
  3185. // freqs.add(i,Double.parseDouble(stringArray[i]));
  3186. // }
  3187. // stringArray = br.readLine().replace("[", "").replace("]","").trim().split("\\s+");//read next line rotational
  3188. // constants (converting from GHz to Hz in the process)
  3189. // rotCons_1 = Double.parseDouble(stringArray[0])*1000000000;
  3190. // rotCons_2 = Double.parseDouble(stringArray[1])*1000000000;
  3191. // rotCons_3 = Double.parseDouble(stringArray[2])*1000000000;
  3192. // }
  3193. // while ( (line = br.readLine()) != null) {
  3194. // //do nothing (there shouldn't be any more information, but this is included to get all the output)
  3195. // }
  3196. // int exitValue = cclibProc.waitFor();
  3197. // }
  3198. // catch (Exception e) {
  3199. // Logger.logStackTrace(e);
  3200. // String err = "Error in running ccLib Python process \n";
  3201. // err += e.toString();
  3202. // Logger.critical(err);
  3203. // System.exit(0);
  3204. // }
  3205. //
  3206. // //start of code "new" to this function (aside from initialization of negativeFreq)
  3207. // if(natoms > 0){
  3208. // for (int i=0; i<freqs.size(); i++){
  3209. // if((Double)freqs.get(i) < 0) negativeFreq = true;
  3210. // }
  3211. // }
  3212. // return negativeFreq;
  3213. // }
  3214. // check the connectivity in a Gaussian/MOPAC result (or XYZ file); returns true if the there appears to be a match,
  3215. // and returns false otherwise
  3216. // the connectivity is assumed to match if the InChI produced by processing the result/coordinates through OpenBabel
  3217. // into MOL file and then using InChI utility produces an InChI that is equivalent (following stereochemical layer
  3218. // removal) to the InChI stored in memory
  3219. // we do not need/want to compare the augmented multN part of the InChI, so InChI will be converted within to the
  3220. // non-modified version
  3221. // for gaussian03, outfileExtension should be ".log" and babelType should be "g03"
  3222. // for MOPAC, outfileExtension should be ".out" and babelType should be "moo"
  3223. // for XYZ, outfileExtension should be ".xyz" and babelType should be "xyz"
  3224. public boolean connectivityMatchInPM3ResultQ(String name, String directory,
  3225. String augInChI, String outfileExtension, String babelType) {
  3226. // Step 0: get the un-modified InChI
  3227. String InChI = getInChIFromModifiedInChI(augInChI);
  3228. // Step 1: call the OpenBabel process (note that this requires OpenBabel environment variable) to convert to MOL
  3229. // file
  3230. String molPath = directory + "/" + name + ".mol";// the path to the MOL file to be created (with connectivity);
  3231. try {
  3232. File runningdir = new File(directory);
  3233. String command = null;
  3234. String inpPath = directory + "/" + name + outfileExtension;// the path to the QM output file with
  3235. // coordinates
  3236. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case
  3237. command = "babel -i" + babelType + " \"" + inpPath
  3238. + "\" -omol \"" + molPath + "\"";
  3239. } else {
  3240. command = "babel -i" + babelType + " " + inpPath + " -omol "
  3241. + molPath;
  3242. }
  3243. Process babelProc = Runtime.getRuntime().exec(command, null,
  3244. runningdir);
  3245. // read in output
  3246. InputStream is = babelProc.getInputStream();
  3247. InputStreamReader isr = new InputStreamReader(is);
  3248. BufferedReader br = new BufferedReader(isr);
  3249. String line = null;
  3250. while ((line = br.readLine()) != null) {
  3251. // do nothing
  3252. // Logger.info(line);
  3253. }
  3254. int exitValue = babelProc.waitFor();
  3255. babelProc.getErrorStream().close();
  3256. babelProc.getOutputStream().close();
  3257. br.close();
  3258. isr.close();
  3259. is.close();
  3260. } catch (Exception e) {
  3261. String err = "Error in running OpenBabel G03/MOO/XYZ to MOL process \n";
  3262. err += e.toString();
  3263. Logger.logStackTrace(e);
  3264. System.exit(0);
  3265. }
  3266. // Step 2. convert the MOL file to InChI (with stereochem layers removed)
  3267. String[] result = Species.runInChIProcess(new File(molPath), new File(
  3268. System.getProperty("RMG.InChI_running_directory")
  3269. + "/species.txt"), true);
  3270. String InChI3D = stripStereochemLayersFromInChI(result[0]);
  3271. // Step 3. check whether there is a match (i.e. InChI equals InChI3D)
  3272. if (InChI3D.equals(InChI)) {
  3273. return true;
  3274. } else {// if there is a mismatch using OpenBabel (either due to OpenBabel segmentation fault (e.g.
  3275. // http://sourceforge.net/tracker/?func=detail&aid=3417992&group_id=40728&atid=428740) or tolerances that are too tight
  3276. // (e.g. InChI=1/C10H12O2/c1-2-6-9(5-1)11-12-10-7-3-4-8-10/h1,7H,2-4,6,8H2)), print a warning and go to the backup check
  3277. // using MoleCoor
  3278. Logger.info("***For species "
  3279. + name
  3280. + " an OpenBabel-based check suggests the optimized three-dimensional InChI ("
  3281. + InChI3D
  3282. + ") does not match the intended (unmodified) InChI ("
  3283. + InChI + "). Will retry connectivity check with MoleCoor");
  3284. return connectivityMatchInPM3ResultQSecondary(name, directory,
  3285. augInChI, babelType);
  3286. }
  3287. }
  3288. // a backup/fallback connectivity check using MoleCoor for when the primary approach using OpenBabel fails
  3289. public boolean connectivityMatchInPM3ResultQSecondary(String name,
  3290. String directory, String augInChI, String babelType) {
  3291. // Step 0: get the un-modified InChI
  3292. String InChI = getInChIFromModifiedInChI(augInChI);
  3293. String molPath = directory + "/" + name + ".mol";// the path to the MOL file to be created (with connectivity);
  3294. // Step 1. parse the file according to the program used, as determined by babelType (we could just use
  3295. // coordinates from a pre-existing MOL file, but that wouldn't handle cases where OpenBabel crashed due to segmentation
  3296. // fault
  3297. String command = null;
  3298. if (babelType.equals("moo"))
  3299. command = getMopacPM3ParseCommand(name, directory); // MOPAC case
  3300. else if (babelType.equals("g03"))
  3301. command = getGaussianPM3ParseCommand(name, directory); // G03 case
  3302. else if (babelType.equals("xyz"))
  3303. command = getMM4ParseCommand(name, directory); // MM4 case
  3304. else {
  3305. Logger.critical("Unrecognized babelType during backup connectivity check");
  3306. System.exit(0);
  3307. }
  3308. QMData qmdata = getQMDataWithCClib(name, directory, command, false);
  3309. // Step 2a. write the coordinates as an xyz file
  3310. String inpFilePath = directory + "/" + name + ".xyz";
  3311. // construct the string to write
  3312. int natoms = qmdata.natoms;
  3313. ArrayList atomicNumber = qmdata.atomicNumber; // vector of atomic numbers (integers) (apparently Vector is
  3314. // thread-safe; cf. http://answers.yahoo.com/question/index?qid=20081214065127AArZDT3; ...should I be using this
  3315. // instead?)
  3316. ArrayList x_coor = qmdata.x_coor; // vectors of x-, y-, and z-coordinates (doubles) (Angstroms) (in order
  3317. // corresponding to above atomic numbers)
  3318. ArrayList y_coor = qmdata.y_coor;
  3319. ArrayList z_coor = qmdata.z_coor;
  3320. String geomXYZ = natoms + "\n";
  3321. geomXYZ += augInChI + "\n"; // use modified/augmented InChI as molecule name
  3322. for (int i = 0; i < natoms; i++) {
  3323. geomXYZ += getAtomicSymbol((Integer) atomicNumber.get(i)) + " "
  3324. + x_coor.get(i) + " " + y_coor.get(i) + " " + z_coor.get(i)
  3325. + "\n";
  3326. }
  3327. // write the string to file
  3328. try {
  3329. // File inputFilePath=new File(qmfolder,"symminput.txt");//SYMMETRY program directory
  3330. FileWriter fw = new FileWriter(inpFilePath);
  3331. fw.write(geomXYZ);
  3332. fw.close();
  3333. } catch (IOException e) {
  3334. String err = "Error writing XYZ file for backup MoleCoor connectivity perception";
  3335. err += e.toString();
  3336. Logger.critical(err);
  3337. System.exit(0);
  3338. }
  3339. // Step 2b. call the MoleCoor script to perceive connectivity and write MOL file (with connectivity)
  3340. try {
  3341. File runningdir = new File(directory);
  3342. String command2 = null;
  3343. if (System.getProperty("os.name").toLowerCase().contains("windows")) {// special windows case where paths
  3344. // can have spaces and are allowed to be surrounded by quotes
  3345. command2 = "python \"" + System.getenv("RMG")
  3346. + "/scripts/XYZtoMOLconn.py\" ";
  3347. // first argument: input file path
  3348. command2 = command2.concat("\"" + inpFilePath + "\" ");
  3349. // second argument: output path
  3350. command2 = command2.concat("\"" + molPath + "\" ");
  3351. // third argument: molecule name (the modified InChI)
  3352. command2 = command2.concat(augInChI + " ");
  3353. // fourth argument: tolerance
  3354. command2 = command2.concat("0.50 ");// InChI=1/C10H12O2/c1-2-6-9(5-1)11-12-10-7-3-4-8-10/h1,7H,2-4,6,8H2
  3355. // case has very strained with long O-O bond length; O-O bond 1.83873 Ang.; would need tol = 0.47873 or higher with
  3356. // MoleCoor scheme; 0.50 is used here
  3357. // fifth argument: PYTHONPATH
  3358. command2 = command2.concat("\"" + System.getenv("RMG")
  3359. + "/source/MoleCoor\"");// this will pass $RMG/source/MoleCoor to the script (in order to get
  3360. // the appropriate path for importing
  3361. } else {// non-Windows case
  3362. command2 = "python " + System.getenv("RMG")
  3363. + "/scripts/XYZtoMOLconn.py ";
  3364. // first argument: input file path
  3365. command2 = command2.concat(inpFilePath + " ");
  3366. // second argument: output path
  3367. command2 = command2.concat(molPath + " ");
  3368. // third argument: molecule name (the modified InChI)
  3369. command2 = command2.concat(augInChI + " ");
  3370. // fourth argument: tolerance
  3371. command2 = command2.concat("0.50 ");// InChI=1/C10H12O2/c1-2-6-9(5-1)11-12-10-7-3-4-8-10/h1,7H,2-4,6,8H2
  3372. // case has very strained with long O-O bond length; O-O bond 1.83873 Ang.; would need tol = 0.47873 or higher with
  3373. // MoleCoor scheme; 0.50 is used here
  3374. // fifth argument: PYTHONPATH
  3375. command2 = command2.concat(System.getenv("RMG")
  3376. + "/source/MoleCoor");// this will pass $RMG/source/MoleCoor to the script (in order to get the
  3377. // appropriate path for importing
  3378. }
  3379. Process molecoorProc = Runtime.getRuntime().exec(command2, null,
  3380. runningdir);
  3381. // read in output
  3382. InputStream is = molecoorProc.getInputStream();
  3383. InputStreamReader isr = new InputStreamReader(is);
  3384. BufferedReader br = new BufferedReader(isr);
  3385. String line = null;
  3386. while ((line = br.readLine()) != null) {
  3387. // do nothing
  3388. }
  3389. int exitValue = molecoorProc.waitFor();
  3390. molecoorProc.getErrorStream().close();
  3391. molecoorProc.getOutputStream().close();
  3392. br.close();
  3393. isr.close();
  3394. is.close();
  3395. } catch (Exception e) {
  3396. String err = "Error in running MoleCoor .XYZ to .MOL process \n";
  3397. err += e.toString();
  3398. Logger.logStackTrace(e);
  3399. System.exit(0);
  3400. }
  3401. // Step 3. convert the MOL file to InChI (with stereochem layers removed)
  3402. String[] result = Species.runInChIProcess(new File(molPath), new File(
  3403. System.getProperty("RMG.InChI_running_directory")
  3404. + "/species.txt"), true);
  3405. String InChI3D = stripStereochemLayersFromInChI(result[0]);
  3406. // Step 4. check whether there is a match (i.e. InChI equals InChI3D)
  3407. if (InChI3D.equals(InChI)) {
  3408. Logger.info("For species "
  3409. + name
  3410. + " a MoleCoor-based check suggests the optimized three-dimensional InChI ("
  3411. + InChI3D
  3412. + ") actually DOES match the intended (unmodified) InChI ("
  3413. + InChI
  3414. + "). Therefore, the result will be assumed to be successful.");
  3415. return true;
  3416. } else {
  3417. Logger.info("***For species "
  3418. + name
  3419. + " a MoleCoor-based check suggests the optimized three-dimensional InChI ("
  3420. + InChI3D
  3421. + ") does not match the intended (unmodified) InChI ("
  3422. + InChI + ").");
  3423. return false;
  3424. }
  3425. }
  3426. // check the connectivity in a MM4 result; returns true if the there appears to be a match, and returns false
  3427. // otherwise
  3428. // the connectivity is assumed to match if the InChI produced by processing the result/coordinates through OpenBabel
  3429. // produces in InChI that is equivalent or superstring to the InChI stored in memory
  3430. // we do not need/want to compare the augmented multN part of the InChI, so InChI will be converted within to the
  3431. // non-modified version
  3432. public boolean connectivityMatchInMM4ResultQ(String name, String directory,
  3433. String augInChI) {
  3434. // first, we convert the .mm4opt file into an xyz file that can be processed by OpenBabel; we use a python
  3435. // script that calls MoleCoor for this
  3436. // call the MoleCoor script to create the XYZ file from the .mm4opt file
  3437. try {
  3438. File runningdir = new File(directory);
  3439. // this will only be run on Linux so we don't have to worry about Linux vs. Windows issues
  3440. String command = "python " + System.getenv("RMG")
  3441. + "/scripts/XYZfromMM4.py ";
  3442. // first argument: input file path
  3443. command = command.concat(directory + "/" + name + ".mm4opt ");
  3444. // second argument: output path
  3445. String inpfilepath = directory + "/" + name + ".xyz";
  3446. command = command.concat(inpfilepath + " ");
  3447. // third argument: molecule name (the modified InChI)
  3448. command = command.concat(augInChI + " ");
  3449. // fourth argument: PYTHONPATH
  3450. command = command.concat(System.getenv("RMG") + "/source/MoleCoor");// this will pass $RMG/source/MoleCoor
  3451. // to the script (in order to get the appropriate path for importing
  3452. Process molecoorProc = Runtime.getRuntime().exec(command, null,
  3453. runningdir);
  3454. // read in output
  3455. InputStream is = molecoorProc.getInputStream();
  3456. InputStreamReader isr = new InputStreamReader(is);
  3457. BufferedReader br = new BufferedReader(isr);
  3458. String line = null;
  3459. while ((line = br.readLine()) != null) {
  3460. // do nothing
  3461. }
  3462. int exitValue = molecoorProc.waitFor();
  3463. molecoorProc.getErrorStream().close();
  3464. molecoorProc.getOutputStream().close();
  3465. br.close();
  3466. isr.close();
  3467. is.close();
  3468. } catch (Exception e) {
  3469. String err = "Error in running MoleCoor .MM4OPT to .XYZ process \n";
  3470. err += e.toString();
  3471. Logger.logStackTrace(e);
  3472. System.exit(0);
  3473. }
  3474. // check whether there is a match (i.e. InChI is a substring of InChI3D); note that this makes use of the
  3475. // connectivityMatchInPM3ResultQ function, which is sufficiently general to process XYZ files
  3476. return connectivityMatchInPM3ResultQ(name, directory, augInChI, ".xyz",
  3477. "xyz");
  3478. }
  3479. // ## operation initGAGroupLibrary()
  3480. protected void initGAGroupLibrary() {
  3481. // #[ operation initGAGroupLibrary()
  3482. thermoLibrary = ThermoGAGroupLibrary.getINSTANCE();
  3483. // #]
  3484. }
  3485. public QMData getMM4QMDataWithCClib(String name, String directory,
  3486. boolean getStericEnergy) {
  3487. String command = "python " + System.getProperty("RMG.workingDirectory")
  3488. + "/scripts/MM4ParsingScript.py ";
  3489. String logfilepath = directory + "/" + name + ".mm4out";
  3490. command = command.concat(logfilepath);
  3491. command = command.concat(" " + System.getenv("RMG") + "/source");// this will pass $RMG/source to the script (in
  3492. // order to get the appropriate path for importing
  3493. if (getStericEnergy)
  3494. command = command.concat(" 1");// option to print stericenergy before molar mass (this will only be used in
  3495. // useHindRot cases, but it is always read in with this function
  3496. return getQMDataWithCClib(name, directory, command, getStericEnergy);
  3497. }
  3498. public QMData getQMDataWithCClib(String name, String directory,
  3499. String command, boolean getStericEnergy) {
  3500. // parse the file using cclib
  3501. int natoms = 0; // number of atoms from Mopac file; in principle, this should agree with number of chemGraph
  3502. // atoms
  3503. ArrayList atomicNumber = new ArrayList(); // vector of atomic numbers (integers) (apparently Vector is
  3504. // thread-safe; cf. http://answers.yahoo.com/question/index?qid=20081214065127AArZDT3; ...should I be using this
  3505. // instead?)
  3506. ArrayList x_coor = new ArrayList(); // vectors of x-, y-, and z-coordinates (doubles) (Angstroms) (in order
  3507. // corresponding to above atomic numbers)
  3508. ArrayList y_coor = new ArrayList();
  3509. ArrayList z_coor = new ArrayList();
  3510. double energy = 0; // energy (Hf298) in Hartree
  3511. double molmass = 0; // molecular mass in amu
  3512. double stericEnergy = 99999;// steric energy in Hartree
  3513. ArrayList freqs = new ArrayList(); // list of frequencies in units of cm^-1
  3514. double rotCons_1 = 0;// rotational constants in (1/s)
  3515. double rotCons_2 = 0;
  3516. double rotCons_3 = 0;
  3517. try {
  3518. Process cclibProc = Runtime.getRuntime().exec(command);
  3519. // read the stdout of the process, which should contain the desired information in a particular format
  3520. InputStream is = cclibProc.getInputStream();
  3521. InputStreamReader isr = new InputStreamReader(is);
  3522. BufferedReader br = new BufferedReader(isr);
  3523. String line = null;
  3524. // example output:
  3525. // C:\Python25>python.exe GaussianPM3ParsingScript.py TEOS.out
  3526. // 33
  3527. // [ 6 6 8 14 8 6 6 8 6 6 8 6 6 1 1 1 1 1 1 1 1 1 1 1 1
  3528. // 1 1 1 1 1 1 1 1]
  3529. // [[ 2.049061 -0.210375 3.133106]
  3530. // [ 1.654646 0.321749 1.762752]
  3531. // [ 0.359284 -0.110429 1.471465]
  3532. // [-0.201871 -0.013365 -0.12819 ]
  3533. // [ 0.086307 1.504918 -0.82893 ]
  3534. // [-0.559186 2.619928 -0.284003]
  3535. // [-0.180246 3.839463 -1.113029]
  3536. // [ 0.523347 -1.188305 -1.112765]
  3537. // [ 1.857584 -1.018167 -1.495088]
  3538. // [ 2.375559 -2.344392 -2.033403]
  3539. // [-1.870397 -0.297297 -0.075427]
  3540. // [-2.313824 -1.571765 0.300245]
  3541. // [-3.83427 -1.535927 0.372171]
  3542. // [ 1.360346 0.128852 3.917699]
  3543. // [ 2.053945 -1.307678 3.160474]
  3544. // [ 3.055397 0.133647 3.403037]
  3545. // [ 1.677262 1.430072 1.750899]
  3546. // [ 2.372265 -0.029237 0.985204]
  3547. // [-0.245956 2.754188 0.771433]
  3548. // [-1.656897 2.472855 -0.287156]
  3549. // [-0.664186 4.739148 -0.712606]
  3550. // [-0.489413 3.734366 -2.161038]
  3551. // [ 0.903055 4.016867 -1.112198]
  3552. // [ 1.919521 -0.229395 -2.269681]
  3553. // [ 2.474031 -0.680069 -0.629949]
  3554. // [ 2.344478 -3.136247 -1.273862]
  3555. // [ 1.786854 -2.695974 -2.890647]
  3556. // [ 3.41648 -2.242409 -2.365094]
  3557. // [-1.884889 -1.858617 1.28054 ]
  3558. // [-1.976206 -2.322432 -0.440995]
  3559. // [-4.284706 -1.26469 -0.591463]
  3560. // [-4.225999 -2.520759 0.656131]
  3561. // [-4.193468 -0.809557 1.112677]]
  3562. // -14.1664924726
  3563. // [ 9.9615 18.102 27.0569 31.8459 39.0096 55.0091
  3564. // 66.4992 80.4552 86.4912 123.3551 141.6058 155.5448
  3565. // 159.4747 167.0013 178.5676 207.3738 237.3201 255.3487
  3566. // 264.5649 292.867 309.4248 344.6503 434.8231 470.2074
  3567. // 488.9717 749.1722 834.257 834.6594 837.7292 839.6352
  3568. // 887.9767 892.9538 899.5374 992.1851 1020.6164 1020.8671
  3569. // 1028.3897 1046.7945 1049.1768 1059.4704 1065.1505 1107.4001
  3570. // 1108.1567 1109.0466 1112.6677 1122.7785 1124.4315 1128.4163
  3571. // 1153.3438 1167.6705 1170.9627 1174.9613 1232.1826 1331.8459
  3572. // 1335.3932 1335.8677 1343.9556 1371.37 1372.8127 1375.5428
  3573. // 1396.0344 1402.4082 1402.7554 1403.2463 1403.396 1411.6946
  3574. // 1412.2456 1412.3519 1414.5982 1415.3613 1415.5698 1415.7993
  3575. // 1418.5409 2870.7446 2905.3132 2907.0361 2914.1662 2949.2646
  3576. // 2965.825 2967.7667 2971.5223 3086.3849 3086.3878 3086.6448
  3577. // 3086.687 3089.2274 3089.4105 3089.4743 3089.5841 3186.0753
  3578. // 3186.1375 3186.3511 3186.365 ]
  3579. // [ 0.52729 0.49992 0.42466]
  3580. // note: above example has since been updated to print molecular mass and steric energy; also frequency and atomic
  3581. // number format has been updated
  3582. String[] stringArray;
  3583. natoms = Integer.parseInt(br.readLine());// read line 1: number of atoms
  3584. stringArray = br.readLine().replace("[", "").replace("]", "")
  3585. .trim().split(",\\s+");// read line 2: the atomic numbers (first removing braces)
  3586. // line = br.readLine().replace("[", "").replace("]","");//read line 2: the atomic numbers (first removing
  3587. // braces)
  3588. // StringTokenizer st = new StringTokenizer(line); //apprently the stringTokenizer class is deprecated, but
  3589. // I am having trouble getting the regular expressions to work properly
  3590. for (int i = 0; i < natoms; i++) {
  3591. // atomicNumber.add(i,Integer.parseInt(stringArray[i]));
  3592. atomicNumber.add(i, Integer.parseInt(stringArray[i]));
  3593. }
  3594. for (int i = 0; i < natoms; i++) {
  3595. stringArray = br.readLine().replace("[", "").replace("]", "")
  3596. .trim().split("\\s+");// read line 3+: coordinates for atom i; used /s+ for split; using spaces
  3597. // with default limit of 0 was giving empty string
  3598. x_coor.add(i, Double.parseDouble(stringArray[0]));
  3599. y_coor.add(i, Double.parseDouble(stringArray[1]));
  3600. z_coor.add(i, Double.parseDouble(stringArray[2]));
  3601. }
  3602. energy = Double.parseDouble(br.readLine());// read next line: energy
  3603. if (getStericEnergy)
  3604. stericEnergy = Double.parseDouble(br.readLine());// read next line: steric energy (in Hartree)
  3605. molmass = Double.parseDouble(br.readLine());// read next line: molecular mass (in amu)
  3606. if (natoms > 1) {// read additional info for non-monoatomic species
  3607. stringArray = br.readLine().replace("[", "").replace("]", "")
  3608. .trim().split(",\\s+");// read next line: frequencies
  3609. for (int i = 0; i < stringArray.length; i++) {
  3610. freqs.add(i, Double.parseDouble(stringArray[i]));
  3611. }
  3612. stringArray = br.readLine().replace("[", "").replace("]", "")
  3613. .trim().split("\\s+");// read next line rotational constants (converting from GHz to Hz in the
  3614. // process)
  3615. rotCons_1 = Double.parseDouble(stringArray[0]) * 1000000000;
  3616. rotCons_2 = Double.parseDouble(stringArray[1]) * 1000000000;
  3617. rotCons_3 = Double.parseDouble(stringArray[2]) * 1000000000;
  3618. }
  3619. while ((line = br.readLine()) != null) {
  3620. // do nothing (there shouldn't be any more information, but this is included to get all the output)
  3621. }
  3622. int exitValue = cclibProc.waitFor();
  3623. cclibProc.getErrorStream().close();
  3624. cclibProc.getOutputStream().close();
  3625. br.close();
  3626. isr.close();
  3627. is.close();
  3628. } catch (Exception e) {
  3629. Logger.logStackTrace(e);
  3630. String err = "Error in running ccLib Python process \n";
  3631. err += e.toString();
  3632. Logger.critical(err);
  3633. System.exit(0);
  3634. }
  3635. // package up the result
  3636. return new QMData(natoms, atomicNumber, x_coor, y_coor, z_coor, energy,
  3637. stericEnergy, molmass, freqs, rotCons_1, rotCons_2, rotCons_3);
  3638. }
  3639. // converts atomicnumber to atomic symbol
  3640. // returns Err if the atomic number is not recognized; currently, H, C, O, N, S, and Si are recognized
  3641. public String getAtomicSymbol(Integer atomicNumber) {
  3642. if (atomicNumber == 1)
  3643. return "H";
  3644. else if (atomicNumber == 6)
  3645. return "C";
  3646. else if (atomicNumber == 8)
  3647. return "O";
  3648. else if (atomicNumber == 7)
  3649. return "N";
  3650. else if (atomicNumber == 16)
  3651. return "S";
  3652. else if (atomicNumber == 14)
  3653. return "Si";
  3654. else
  3655. return "Err";//
  3656. }
  3657. }
  3658. /*********************************************************************
  3659. * File Path : RMG\RMG\jing\chem\QMTP.java
  3660. *********************************************************************/