├── .gitignore ├── LICENSE.txt ├── README.md ├── analysis └── placeholder ├── checkpoints └── placeholder ├── gaussian └── run_gaussian.sh ├── img ├── reaction.png ├── velocity_verlet_position.svg └── velocity_verlet_velocity.svg ├── pom.xml ├── src ├── main │ └── java │ │ └── edu │ │ └── harvard │ │ └── chemistry │ │ └── ekwan │ │ └── Jprogdyn │ │ ├── AsciiBar.java │ │ ├── Atom.java │ │ ├── CalculationMethod.java │ │ ├── Element.java │ │ ├── FileFormat.java │ │ ├── GaussianCalculationMethod.java │ │ ├── GaussianInputFile.java │ │ ├── GaussianJob.java │ │ ├── GaussianOutputFile.java │ │ ├── GaussianResult.java │ │ ├── HarmonicOscillatorDistribution.java │ │ ├── Immutable.java │ │ ├── Initializer.java │ │ ├── InputFileFormat.java │ │ ├── InternalCoordinate.java │ │ ├── Loader.java │ │ ├── Molecule.java │ │ ├── NMRTrajectoryAnalyzer.java │ │ ├── NormalMode.java │ │ ├── OutputFileFormat.java │ │ ├── Propagator.java │ │ ├── Propagators.java │ │ ├── RotationalBoltzmann.java │ │ ├── Singleton.java │ │ ├── Trajectory.java │ │ ├── TrajectoryAnalyzer.java │ │ ├── TrajectoryExecutorService.java │ │ ├── TrajectoryPoint.java │ │ ├── Units.java │ │ └── package-info.java └── test │ └── java │ └── edu │ └── harvard │ └── chemistry │ └── ekwan │ └── Jprogdyn │ ├── HarmonicTestGaussian.java │ └── InitializerTest.java ├── test_files ├── dinitro_Cl_F-1H2O-ts-b3lyp_d3bj-631+gd-dmf_pcm.out ├── methane_b3lyp_midix.out └── methane_b3lyp_midix_NMR_b3lyp_dz.out └── tutorials ├── methane_NMR_analysis.config ├── methane_NMR_trajectories.config ├── reaction_tutorial_analysis.config └── reaction_tutorial_trajectories.config /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | .mvn/wrapper/maven-wrapper.jar 11 | *.class 12 | *.swp 13 | gaussian/* 14 | checkpoints/* 15 | analysis/* 16 | *.sh 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /analysis/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekwan/Jprogdyn/b366f9972cd8ec42a1b02175f75a248a749e15a6/analysis/placeholder -------------------------------------------------------------------------------- /checkpoints/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekwan/Jprogdyn/b366f9972cd8ec42a1b02175f75a248a749e15a6/checkpoints/placeholder -------------------------------------------------------------------------------- /gaussian/run_gaussian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script runs a Gaussian job. 4 | # 5 | # Usage: run_gaussian.sh /scratch/dae23_gaussian_0000020002 6 | # 7 | # This script assumes the job will be called gaussian.gjf and the output should go into gaussian.out. 8 | # You can modify this script to suit your system's requirements. 9 | 10 | # read command line arguments 11 | jobDirectory=$1 12 | 13 | # ensure directory is empty 14 | cd $jobDirectory 15 | #export GAUSS_SCRDIR=$jobDirectory 16 | 17 | # run Gaussian 18 | g16 gaussian.gjf gaussian.out 19 | 20 | # the job directory will be cleaned up by Jprogdyn, so we don't need to do anything for that 21 | -------------------------------------------------------------------------------- /img/reaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekwan/Jprogdyn/b366f9972cd8ec42a1b02175f75a248a749e15a6/img/reaction.png -------------------------------------------------------------------------------- /img/velocity_verlet_position.svg: -------------------------------------------------------------------------------- 1 | 2 | {\displaystyle {\vec {x}}(t+\Delta t)={\vec {x}}(t)+{\vec {v}}(t)\,\Delta t+{\frac {1}{2}}\,{\vec {a}}(t)\Delta t^{2},} 3 | 18 | 67 | -------------------------------------------------------------------------------- /img/velocity_verlet_velocity.svg: -------------------------------------------------------------------------------- 1 | 2 | {\displaystyle {\vec {v}}(t+\Delta t)={\vec {v}}(t)+{\frac {{\vec {a}}(t)+{\vec {a}}(t+\Delta t)}{2}}\Delta t.} 3 | 16 | 62 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | edu.harvard.chemistry.ekwan 6 | Jprogdyn 7 | jar 8 | 9 | 1.0-SNAPSHOT 10 | Jprogdyn 11 | http://www.github.com/ekwan/Jprogdyn 12 | 13 | 14 | edu.harvard.chemistry.ekwan.Jprogdyn.Loader 15 | UTF-8 16 | 1.8 17 | 1.8 18 | 1.0 19 | 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | 3.8.1 27 | test 28 | 29 | 30 | 31 | com.google.guava 32 | guava 33 | 23.0 34 | 35 | 36 | 37 | org.apache.commons 38 | commons-math3 39 | 3.6 40 | 41 | 42 | 43 | commons-io 44 | commons-io 45 | 2.6 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-javadoc-plugin 55 | 3.0.0 56 | 57 | -Xdoclint:all -Xdoclint:+missing 58 | -Xdoclint:all -Xdoclint:+missing 59 | false 60 | 61 | 62 | 63 | default 64 | 65 | javadoc 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.5.1 81 | 82 | 1.8 83 | 1.8 84 | 85 | -Xlint:all 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/AsciiBar.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * This class lets takes a value and a range and draws a bar like this: 7 | * 8 | * [------*-----] 9 | * 10 | */ 11 | public class AsciiBar implements Immutable, Singleton { 12 | 13 | /** Not instantiable. */ 14 | private AsciiBar() { 15 | throw new IllegalArgumentException("not instantiable"); 16 | } 17 | 18 | /** 19 | * Prints a bar with a marker indicating the position of the value. 20 | * The returned string will have length+4 characters to accomodate the edges of the bar. 21 | * @param value where the marker is 22 | * @param min the value of the left end of the bar 23 | * @param max the value of the right end of the bar 24 | * @param marker what the marker should be (one character) 25 | * @param notMarker what to put on the bar in places other than the marker 26 | * @param length the length of the bar not including the two characters that indicate the edges of the bar 27 | * @return the bar as a string 28 | */ 29 | public static String make(double value, double min, double max, String marker, String notMarker, int length) { 30 | // check invariants 31 | if ( max <= min ) 32 | throw new IllegalArgumentException("check bounds, max is less than min"); 33 | if ( marker == null || marker.length() != 1 || notMarker == null || notMarker.length() != 1 ) 34 | throw new NullPointerException("one character markers required"); 35 | if ( length < 5 ) 36 | throw new IllegalArgumentException("bar too short"); 37 | 38 | // return special bars if the bounds are exceeded 39 | if ( value < min ) 40 | { 41 | StringBuilder s = new StringBuilder(marker + "["); 42 | for (int i=0; i < length; i++) 43 | s.append(notMarker); 44 | s.append("] "); 45 | return s.toString(); 46 | } 47 | else if ( value > max ) 48 | { 49 | StringBuilder s = new StringBuilder(" ["); 50 | for (int i=0; i < length; i++) 51 | s.append(notMarker); 52 | s.append("]" + marker); 53 | return s.toString(); 54 | } 55 | 56 | // figure out where to put the marker 57 | double temp = (value-min)*(length-1) / (max-min); 58 | int markerPosition = (int)temp; 59 | 60 | // build the string 61 | StringBuilder s = new StringBuilder(" ["); 62 | for (int i=0; i < markerPosition; i++) 63 | s.append(notMarker); 64 | s.append(marker); 65 | for (int i=markerPosition+1; i < length; i++) 66 | s.append(notMarker); 67 | s.append("] "); 68 | return s.toString(); 69 | } 70 | 71 | /** 72 | * Prints a bar with standard parameters. 73 | * @param value the value of the quantity 74 | * @param min the minimum value of the quantity (the left edge) 75 | * @param max the maximum value of the quantity (the right edge) 76 | * @return the ASCII bar as a String 77 | */ 78 | public static String make(double value, double min, double max) { 79 | return make(value, min, max, "*", " ", 20); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Atom.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.*; 4 | import java.util.*; 5 | import java.io.*; 6 | 7 | /** 8 | * This class represents an atom. It is immutable. 9 | */ 10 | public class Atom implements Immutable, Serializable { 11 | /** For serialization. */ 12 | public static final long serialVersionUID = 1L; 13 | 14 | /** The atomic symbol. */ 15 | public final String symbol; 16 | 17 | /** The atomic mass in amu. */ 18 | public final double mass; 19 | 20 | /** The position of the atom. */ 21 | public final Vector3D position; 22 | 23 | /** 24 | * Constructs an atom. 25 | * @param symbol the atomic symbol 26 | * @param mass the atomic mass 27 | * @param x the x position of the atom in angstroms 28 | * @param y the y position of the atom in angstroms 29 | * @param z the z position of the atom in angstroms 30 | */ 31 | public Atom(String symbol, double mass, double x, double y, double z) { 32 | this(symbol, mass, new Vector3D(x,y,z)); 33 | } 34 | 35 | /** 36 | * Constructs an atom. 37 | * @param symbol the atomic symbol 38 | * @param mass the atomic mass 39 | * @param position the position of the atom 40 | */ 41 | public Atom(String symbol, double mass, Vector3D position) { 42 | if ( symbol == null ) 43 | throw new NullPointerException("null symbol"); 44 | else if ( symbol.length() == 0 ) 45 | throw new IllegalArgumentException("zero length symbol"); 46 | if ( mass < 0.0 ) 47 | throw new IllegalArgumentException("negative mass"); 48 | this.symbol = symbol; 49 | this.mass = mass; 50 | if ( position == null ) 51 | throw new NullPointerException("null position"); 52 | this.position = position; 53 | } 54 | 55 | /** 56 | * Returns a copy of this atom 57 | * @param newPosition the new position for the atom 58 | * @return a copy of the atom 59 | */ 60 | public Atom shift(Vector3D newPosition) { 61 | return new Atom(symbol, mass, newPosition); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return String.format("%-5s %12.7f %15.10f %15.10f %15.10f", symbol, mass, position.getX(), position.getY(), position.getZ()); 67 | } 68 | 69 | /** 70 | * Prints a MOLDEN geometry string. 71 | * @return the description 72 | */ 73 | public String toTrajString() { 74 | return String.format("%s %.7f %.7f %.7f\n", symbol, position.getX(), position.getY(), position.getZ()); 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | return Objects.hash(symbol, mass, position); 80 | } 81 | 82 | @Override 83 | public boolean equals(Object obj) { 84 | if ( obj == null ) 85 | return false; 86 | if ( obj == this ) 87 | return true; 88 | if ( !(obj instanceof Atom) ) 89 | return false; 90 | 91 | Atom a = (Atom)obj; 92 | if ( symbol.equals(a.symbol) && 93 | mass == a.mass && 94 | position.equals(a.position) ) 95 | return true; 96 | return false; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/CalculationMethod.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * This is an abstract class that represents the information needed to perform an 7 | * electronic structure calculation. It contains the program, the level of theory, 8 | * and how much memory/how many processors to use. 9 | */ 10 | public abstract class CalculationMethod implements Immutable, Serializable { 11 | 12 | /** For serialization. */ 13 | public static final long serialVersionUID = 1L; 14 | 15 | /** Represents the kind of calculation that will be performed. */ 16 | public enum CalculationType 17 | { 18 | /** For trajectory points. */ 19 | ENERGY_AND_FORCE, 20 | 21 | /** For NMR calculations. */ 22 | NMR; 23 | } 24 | 25 | /** Represents an electronic structure program. */ 26 | public enum Program 27 | { 28 | /** Use Gaussian. */ 29 | GAUSSIAN; 30 | } 31 | 32 | /** Represents the kind of calculation that will be performed. */ 33 | public final CalculationType calculationType; 34 | 35 | /** Which program to use. */ 36 | public final Program program; 37 | 38 | /** Memory to use in GB. */ 39 | public final int memory; 40 | 41 | /** Number of rocessors to use. */ 42 | public final int processors; 43 | 44 | /** 45 | * Constructs a CalculationMethod. 46 | * @param calculationType the kind of calculation to run 47 | * @param program the electornic structure program to use 48 | * @param memory the amount of memory to use in GB 49 | * @param processors the number of processors to use 50 | */ 51 | public CalculationMethod(CalculationType calculationType, Program program, 52 | int memory, int processors) 53 | { 54 | this.calculationType = calculationType; 55 | this.program = program; 56 | if ( memory < 1 ) 57 | throw new IllegalArgumentException("must use at least 1 GB"); 58 | this.memory = memory; 59 | if ( processors < 1 ) 60 | throw new IllegalArgumentException("must use at least one processor"); 61 | this.processors = processors; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Element.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import com.google.common.collect.*; 5 | 6 | /** 7 | * This enum represents an element. 8 | */ 9 | public enum Element 10 | { 11 | /** Hydrogen. */ 12 | HYDROGEN("H", 1), 13 | 14 | /** Carbon. */ 15 | CARBON ("C", 6), 16 | 17 | /** Nitrogen. */ 18 | NITROGEN("N", 7), 19 | 20 | /** Oxygen. */ 21 | OXYGEN ("O", 8), 22 | 23 | /** Sulfur. */ 24 | SULFUR ("S", 16); 25 | 26 | /** The atomic symbol. */ 27 | public final String symbol; 28 | 29 | /** The atomic number. */ 30 | public final int atomicNumber; 31 | 32 | /** 33 | * Constructs an element. 34 | * @param symbol the atomic symbol 35 | * @param atomicNumber the atomic number 36 | */ 37 | Element(String symbol, int atomicNumber) 38 | { 39 | this.symbol = symbol; 40 | this.atomicNumber = atomicNumber; 41 | } 42 | 43 | /** 44 | * Identifies the element corresponding to a string. 45 | * @param symbol the symbol for the requested element (case-sensitive) 46 | * @return the corresponding Element enum element 47 | */ 48 | public static Element getElement(String symbol) 49 | { 50 | for (Element e : Element.values()) { 51 | if ( e.symbol.equals(symbol) || Integer.toString(e.atomicNumber).equals(symbol) ) 52 | return e; 53 | } 54 | throw new IllegalArgumentException(String.format("element not found for symbol %s", symbol)); 55 | } 56 | 57 | /** 58 | * Returns a string representation of this Element. 59 | * @return the String 60 | */ 61 | public String toString() 62 | { 63 | return symbol; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/FileFormat.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | /** 4 | * This is a marker interface that groups file format classes together. 5 | */ 6 | public interface FileFormat { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/GaussianCalculationMethod.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * This class represents a Gaussian calculation. 7 | * Note that NMR calculations often require forces. 8 | */ 9 | public class GaussianCalculationMethod extends CalculationMethod { 10 | 11 | /** For serialization. */ 12 | public static final long serialVersionUID = 1L; 13 | 14 | /** The route card. Can contain multiple lines. A newline will automatically be appended. */ 15 | public final String routeCard; 16 | 17 | /** Will be appended at the end of the input file. A newline will automatically be appended. */ 18 | public final String footer; 19 | 20 | /** 21 | * Constructor. 22 | * @param calculationType whether this is an NMR or force calculation 23 | * @param memory memory to use in GB 24 | * @param processors number of processors to use 25 | * @param routeCard the route card 26 | * @param footer any text to append after the geometry 27 | */ 28 | public GaussianCalculationMethod(CalculationMethod.CalculationType calculationType, int memory, 29 | int processors, String routeCard, String footer) { 30 | super(calculationType, CalculationMethod.Program.GAUSSIAN, memory, processors); 31 | if ( routeCard == null || routeCard.trim().length() == 0 ) 32 | throw new IllegalArgumentException("blank route card"); 33 | this.routeCard = routeCard; 34 | this.footer = footer; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | String returnString = String.format("G09 calculation (%s, %d GB, %d processors)\n", calculationType.toString(), memory, processors); 40 | returnString += String.format("Route card: %s\nFooter:%s\n", routeCard, footer); 41 | return returnString; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(calculationType, program, memory, processors, routeCard, footer); 47 | } 48 | 49 | @Override 50 | public boolean equals(Object obj) { 51 | if ( obj == null ) 52 | return false; 53 | if ( obj == this ) 54 | return true; 55 | if ( !(obj instanceof GaussianCalculationMethod) ) 56 | return false; 57 | 58 | GaussianCalculationMethod m = (GaussianCalculationMethod)obj; 59 | if ( m.calculationType == calculationType && 60 | m.program == program && 61 | m.memory == memory && 62 | m.processors == processors && 63 | Objects.equals(m.routeCard, routeCard) && 64 | Objects.equals(m.footer, footer) ) 65 | return true; 66 | return false; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/GaussianInputFile.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | import org.apache.commons.math3.geometry.euclidean.threed.*; 6 | 7 | /** 8 | * This class represent a Gaussian input file. 9 | */ 10 | public class GaussianInputFile extends InputFileFormat { 11 | 12 | /** For serialization. */ 13 | public static final long serialVersionUID = 1L; 14 | 15 | /** 16 | * Creates a GJF file. Terminate the headers with two newlines. 17 | * @param molecule the template molecule where atom symbols, charge, and multiplicity will be read from (mandatory) 18 | * @param positions the geometry to use (if set to null, uses the molecule's original geometry) 19 | * @param method the header and footer information 20 | */ 21 | public GaussianInputFile(Molecule molecule, List positions, GaussianCalculationMethod method) { 22 | super(getGaussianString(molecule, positions, method)); 23 | } 24 | 25 | /** 26 | * This helper method creates the actual text of the input file given the requested calculation method. 27 | * @param molecule a template molecule 28 | * @param positions the positions to write out 29 | * @param method the header and footer information 30 | */ 31 | private static String getGaussianString(Molecule molecule, List positions, GaussianCalculationMethod method) { 32 | // check invariants 33 | if ( molecule == null ) 34 | throw new NullPointerException("null molecule"); 35 | if ( positions != null && positions.size() != molecule.contents.size() ) 36 | throw new IllegalArgumentException("size of positions does not match number of atoms in molecule"); 37 | if ( ! method.routeCard.contains("#p") ) 38 | throw new IllegalArgumentException("you should set the print level to #p in the route card"); 39 | 40 | // add header 41 | StringBuilder s = new StringBuilder(); 42 | s.append("%chk=Jprogdyn.chk\n"); 43 | if ( method.processors > 1 ) 44 | s.append(String.format("%%nprocshared=%d\n", method.processors)); 45 | s.append(String.format("%%mem=%dGB\n", method.memory)); 46 | s.append(method.routeCard + "\n\nauto-generated by Jprogdyn\n\n"); 47 | s.append(String.format("%d %d\n", molecule.charge, molecule.multiplicity)); 48 | 49 | // add geometry 50 | List xyz = null; 51 | if ( positions == null ) { 52 | xyz = new ArrayList<>(molecule.contents.size()); 53 | for (Atom a : molecule.contents) 54 | xyz.add(a.position); 55 | } 56 | else 57 | xyz = positions; 58 | for (int i=0; i < molecule.contents.size(); i++) { 59 | String symbol = molecule.contents.get(i).symbol; 60 | double x = xyz.get(i).getX(); 61 | double y = xyz.get(i).getY(); 62 | double z = xyz.get(i).getZ(); 63 | s.append(String.format(" %-5s %15.10f %15.10f %15.10f\n", symbol, x, y, z)); 64 | } 65 | s.append("\n"); 66 | 67 | // add the footer 68 | if ( method.footer.trim().length() > 0 ) 69 | s.append(method.footer + "\n"); 70 | s.append("\n\n"); 71 | 72 | // return the result 73 | return s.toString(); 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return stringRepresentation; 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return Objects.hash(stringRepresentation); 84 | } 85 | 86 | @Override 87 | public boolean equals(Object obj) { 88 | if ( obj == null ) 89 | return false; 90 | if ( obj == this ) 91 | return true; 92 | if ( !(obj instanceof GaussianInputFile) ) 93 | return false; 94 | 95 | GaussianInputFile f = (GaussianInputFile)obj; 96 | if ( Objects.equals(f.stringRepresentation, stringRepresentation) ) 97 | return true; 98 | return false; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/GaussianJob.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.io.*; 5 | import java.util.concurrent.atomic.*; 6 | import org.apache.commons.io.FileUtils; 7 | import java.util.concurrent.*; 8 | import com.google.common.collect.*; 9 | 10 | /** 11 | * This class represents a Gaussian job. The Gaussian executable must be in the path for this to work. 12 | */ 13 | public class GaussianJob implements Callable, Serializable { 14 | 15 | /** For serialization. */ 16 | public static final long serialVersionUID = 1L; 17 | 18 | /** Creates temporary filename indices to run the jobs with. */ 19 | public static final AtomicInteger INDEX_GENERATOR = new AtomicInteger(); 20 | 21 | /** The input file to run. */ 22 | public final GaussianInputFile gjf; 23 | 24 | /** The maximum number of filenames in one Gaussian directory. */ 25 | public static final int MAX_FILENAMES = Loader.getInteger("gaussian_max_filenames"); 26 | 27 | /** Where to run jobs. */ 28 | public static final String GAUSSIAN_DIRECTORY = String.format("%s/%s", Loader.getString("working_directory"), Loader.getString("gaussian_directory")); 29 | 30 | /** 31 | * Constructor. 32 | * @param gjf the name of the Gaussian input file to run 33 | */ 34 | public GaussianJob(GaussianInputFile gjf) { 35 | this.gjf = gjf; 36 | } 37 | 38 | /** 39 | * Auto-selects a filename and runs the analysis calculation. 40 | * @return the result of the calculation 41 | */ 42 | public GaussianResult call() { 43 | // choose a base filename for this set of jobs 44 | String baseFilename = ""; 45 | int index = 0; 46 | 47 | // expand environment variable if necessary 48 | 49 | counting: 50 | for (int i=0; i < MAX_FILENAMES; i++) { 51 | // get a new ID number for this job 52 | index = INDEX_GENERATOR.getAndIncrement(); 53 | baseFilename = String.format("%s_gaussian_%010d", Loader.HOSTNAME, index); 54 | 55 | // don't allow this choice of filenames if any files with this prefix already exist 56 | for ( File f : new File(GAUSSIAN_DIRECTORY).listFiles() ) { 57 | if ( f.getName().startsWith(baseFilename) ) { 58 | baseFilename = ""; 59 | continue counting; 60 | } 61 | } 62 | 63 | // reset counter if necessary 64 | if ( INDEX_GENERATOR.get() > MAX_FILENAMES ) 65 | INDEX_GENERATOR.getAndSet(0); 66 | break; 67 | } 68 | if ( baseFilename.length() == 0 ) 69 | throw new IllegalArgumentException("Unable to set filename!"); 70 | 71 | // write input files to disk 72 | String jobDirectoryName = String.format("%s/%s", GAUSSIAN_DIRECTORY, baseFilename); 73 | File jobDirectory = new File(jobDirectoryName); 74 | boolean success = jobDirectory.mkdir(); 75 | if ( !success ) 76 | throw new IllegalArgumentException("failed to create directory " + jobDirectoryName); 77 | String gjfFilename = String.format("%s/gaussian.gjf", jobDirectoryName); 78 | gjf.write(gjfFilename); 79 | 80 | // call Gaussian 81 | double elapsedTime = 0.0; 82 | try { 83 | long startTime = System.currentTimeMillis(); 84 | 85 | String runString = String.format("%s/run_gaussian.sh %s %s", GAUSSIAN_DIRECTORY, jobDirectoryName, baseFilename); 86 | //System.out.println(runString); 87 | ProcessBuilder processBuilder = new ProcessBuilder(runString.split(" ")); 88 | //processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT); 89 | //processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); 90 | Process process = processBuilder.start(); 91 | //Process process = Runtime.getRuntime().exec(runString); 92 | int exitValue = process.waitFor(); 93 | long endTime = System.currentTimeMillis(); 94 | elapsedTime = (endTime - startTime) / 1000.0; 95 | } 96 | catch (Exception e) { 97 | System.out.println("Error while running Gaussian job:"); 98 | e.printStackTrace(); 99 | } 100 | 101 | // retrieve output 102 | String outputFilename = String.format("%s/%s/gaussian.out", GAUSSIAN_DIRECTORY, baseFilename); 103 | GaussianResult result = new GaussianResult(outputFilename, elapsedTime); 104 | 105 | // remove files 106 | try { 107 | FileUtils.deleteDirectory(jobDirectory); 108 | } 109 | catch (Exception e) { 110 | System.out.println("Error while trying to delete directory: " + jobDirectory.getName()); 111 | e.printStackTrace(); 112 | } 113 | 114 | // return result 115 | return result; 116 | } 117 | 118 | @Override 119 | public int hashCode() { 120 | return Objects.hash(gjf); 121 | } 122 | 123 | @Override 124 | public boolean equals(Object obj) { 125 | if ( obj == null ) 126 | return false; 127 | if ( obj == this ) 128 | return true; 129 | if ( !(obj instanceof GaussianJob) ) 130 | return false; 131 | 132 | GaussianJob j = (GaussianJob)obj; 133 | if ( gjf.equals(j.gjf) ) 134 | return true; 135 | return false; 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | return "GaussianJob"; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/GaussianResult.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.io.*; 5 | 6 | /** 7 | * This class represents the output of a Gaussian job. 8 | */ 9 | public class GaussianResult { 10 | 11 | /** The result of the calculation. */ 12 | public final GaussianOutputFile out; 13 | 14 | /** How long the calculation took in wallclock time (seconds). */ 15 | public final double elapsedTime; 16 | 17 | /** 18 | * Reads the result of the job from the specified filename. 19 | * @param filename the filename of the Gaussian output file to read from 20 | * @param elapsedTime how long the calculation took in wallclock time (seconds) 21 | */ 22 | public GaussianResult(String filename, double elapsedTime) { 23 | if ( ! new File(filename).exists() ) 24 | throw new IllegalArgumentException(String.format("Filename %s not found!", filename)); 25 | OutputFileFormat temp = new OutputFileFormat(filename) {}; 26 | List> fileContents = temp.fileContents; 27 | if ( fileContents.size() == 0 ) 28 | throw new IllegalArgumentException("g09 output file is empty"); 29 | 30 | String debugString = ""; 31 | for (int i = Math.max(0, temp.fileContents.size()-5); i < temp.fileContents.size(); i++) { 32 | List line = temp.fileContents.get(i); 33 | for (String s : line) 34 | debugString += s + " "; 35 | debugString += "\n"; 36 | } 37 | debugString = debugString.substring(0, debugString.length()-1); 38 | 39 | List lastLine = fileContents.get(fileContents.size()-1); 40 | if ( lastLine.size() < 2 ) 41 | throw new IllegalArgumentException("Gaussian output last line truncated, end of file follows:\n" + debugString); 42 | if ( ! (lastLine.get(0).equals("Normal") && (lastLine.get(1).equals("termination")) ) ) 43 | throw new IllegalArgumentException("Gaussian job did not terminate normally, end of file follows:\n" + debugString); 44 | out = new GaussianOutputFile(filename); 45 | this.elapsedTime = elapsedTime; 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(out); 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if ( obj == null ) 56 | return false; 57 | if ( obj == this ) 58 | return true; 59 | if ( !(obj instanceof GaussianResult) ) 60 | return false; 61 | 62 | GaussianResult r = (GaussianResult)obj; 63 | if ( out.equals(r.out) ) 64 | return true; 65 | return false; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "GaussianResult"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/HarmonicOscillatorDistribution.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import com.google.common.collect.*; 5 | import org.apache.commons.math3.analysis.polynomials.*; 6 | import java.util.concurrent.*; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * This class draws random numbers from the eigenfunctions of the harmonic oscillator. 11 | * This class has no state. 12 | */ 13 | public class HarmonicOscillatorDistribution { 14 | 15 | /** Energy levels higher than this will be evaluated classically. Attempting to go too high here will blow out the numerical precision of double. */ 16 | public static final int MAX_LEVEL = 20; 17 | 18 | /** Maximum number of times to try drawing a quantum displacement. */ 19 | public static final int MAX_QUANTUM_ATTEMPTS = 10000; 20 | 21 | /** Prevents two Hermite polynomials from being generated at the same time. */ 22 | private static final Object HERMITE_LOCK = new Object(); 23 | 24 | /** 25 | * Draws a random displacement from the quantum distribution for the specified oscillator. The answer 26 | * must be within the classically allowed region. Rejection sampling is used. This class is thread-safe. 27 | * @param n the vibrational energy level (0, 1, ...) 28 | * @param mass the reduced mass in amu 29 | * @param wavenumber the frequency of this mode in cm^-1 30 | * @param forceConstant the force constant in mDyne/A 31 | * @return the random displacement in angstroms 32 | */ 33 | public static double drawRandomQuantumDisplacement(int n, double mass, double wavenumber, double forceConstant) 34 | { 35 | //System.out.printf("n=%d mass=%.1f freq=%.1f force_constant=%.1f\n", n, mass, wavenumber, forceConstant); 36 | if ( wavenumber < 0.0 ) 37 | throw new IllegalArgumentException("negative frequencies not allowed"); 38 | 39 | // compute the classical turning point 40 | double ZPE = Initializer.getZeroPointEnergy(wavenumber); // kcal/mol 41 | double energy = ZPE * (2.0 * n + 1.0); // kcal/mol 42 | double turningPoint = getClassicalTurningPoint(energy, forceConstant); 43 | 44 | // if this is a high energy level, draw a classical displacement 45 | if ( n > MAX_LEVEL ) 46 | return drawRandomClassicalDisplacement(energy, forceConstant); 47 | 48 | // the are the minimum and maximum y-values of the distribution 49 | double minValue = 0.0; 50 | double maxValue = getQuantumDistributionMaxValue(n, mass, wavenumber, turningPoint); 51 | 52 | int attempts = 0; 53 | while (attempts < MAX_QUANTUM_ATTEMPTS ) 54 | { 55 | // draw a random x-value between -turningPoint and +turningPoint 56 | // use MAX_A so we don't go right to the edge 57 | double x = getDouble(-turningPoint, turningPoint); 58 | 59 | // evaluate the function value 60 | double functionValue = getQuantumDistributionValue(n, mass, wavenumber, x); 61 | 62 | // draw a random number between minValue and maxValue 63 | double y = getDouble(minValue, maxValue); 64 | 65 | // accept or reject 66 | if ( y <= functionValue ) 67 | return x; 68 | 69 | // keep track of how many times we've tried 70 | attempts++; 71 | } 72 | throw new IllegalArgumentException("classical rejection sampling failed"); 73 | } 74 | 75 | /** 76 | * Finds the maximum value of the specified quantum harmonic oscillator distribution. 77 | * @param n the vibrational energy level (0, 1, ...) 78 | * @param mass the reduced mass in amu 79 | * @param wavenumber the frequency of this mode in cm^-1 80 | * @param maxShift the maximum positive displacement in angstroms 81 | * @return the maximum y-value 82 | */ 83 | public static double getQuantumDistributionMaxValue(int n, double mass, double wavenumber, double maxShift) 84 | { 85 | if ( wavenumber < 0.0 ) 86 | throw new IllegalArgumentException("negative frequencies not allowed"); 87 | if ( n == 0 ) 88 | return getQuantumDistributionValue(0, mass, wavenumber, 0); 89 | double stepSize = maxShift / (MAX_LEVEL * 100); 90 | double maxValue = 0.0; 91 | for (double x = -1.0 * maxShift; x <= 0; x += stepSize) 92 | { 93 | double value = getQuantumDistributionValue(n, mass, wavenumber, x); 94 | if ( value > maxValue ) 95 | maxValue = value; 96 | } 97 | return maxValue; 98 | } 99 | 100 | /** 101 | * Computes the value of |psi_n|^2, where psi is an eigenstate of the quantum harmonic oscillator. 102 | * @param n the vibrational energy level (0, 1, ...) Should not exceed {@link #MAX_LEVEL}. 103 | * If it does, use {@link #drawRandomClassicalDisplacement(double,double)} 104 | * @param mass the reduced mass in amu 105 | * @param wavenumber the frequency of this mode in cm^-1 106 | * @param x the displacement in angstroms 107 | * @return the value of the probability distribution in angstroms^-1 108 | */ 109 | public static double getQuantumDistributionValue(int n, double mass, double wavenumber, double x) 110 | { 111 | if ( wavenumber < 0.0 ) 112 | throw new IllegalArgumentException("frequency cannot be negative"); 113 | if ( n < 0 ) 114 | throw new IllegalArgumentException("n cannot be negative"); 115 | if ( n > MAX_LEVEL ) 116 | throw new IllegalArgumentException("exceeded maximum excitation level"); 117 | 118 | // calculate 1 / (2^n n!) 119 | double term1 = 1.0 / ( Math.pow(2.0,n) * factorial(n) ); 120 | 121 | // calculate sqrt(m*omega/pi*hbar) 122 | // where omega = 2 * pi * c * wavenumber 123 | // 124 | // m * 2 * pi * c * wavenumber 4 * pi * m * c * wavenumber 125 | // ----------------------------- = ----------------------------- 126 | // pi * h / 2 * pi h 127 | // 128 | // dimensional analysis (this is the only term that has units): 129 | // we need the sqrt to work out to angstroms^-2 130 | // 131 | // mass g kg mol C cm wavenumber s m^2 132 | // 4pi * -------- * ------- * ------------------ * ------ * ------------ * ------------- * ----------- 133 | // mol 1000 g AVOGADROS_NUMBER s cm H m^2 * kg 1E20 A^2 134 | // 135 | // 4 * pi * mass * C * wavenumber 136 | // = ------------------------------------ 137 | // 1000 * AVOGADROS_NUMBER * H * 1E20 138 | // 139 | // note that we also need this quantity in the term3 argument, just times pi 140 | double temp = 4.0 * Math.PI * mass * Units.C * wavenumber / (Units.AVOGADROS_NUMBER * Units.H * 1E23); 141 | double term2 = Math.sqrt(temp); 142 | 143 | // exp[(-m omega / 2hbar) * x^2)] 144 | double term3 = Math.exp(-1.0 * temp * Math.PI * x * x); 145 | 146 | // Hermite polynomial of degree n, where x = sqrt(m * omega / hbar) x 147 | // note that the Hermite generator is not thread safe 148 | PolynomialFunction function = null; 149 | synchronized (HERMITE_LOCK) 150 | { 151 | function = PolynomialsUtils.createHermitePolynomial(n); 152 | } 153 | double argument = Math.sqrt(temp * Math.PI) * x; 154 | double term4 = function.value(argument); 155 | term4 = term4 * term4; 156 | return term1 * term2 * term3 * term4; 157 | } 158 | 159 | /** 160 | * Helper method that returns factorials. 161 | * @param n the number to return the factorial of 162 | * @return n! 163 | */ 164 | public static double factorial(int n) 165 | { 166 | if ( n < 0 ) 167 | throw new IllegalArgumentException("n must not be negative"); 168 | 169 | double factorial = 1.0; 170 | for (int i=1; i <= n; i++) 171 | factorial = factorial * i; 172 | return factorial; 173 | } 174 | 175 | /** Maximum number of times to try drawing a classical displacement. */ 176 | public static final int MAX_CLASSICAL_ATTEMPTS = 10000; 177 | 178 | /** 179 | * Draws a random displacement from a uniform distribution between classical turning points. 180 | * It is recommended that you do not use the program if you cannot understand this function. 181 | * 182 | * @param energy the total energy in the mode in kcal/mol 183 | * @param forceConstant the force constant of the mode in mDyne/A 184 | * @return a random x-value in angstroms, which should be uniformly distributed. 185 | */ 186 | public static double drawRandomUniformDisplacement(double energy, double forceConstant) 187 | { 188 | double A = getClassicalTurningPoint(energy, forceConstant); 189 | return getDouble(-A, A); 190 | } 191 | 192 | /** 193 | * Draws a random displacement from the classical distribution for the specified oscillator. The answer 194 | * must be within the classically allowed region. Rejection sampling is used. This class is thread-safe. 195 | * 196 | * @param energy the total energy in the mode in kcal/mol 197 | * @param forceConstant the force constant of the mode in mDyne/A 198 | * @return a random x-value in angstroms, which should be concentrated near the classical turning points 199 | */ 200 | public static double drawRandomClassicalDisplacement(double energy, double forceConstant) 201 | { 202 | // compute the classical turning point 203 | double A = getClassicalTurningPoint(energy, forceConstant); 204 | double minA = -1.0 * MAX_A * A; 205 | double maxA = MAX_A * A; 206 | 207 | // compute the minimum value of the distribution, which occurs at x=0 208 | double minValue = getClassicalDistributionValue(A, 0.0); 209 | 210 | // compute the maximum value of the distribution, using A*MAX_A so we don't go right to the edge 211 | double maxValue = getClassicalDistributionValue(A, maxA); 212 | 213 | int attempts = 0; 214 | while (attempts < MAX_CLASSICAL_ATTEMPTS ) 215 | { 216 | // draw a random x-value between -A and +A 217 | // use MAX_A so we don't go right to the edge 218 | double x = getDouble(minA, maxA); 219 | 220 | // evaluate the function value 221 | double functionValue = getClassicalDistributionValue(A, x); 222 | 223 | // draw a random number between minValue and maxValue 224 | double y = getDouble(minValue, maxValue); 225 | 226 | // accept or reject 227 | if ( y <= functionValue ) 228 | return x; 229 | 230 | // keep track of how many times we've tried 231 | attempts++; 232 | } 233 | throw new IllegalArgumentException("classical rejection sampling failed"); 234 | } 235 | 236 | /** Prevents getClassicalDistributionValue(double,double,double) from blowing up if x is set to A. */ 237 | public static final double MAX_A = 0.999; 238 | 239 | /** 240 | * Computes the classical probability distribution function for the quantum harmonic oscillator. 241 | * The classical distribution is (Robinett, R.W. Am. J. Phys. 1995, 63(9), 823): 242 | * 243 | * 1 1 244 | * ---- * ----------------- , where A is the classical turning point. Note this diverges as x approaches A. 245 | * pi sqrt(A^2 - x^2) 246 | * 247 | * @param A the classical turning point in angstroms 248 | * @param x the x-coordinate in angstroms to evaluate the value of the probability distribution 249 | * @return the value of the probability distribution 250 | */ 251 | public static double getClassicalDistributionValue(double A, double x) 252 | { 253 | // throw an exception if outside classically allowed region 254 | double x1 = x; 255 | if ( x1 > A || x1 < -1.0 * A ) 256 | throw new IllegalArgumentException("outside classically allowed region"); 257 | 258 | // if we are close to the edge, return something close to the edge to prevent the function value from blowing up 259 | if ( x1 > MAX_A * A ) 260 | x1 = MAX_A*A; 261 | else if ( x1 < -1.0 * MAX_A * A ) 262 | x1 = -1.0 * MAX_A * A; 263 | 264 | return (1.0 / Math.PI) * ( 1.0 / Math.sqrt( A*A - x1*x1 ) ); 265 | } 266 | 267 | /** 268 | * Computes the classical turning point in a harmonic oscillator. 269 | * 270 | * total energy = 1 / ( 2.0 * forceConstant * turningPoint^2) 271 | * turningPoint = sqrt(2E/k) 272 | * 273 | * dimensional analysis (1 dyne = 1E-5 N) 274 | * 275 | * energy kcal A mdyne N s^2 m KCAL_PER_MOL_TO_J J mol kg m^2 1E20 A^2 276 | * -------- = ------ * ------- * -------- * ------- * -------- * ------------------------- * -------- * ---------- 277 | * k mol mDyne 1E-8 N kg m 1E10 A kcal s^2 J m^2 278 | * 279 | * = KCAL_PER_MOL_TO_J * 1E18 280 | * 281 | * @param energy the total energy in the mode in kcal/mol 282 | * @param forceConstant the force constant of the mode in mDyne/A 283 | * @return the classical turning point (in the +x direction, angstroms) 284 | */ 285 | public static double getClassicalTurningPoint(double energy, double forceConstant) 286 | { 287 | return Math.sqrt( (2.0 * energy / forceConstant) * (Units.KCAL_PER_MOL_TO_J * 1E18) ); 288 | } 289 | 290 | public static double getDouble(double min, double max) 291 | { 292 | if ( min > max ) 293 | throw new IllegalArgumentException("min greater than max" + min + "," + max); 294 | return min + (max-min) * ThreadLocalRandom.current().nextDouble(); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Immutable.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | /** 4 | * This is a marker interface for immutable or effectively immutable classes. 5 | */ 6 | public interface Immutable { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/InputFileFormat.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | 6 | /** 7 | * This abstract class represents files that are used as inputs to other programs. 8 | * This class is immutable. 9 | */ 10 | public abstract class InputFileFormat implements FileFormat, Immutable, Serializable { 11 | 12 | /** For serialization. */ 13 | public static final long serialVersionUID = 1L; 14 | 15 | /** The string that will be written to a file */ 16 | public final String stringRepresentation; 17 | 18 | /** 19 | * Constructor. 20 | * @param stringRepresentation the text of the file 21 | */ 22 | public InputFileFormat(String stringRepresentation) { 23 | this.stringRepresentation = stringRepresentation; 24 | } 25 | 26 | /** 27 | * Writes the input file to disk. 28 | * @param filename the destination file 29 | */ 30 | public void write(String filename) { 31 | writeStringToDisk(stringRepresentation, filename); 32 | } 33 | 34 | /** 35 | * Convenience method that writes a string to a file. 36 | * @param string the string to write 37 | * @param filename the filename to write to 38 | */ 39 | public static void writeStringToDisk(String string, String filename) { 40 | try (PrintWriter outputFile = new PrintWriter(filename)) { 41 | outputFile.print(string); 42 | } 43 | catch (IOException e) { 44 | // abort program if there's a problem 45 | System.out.println("Error writing to " + filename + "!"); 46 | e.printStackTrace(); 47 | } 48 | } 49 | 50 | /** 51 | * Convenience method that appends a string to a file. 52 | * @param string the text to append 53 | * @param filename the file to append to 54 | */ 55 | public static void appendStringToDisk(String string, String filename) { 56 | try { 57 | File file = new File(filename); 58 | if ( ! file.exists() ) 59 | file.createNewFile(); 60 | FileWriter fileWriter = new FileWriter(filename,true); 61 | BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); 62 | bufferedWriter.write(string); 63 | bufferedWriter.close(); 64 | } 65 | catch (IOException e) { 66 | System.out.println("Error appending to " + filename + "!"); 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return stringRepresentation; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/InternalCoordinate.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import com.google.common.collect.*; 4 | import java.util.*; 5 | import java.io.*; 6 | import org.apache.commons.math3.geometry.euclidean.threed.*; 7 | 8 | /** 9 | * This abstract class represents some feature of molecular geometry. 10 | */ 11 | public abstract class InternalCoordinate implements Immutable, Serializable { 12 | 13 | /** For serialization. */ 14 | public static final long serialVersionUID = 1L; 15 | 16 | /** Atom indices. */ 17 | public final List indices; 18 | 19 | /** Description of this coordinate. */ 20 | public final String description; 21 | 22 | /** 23 | * Generic constructor. 24 | * @param indices the atom indices 25 | * @param description description of this coordinate 26 | */ 27 | public InternalCoordinate(List indices, String description) 28 | { 29 | if ( indices == null || indices.size() < 2 ) 30 | throw new NullPointerException("must specify at least two atom indices"); 31 | for (Integer i : indices) 32 | if ( i < 0 ) 33 | throw new IllegalArgumentException("negative index " + i); 34 | if ( ImmutableSet.copyOf(indices).size() != indices.size() ) 35 | throw new IllegalArgumentException("duplicate atom numbers: " + indices.toString()); 36 | this.indices = indices; 37 | this.description = description; 38 | } 39 | 40 | /** 41 | * Returns the numerical value of this coordinate given some atom positions. 42 | * @param positions the positions to use 43 | * @return the value of this internal coordinate 44 | */ 45 | public abstract double getValue(List positions); 46 | 47 | @Override 48 | public String toString() 49 | { 50 | if ( description != null && description.trim().length() > 0 ) 51 | return description; 52 | String returnString = ""; 53 | for (Integer i : indices) 54 | returnString += String.format("%d-", i); 55 | return returnString.substring(0, returnString.length()-1); 56 | } 57 | 58 | @Override 59 | public int hashCode() 60 | { 61 | return Objects.hash(indices); 62 | } 63 | 64 | @Override 65 | public boolean equals(Object obj) 66 | { 67 | if ( obj == null ) 68 | return false; 69 | if ( obj == this ) 70 | return true; 71 | if ( !(obj instanceof InternalCoordinate) ) 72 | return false; 73 | 74 | InternalCoordinate c = (InternalCoordinate)obj; 75 | if ( Objects.equals(indices, c.indices) ) 76 | return true; 77 | return false; 78 | } 79 | 80 | /** 81 | * The condition will return true when the internal coordinate meets this requirement. 82 | */ 83 | public enum ConditionType { 84 | 85 | /** True if the coordinate is greater than the given condition. */ 86 | GREATER_THAN(">"), 87 | 88 | /** True if the coordinate is equal to the given condition. */ 89 | EQUALS("="), 90 | 91 | /** True if the coordinate is less than or equal to the given condition. */ 92 | LESS_THAN("<="); 93 | 94 | /** Description of this condition type.*/ 95 | public final String description; 96 | 97 | /** 98 | * Constructor. 99 | * @param description description of this condition type 100 | */ 101 | ConditionType(String description) { 102 | this.description = description; 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return description; 108 | } 109 | } 110 | 111 | /** 112 | * Represents a stopping condition for a trajectory. 113 | */ 114 | public static class Condition implements Serializable, Immutable { 115 | /** For serialization. */ 116 | public static final long serialVersionUID = 1L; 117 | 118 | /** Values within this tolerance of the critical value will be considered equal. */ 119 | public static final double EQUALS_TOLERANCE = 0.20; 120 | 121 | /** The internal coordinate to check. */ 122 | public final InternalCoordinate internalCoordinate; 123 | 124 | /** The type of condition to check. */ 125 | public final ConditionType conditionType; 126 | 127 | /** The critical value. */ 128 | public final double criticalValue; 129 | 130 | /** 131 | * Create a stopping condition for a trajectory. 132 | * @param internalCoordinate the internal coordinate to check 133 | * @param conditionType the type of condition to check 134 | * @param criticalValue the critical value 135 | */ 136 | public Condition(InternalCoordinate internalCoordinate, ConditionType conditionType, double criticalValue) { 137 | if ( internalCoordinate == null ) 138 | throw new NullPointerException("must specify a coordinate"); 139 | this.internalCoordinate = internalCoordinate; 140 | 141 | if ( conditionType == null ) 142 | throw new NullPointerException("must specificy a condition type"); 143 | this.conditionType = conditionType; 144 | 145 | this.criticalValue = criticalValue; 146 | } 147 | 148 | /** 149 | * Check if the stopping condition has been reached. 150 | * @param positions the given atomic positions 151 | * @return true if the stopping conditions has been reached 152 | */ 153 | public boolean reached(List positions) { 154 | double value = internalCoordinate.getValue(positions); 155 | if ( conditionType == ConditionType.GREATER_THAN ) 156 | return value > criticalValue; 157 | else if ( conditionType == ConditionType.LESS_THAN ) 158 | return value < criticalValue; 159 | else if ( conditionType == ConditionType.EQUALS ) 160 | return Math.abs( value - criticalValue ) < EQUALS_TOLERANCE; 161 | else 162 | throw new IllegalArgumentException("unsupported condition type"); 163 | } 164 | 165 | @Override 166 | public String toString() { 167 | return String.format("%s %s %.3f", internalCoordinate.toString(), conditionType.toString(), criticalValue); 168 | } 169 | 170 | @Override 171 | public int hashCode() { 172 | return Objects.hash(internalCoordinate, conditionType, criticalValue); 173 | } 174 | 175 | @Override 176 | public boolean equals(Object obj) { 177 | if ( obj == null ) 178 | return false; 179 | if ( obj == this ) 180 | return true; 181 | if ( !(obj instanceof Condition) ) 182 | return false; 183 | 184 | Condition c = (Condition)obj; 185 | if ( Objects.equals(internalCoordinate, c.internalCoordinate) && 186 | Objects.equals(conditionType, c.conditionType) && 187 | criticalValue == c.criticalValue ) 188 | return true; 189 | return false; 190 | } 191 | 192 | } 193 | 194 | /** 195 | * Represents a bond length. 196 | */ 197 | public static class Length extends InternalCoordinate 198 | { 199 | /** For serialization. */ 200 | public static final long serialVersionUID = 1L; 201 | 202 | /** 203 | * Creates a bond length. 204 | * @param atom1 the index of the first atom (0-indexed) 205 | * @param atom2 the index of the second atom (0-indexed) 206 | * @param description the description of this bond length 207 | */ 208 | public Length(int atom1, int atom2, String description) { 209 | super(ImmutableList.of(atom1, atom2), description); 210 | } 211 | 212 | /** 213 | * Compute the bond length. 214 | * @param positions the atomic coordinates 215 | * @return the bond length in Angstroms 216 | */ 217 | public double getValue(List positions) { 218 | Vector3D v1 = positions.get(indices.get(0)); 219 | Vector3D v2 = positions.get(indices.get(1)); 220 | return Vector3D.distance(v1, v2); 221 | } 222 | } 223 | 224 | /** 225 | * Represents a bond angle. 226 | */ 227 | public static class Angle extends InternalCoordinate { 228 | 229 | /** For serialization. */ 230 | public static final long serialVersionUID = 1L; 231 | 232 | /** 233 | * Creates a bond angle. 234 | * @param atom1 the index of the first atom (0-indexed) 235 | * @param atom2 the index of the second atom (0-indexed) 236 | * @param atom3 the index of the third atom (0-indexed) 237 | * @param description the description of this bond angle 238 | */ 239 | public Angle(int atom1, int atom2, int atom3, String description) { 240 | super(ImmutableList.of(atom1, atom2, atom3), description); 241 | } 242 | 243 | /** 244 | * Compute the bond angle. 245 | * @param positions the atomic coordinates 246 | * @return the bond angle in degrees 247 | */ 248 | public double getValue(List positions) { 249 | Vector3D v1 = positions.get(indices.get(0)); 250 | Vector3D v2 = positions.get(indices.get(1)); 251 | Vector3D v3 = positions.get(indices.get(2)); 252 | Vector3D v1prime = v1.subtract(v2); 253 | Vector3D v3prime = v3.subtract(v2); 254 | return Math.toDegrees(Vector3D.angle(v1prime, v3prime)); 255 | } 256 | } 257 | 258 | /** 259 | * Represents a bond torsion. 260 | */ 261 | public static class Torsion extends InternalCoordinate { 262 | 263 | /** For serialization. */ 264 | public static final long serialVersionUID = 1L; 265 | 266 | /** 267 | * Creates a bond torsion. 268 | * @param atom1 the index of the first atom (0-indexed) 269 | * @param atom2 the index of the second atom (0-indexed) 270 | * @param atom3 the index of the third atom (0-indexed) 271 | * @param atom4 the index of the fourth atom (0-indexed) 272 | * @param description the description of this bond torsion 273 | */ 274 | public Torsion(int atom1, int atom2, int atom3, int atom4, String description) { 275 | super(ImmutableList.of(atom1, atom2, atom3, atom4), description); 276 | } 277 | 278 | /** 279 | * Compute the bond torsion. 280 | * @param positions the atomic coordinates 281 | * @return the dihedral angle in degrees 282 | */ 283 | public double getValue(List positions) { 284 | Vector3D v1 = positions.get(indices.get(0)); 285 | Vector3D v2 = positions.get(indices.get(1)); 286 | Vector3D v3 = positions.get(indices.get(2)); 287 | Vector3D v4 = positions.get(indices.get(3)); 288 | 289 | Vector3D b1 = v2.add(-1.0, v1); 290 | Vector3D b2 = v3.add(-1.0, v2); 291 | Vector3D b3 = v4.add(-1.0, v3); 292 | 293 | // make sure the vectors are not collinear 294 | if (Vector3D.angle(b1,b2) == 0 || Vector3D.angle(b2,b3) == 0) { 295 | System.out.println("Warning! Collinear dihedral angle!"); 296 | return 0.0; 297 | } 298 | 299 | // compute dihedral angle 300 | double term1 = Vector3D.dotProduct(b1.scalarMultiply( b2.getNorm() ), Vector3D.crossProduct(b2, b3) ); 301 | double term2 = Vector3D.dotProduct(Vector3D.crossProduct(b1, b2), Vector3D.crossProduct(b2, b3) ); 302 | return Math.toDegrees( Math.atan2(term1, term2) ); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Molecule.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.io.*; 5 | import org.apache.commons.math3.geometry.euclidean.threed.*; 6 | import com.google.common.collect.*; 7 | 8 | /** 9 | * This immutable class represents a molecule. 10 | */ 11 | public class Molecule implements Immutable, Serializable { 12 | 13 | /** For serialization. */ 14 | public static final long serialVersionUID = 1L; 15 | 16 | /** Geometry. */ 17 | public final List contents; 18 | 19 | /** Normal modes. */ 20 | public final List modes; 21 | 22 | /** Forces in hartree/bohr. */ 23 | public final List forces; 24 | 25 | /** Description of this molecule. */ 26 | public final String name; 27 | 28 | /** Overall electrostatic charge. */ 29 | public final int charge; 30 | 31 | /** Overall spin multiplicity. */ 32 | public final int multiplicity; 33 | 34 | /** Potential energy in hartree. */ 35 | public final double potentialEnergy; 36 | 37 | /** NMR absolute shieldings in ppm. */ 38 | public final List shieldings; 39 | 40 | /** 41 | * Constructs a molecule. 42 | * @param contents the atoms that this molecule will contain 43 | * @param modes the normal modes of the molecule 44 | * @param forces the Cartesian forces on each atom 45 | * @param name the description for the molecule 46 | * @param charge the overall electrostatic charge 47 | * @param multiplicity the overall spin multiplicity 48 | * @param potentialEnergy the potential energy in hartree 49 | * @param shieldings the absolute chemical shieldings in ppm 50 | */ 51 | public Molecule(List contents, List modes, List forces, 52 | String name, int charge, int multiplicity, 53 | double potentialEnergy, List shieldings) { 54 | // check invariants 55 | if ( contents == null || contents.size() == 0 ) 56 | throw new NullPointerException("null or zero length contents"); 57 | this.contents = ImmutableList.copyOf(contents); 58 | 59 | if ( modes == null ) 60 | throw new NullPointerException("null modes"); 61 | this.modes = ImmutableList.copyOf(modes); 62 | 63 | if ( forces == null ) 64 | throw new NullPointerException("forces"); 65 | this.forces = ImmutableList.copyOf(forces); 66 | 67 | if ( name == null ) 68 | throw new NullPointerException("null name"); 69 | this.name = name; 70 | this.charge = charge; 71 | this.multiplicity = multiplicity; 72 | this.potentialEnergy = potentialEnergy; 73 | 74 | if ( shieldings == null ) 75 | throw new IllegalArgumentException("zero size shieldings"); 76 | this.shieldings = ImmutableList.copyOf(shieldings); 77 | } 78 | 79 | /** 80 | * Counts the number of imaginary frequencies. 81 | * @return the number of imaginary frequencies 82 | */ 83 | public int numberOfImaginaryFrequencies() { 84 | int imag = 0; 85 | for (NormalMode n : modes) 86 | if ( n.frequency < 0 ) 87 | imag++; 88 | return imag; 89 | } 90 | 91 | /** 92 | * Get the positions of the atoms. 93 | * @return the atomic positions 94 | */ 95 | public List getPositions() { 96 | List positions = new ArrayList<>(contents.size()); 97 | for (Atom a : contents) 98 | positions.add(a.position); 99 | return positions; 100 | } 101 | 102 | /** 103 | * Returns a new molecule with the specified positions. 104 | * @param newPositions the new positions 105 | * @return copy of this molecule with the new positions 106 | */ 107 | public Molecule setPositions(List newPositions) { 108 | int numberOfAtoms = contents.size(); 109 | if ( newPositions.size() != numberOfAtoms ) 110 | throw new IllegalArgumentException("size of new positions array does not match number of atoms"); 111 | List newContents = new ArrayList<>(numberOfAtoms); 112 | for (int i=0; i < numberOfAtoms; i++) { 113 | Atom oldAtom = contents.get(i); 114 | Vector3D newPosition = newPositions.get(i); 115 | Atom newAtom = oldAtom.shift(newPosition); 116 | newContents.add(newAtom); 117 | } 118 | return new Molecule(newContents, modes, forces, name, charge, multiplicity, 0.0, new ArrayList()); 119 | } 120 | 121 | 122 | /** 123 | * Counts the number of heavy atoms. 124 | * @return the number of heavy atoms 125 | */ 126 | public int heavyAtoms() { 127 | int count = 0; 128 | for (Atom a : contents) 129 | if (! a.symbol.equals("H")) 130 | count++; 131 | return count; 132 | } 133 | 134 | /** 135 | * Writes the geometry in Gaussian format. 136 | * @return the geometry 137 | */ 138 | public String geometryString() { 139 | String returnString = charge + " " + multiplicity + "\n"; 140 | for (int i=0; i < contents.size(); i++) 141 | returnString += contents.get(i).toString() + "\n"; 142 | return returnString; 143 | } 144 | 145 | /** 146 | * Writes the geometry in MOLDEN format. 147 | * @return the geometry 148 | */ 149 | public String trajGeometryString() { 150 | String returnString = ""; 151 | for (int i=0; i < contents.size(); i++) 152 | returnString += contents.get(i).toTrajString(); 153 | return returnString; 154 | } 155 | 156 | /** 157 | * Returns a human-readable description of the normal modes. 158 | * @return the normal modes 159 | */ 160 | public String modesString() { 161 | String returnString = String.format("Normal Modes (%d total):\n\n", modes.size()); 162 | for (int i=0; i < modes.size(); i++) { 163 | returnString += String.format("Mode %d:\n\n", i); 164 | returnString += modes.get(i).toString() + "\n-------------\n"; 165 | } 166 | return returnString; 167 | } 168 | 169 | @Override 170 | public String toString() { 171 | String returnString = String.format("Charge = %d Multiplicity = %d\n\n", charge, multiplicity); 172 | returnString += "Symbol Weight (amu) X (A) Y (A) Z (A)\n"; 173 | for (Atom a : contents) 174 | returnString += a.toString() + "\n"; 175 | returnString += String.format("%d normal modes read.", modes.size()); 176 | return returnString; 177 | } 178 | 179 | @Override 180 | public int hashCode() { 181 | return Objects.hash(contents, modes, name, charge, multiplicity, potentialEnergy, shieldings); 182 | } 183 | 184 | @Override 185 | public boolean equals(Object obj) { 186 | if ( obj == null ) 187 | return false; 188 | if ( obj == this ) 189 | return true; 190 | if ( !(obj instanceof Molecule) ) 191 | return false; 192 | 193 | Molecule m = (Molecule)obj; 194 | if ( contents.equals(m.contents) && 195 | modes.equals(m.modes) && 196 | name.equals(m.name) && 197 | charge == m.charge && multiplicity == m.multiplicity && 198 | potentialEnergy == m.potentialEnergy && 199 | shieldings.equals(m.shieldings) ) 200 | return true; 201 | return false; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/NMRTrajectoryAnalyzer.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.io.*; 5 | import com.google.common.collect.*; 6 | import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; 7 | 8 | /** 9 | * This class collects together methods for analyzing NMR trajectories. 10 | */ 11 | public class NMRTrajectoryAnalyzer implements Immutable, Singleton { 12 | /** Not instantiable. */ 13 | private NMRTrajectoryAnalyzer() { 14 | throw new IllegalArgumentException("not instantiable"); 15 | } 16 | 17 | /** Represents the raw corrections obtained by averaging over a number of trajectories. */ 18 | public static class NMRtrajectoryAnalysis implements Immutable { 19 | /** Parallel list containing the element of the nucleus. */ 20 | public final List elements; 21 | 22 | /** Parallel list containing the atom numbers over which each correction has been averaged. 1, 2, ..., n. */ 23 | public final List> atomNumbers; 24 | 25 | /** Parallel list containing the mean raw corrections in ppm. */ 26 | public final List means; 27 | 28 | /** Parallel list containing the standard deviations in ppm. */ 29 | public final List standardDeviations; 30 | 31 | /** Parallel list containing the standard errors in ppm. */ 32 | public final List standardErrors; 33 | 34 | /** 35 | * Make an analysis object. 36 | * @param elements the kinds of atoms being analyzed (parallel list) 37 | * @param atomNumbers list of 1-indexed atom numbers (parallel list) 38 | * @param means the mean raw corrections in ppm (parallel list) 39 | * @param standardDeviations the standard deviations in the mean raw corrections corrections (parallel list) 40 | * @param standardErrors the standard errors of the mean raw corrections (parallel list) 41 | */ 42 | public NMRtrajectoryAnalysis(List elements, List> atomNumbers, 43 | List means, List standardDeviations, List standardErrors) { 44 | if ( ImmutableSet.of(elements.size(), atomNumbers.size(), means.size(), standardDeviations.size()).size() != 1 ) 45 | throw new IllegalArgumentException("size mismatch"); 46 | if ( elements == null || atomNumbers == null || means == null || standardDeviations == null ) 47 | throw new NullPointerException("no nulls allowed"); 48 | for (List list : atomNumbers) 49 | if ( list == null || list.size() == 0 ) 50 | throw new NullPointerException("no empties allowed"); 51 | this.elements = ImmutableList.copyOf(elements); 52 | List> tempList = new ArrayList<>(atomNumbers.size()); 53 | for (List list : atomNumbers) 54 | { 55 | List sorted = new ArrayList(list); 56 | Collections.sort(sorted); 57 | tempList.add(ImmutableList.copyOf(sorted)); 58 | } 59 | this.atomNumbers = ImmutableList.copyOf(tempList); 60 | this.means = ImmutableList.copyOf(means); 61 | this.standardDeviations = ImmutableList.copyOf(standardDeviations); 62 | this.standardErrors = ImmutableList.copyOf(standardErrors); 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | String returnString = "Element: Atom Numbers: Raw Correction: Std. Err.: Std. Dev.:\n"; 68 | List strings = new ArrayList<>(elements.size()); 69 | for (int i=0; i < elements.size(); i++) 70 | { 71 | Element element = elements.get(i); 72 | List thisAtomNumbers = atomNumbers.get(i); 73 | Double mean = means.get(i); 74 | Double standardDeviation = standardDeviations.get(i); 75 | Double standardError = standardErrors.get(i); 76 | 77 | String atomString = ""; 78 | for (Integer j : thisAtomNumbers) 79 | atomString += String.format("%3d ", j); 80 | returnString += String.format(" %2s %-30s %7.2f %5.3f %7.2f\n", 81 | element.toString(), atomString, mean, standardError, standardDeviation); 82 | } 83 | Collections.sort(strings); 84 | for (String s : strings) 85 | returnString += s; 86 | return returnString; 87 | } 88 | } 89 | 90 | /** 91 | * Analyzes the given trajectories. 92 | * @param trajectories the trajectories to analyze 93 | * @param ignoreIncomplete whether to include incomplete trajectories in the analysis 94 | * @param symmetryList a list of lists containing the symmetry-equivalent atom _numbers_ (e.g. [ [1,2,3], [4,5] would mean average atoms 1,2,3 and 4,5) 95 | * @param molecule the molecule that contains the NMR shifts calculated at the stationary point geometry (needed to calculate raw corrections) 96 | * @return the analysis 97 | */ 98 | public static NMRtrajectoryAnalysis analyze(List trajectories, boolean ignoreIncomplete, List> symmetryList, Molecule molecule) 99 | { 100 | // initialize lists 101 | List elements = new ArrayList<>(); 102 | int size = molecule.contents.size(); 103 | List> atomNumbers = new ArrayList<>(); 104 | List included = new ArrayList<>(); 105 | for (Set set : symmetryList) 106 | { 107 | if ( set.size() == 0 ) 108 | throw new IllegalArgumentException("unexpected empty"); 109 | 110 | // put down the atom numbers in each list into a master list so we can make singly-occupied lists for all the other atom numbers 111 | List list = new ArrayList<>(set); 112 | atomNumbers.add(list); 113 | included.addAll(set); 114 | 115 | // check we are averaging nuclei of the same element 116 | Set theseElements = new HashSet<>(); 117 | for (Integer i : list) 118 | { 119 | Atom a = molecule.contents.get(i-1); 120 | theseElements.add(Element.getElement(a.symbol)); 121 | } 122 | if ( theseElements.size() != 1 ) 123 | throw new IllegalArgumentException("check symmetry list: " + set.toString()); 124 | 125 | // ok, so make a note of the element 126 | int atomIndex = list.get(0)-1; 127 | Atom a = molecule.contents.get(atomIndex); 128 | elements.add(Element.getElement(a.symbol)); 129 | } 130 | for (int i=1; i <= size; i++) 131 | { 132 | if ( included.contains(i) ) 133 | continue; 134 | atomNumbers.add(ImmutableList.of(i)); 135 | Atom a = molecule.contents.get(i-1); 136 | elements.add(Element.getElement(a.symbol)); 137 | } 138 | List> meansByTrajectory = new ArrayList<>(); // means for each trajectory, index parallel to atomNumbers 139 | for (int i=0; i < atomNumbers.size(); i++) 140 | meansByTrajectory.add(new ArrayList()); 141 | 142 | // collect data 143 | int count = 0; 144 | System.out.println(trajectories.size() + " total trajs"); 145 | for (Trajectory trajectory : trajectories) 146 | { 147 | if ( ignoreIncomplete && !trajectory.isDone() ) 148 | { 149 | System.out.printf("Skipping incomplete trajectory %s.\n", trajectory.checkpointFilename); 150 | continue; 151 | } 152 | 153 | // collect all the shieldings in this trajectory together 154 | List allPoints = new ArrayList<>(); 155 | allPoints.add(trajectory.initialPoint); 156 | allPoints.addAll(trajectory.forwardPoints); 157 | allPoints.addAll(trajectory.backwardPoints); 158 | List> trajectoryShieldings = new ArrayList<>(); // all shieldings in trajectory, outer index is trajectory point, inner index is atom 159 | for (TrajectoryPoint p : allPoints) 160 | { 161 | if (p.shieldings != null && p.shieldings.size() > 0) 162 | trajectoryShieldings.add(p.shieldings); 163 | } 164 | //System.out.println(trajectoryShieldings.size() + " shieldings in this traj"); 165 | if (trajectoryShieldings.size() <= 20) // if there aren't a minimum number of shieldings, keep going 166 | continue; 167 | count++; 168 | 169 | // calculate the mean shielding accounting for symmetry in this trajectory 170 | 171 | for (int i=0; i < atomNumbers.size(); i++) 172 | { 173 | // collect all the shieldings for these atom numbers 174 | List atomNumbersToAverage = atomNumbers.get(i); 175 | List shieldingsToAverage = new ArrayList<>(); 176 | for (Integer atomNumber : atomNumbersToAverage) 177 | { 178 | for (List pointShieldings : trajectoryShieldings) 179 | { 180 | Double thisShielding = pointShieldings.get(atomNumber-1); 181 | shieldingsToAverage.add(thisShielding); 182 | } 183 | } 184 | 185 | // add the mean 186 | double thisMean = average(shieldingsToAverage); 187 | meansByTrajectory.get(i).add(thisMean); 188 | } 189 | } 190 | if ( count <= 1 ) 191 | throw new IllegalArgumentException("not enough trajectories to proceed"); 192 | 193 | // get the stationary point shieldings 194 | List stationaryPointShieldings = new ArrayList<>(); 195 | for (List atomNumbersToAverage : atomNumbers) 196 | { 197 | List shieldingsToAverage = new ArrayList<>(); 198 | for (Integer atomNumber : atomNumbersToAverage) 199 | shieldingsToAverage.add( molecule.shieldings.get(atomNumber-1) ); 200 | double averageShielding = average(shieldingsToAverage); 201 | stationaryPointShieldings.add(averageShielding); 202 | } 203 | 204 | // do statistics 205 | List overallMeans = new ArrayList<>(); 206 | List standardDeviations = new ArrayList<>(); 207 | List standardErrors = new ArrayList<>(); 208 | for (int i=0; i < meansByTrajectory.size(); i++) 209 | { 210 | DescriptiveStatistics stats = new DescriptiveStatistics(); 211 | List theseMeans = meansByTrajectory.get(i); // shieldings for this set of atom numbers, indexed by trajectory 212 | double stationaryPointShielding = stationaryPointShieldings.get(i); 213 | 214 | // compute the raw correction 215 | for (Double d : theseMeans) 216 | stats.addValue(d-stationaryPointShielding); 217 | overallMeans.add(stats.getMean()); 218 | standardDeviations.add(stats.getStandardDeviation()); 219 | double standardError = stats.getStandardDeviation() / Math.sqrt(theseMeans.size()); 220 | standardErrors.add(standardError); 221 | } 222 | 223 | // return result 224 | return new NMRtrajectoryAnalysis(elements, atomNumbers, overallMeans, standardDeviations, standardErrors); 225 | } 226 | 227 | /** 228 | * Averages the numbers in a list. 229 | * @param list the numbers to average 230 | * @return the average 231 | */ 232 | public static Double average(List list) 233 | { 234 | if ( list == null || list.size() == 0 ) 235 | throw new NullPointerException("empty list for averaging"); 236 | double sum = 0.0; 237 | for (Double d : list) 238 | sum += d; 239 | return sum / list.size(); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/NormalMode.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.*; 4 | import com.google.common.collect.*; 5 | import java.util.*; 6 | import java.io.*; 7 | 8 | /** 9 | * This immutable class represents a molecular vibration. 10 | */ 11 | public class NormalMode implements Immutable, Serializable { 12 | 13 | /** For serialization. */ 14 | public static final long serialVersionUID = 1L; 15 | 16 | /** Harmonic frequency of this mode in inverse cm. */ 17 | public final double frequency; 18 | 19 | /** Reduced mass in amu. */ 20 | public final double reducedMass; 21 | 22 | /** Force constant in mDyne/A. */ 23 | public final double forceConstant; 24 | 25 | /** The normal displacements in A for each atom. Indexed by atom. See {@link Molecule#contents}. */ 26 | public final List coordinates; 27 | 28 | /** 29 | * Constructs a NormalMode. 30 | * @param frequency the frequency in inverse cm 31 | * @param reducedMass the reduced mass in amu 32 | * @param forceConstant the force constant in mDyne/A 33 | * @param coordinates the normal displacements in Angstrom for each atom 34 | */ 35 | public NormalMode(double frequency, double reducedMass, double forceConstant, List coordinates) { 36 | this.frequency = frequency; 37 | this.reducedMass = reducedMass; 38 | this.forceConstant = forceConstant; 39 | 40 | if ( coordinates == null || coordinates.size() == 0 ) 41 | throw new NullPointerException("no normal coordinates"); 42 | this.coordinates = ImmutableList.copyOf(coordinates); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | StringBuilder s = new StringBuilder(String.format("\nFrequency (cm-1): %.2f\n", frequency)); 48 | s.append(String.format("Reduced Mass (amu): %.2f\n", reducedMass)); 49 | s.append(String.format("Force Constant (mDyne/A): %.2f\n",forceConstant)); 50 | s.append("Normal Coordinates (xyz; A):\nAtom Index: X displacement: Y displacement Z displacement:\n"); 51 | for (int i=0; i < coordinates.size(); i++) 52 | { 53 | Vector3D v = coordinates.get(i); 54 | s.append(String.format("%5d %10.5f %10.5f %10.5f\n", (i+1), v.getX(), v.getY(), v.getZ())); 55 | } 56 | s.append("\n"); 57 | return s.toString(); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(frequency, reducedMass, forceConstant, coordinates); 63 | } 64 | 65 | @Override 66 | public boolean equals(Object obj) { 67 | if ( obj == null ) 68 | return false; 69 | if ( obj == this ) 70 | return true; 71 | if ( !(obj instanceof NormalMode) ) 72 | return false; 73 | 74 | NormalMode n = (NormalMode)obj; 75 | if ( frequency == n.frequency && 76 | reducedMass == n.reducedMass && 77 | forceConstant == n.forceConstant && 78 | coordinates.equals(coordinates) ) 79 | return true; 80 | return false; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/OutputFileFormat.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | import com.google.common.collect.*; 6 | 7 | /** 8 | * This abstract class represents the result of a program external to Jprogdyn. 9 | */ 10 | public abstract class OutputFileFormat implements FileFormat 11 | { 12 | 13 | /** 14 | * The raw text in the file. 15 | */ 16 | public final String stringRepresentation; 17 | 18 | /** 19 | *

The parsed contents of the file.

20 | *

The outer list contains each line.

21 | *

Each inner list contains the space-separated tokens for each line.

22 | */ 23 | public final List> fileContents; 24 | 25 | /** 26 | * Constructor. 27 | * @param stringRepresentation the raw text in the file 28 | * @param fileContents the parsed contents of the file (outer list contains each line, inner list contains space-separated tokens) 29 | */ 30 | public OutputFileFormat(String stringRepresentation, List> fileContents) { 31 | this.stringRepresentation = stringRepresentation; 32 | this.fileContents = fileContents; 33 | } 34 | 35 | /** 36 | * Constructs an instance by reading text from filename. 37 | * Fields are parsed by using spaces as delimeters. Consecutive delimiters are ignored. 38 | * @param filename the file to read from 39 | */ 40 | public OutputFileFormat(String filename) { 41 | // get file length 42 | File file = new File(filename); 43 | long length = file.length(); // in bytes 44 | int characters = (int)(length/2L); 45 | 46 | List> tempList = new LinkedList<>(); 47 | StringBuilder builder = new StringBuilder(characters); 48 | String line = null; 49 | try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { 50 | while ( (line = reader.readLine()) != null ) 51 | { 52 | line = line.trim(); 53 | String[] fields = line.split("\\s+"); 54 | tempList.add(ImmutableList.copyOf(fields)); 55 | builder.append(line); 56 | builder.append("\n"); 57 | } 58 | } 59 | catch (Exception e) { 60 | throw new IllegalArgumentException(e.getMessage()); 61 | } 62 | stringRepresentation = builder.toString(); 63 | fileContents = ImmutableList.copyOf(tempList); 64 | } 65 | 66 | /** 67 | * Return the contents of the file. Newlines will be present. 68 | * @return the text that was in the file 69 | */ 70 | @Override 71 | public String toString() { 72 | return stringRepresentation; 73 | } 74 | 75 | @Override 76 | public int hashCode() { 77 | return Objects.hash(stringRepresentation, fileContents); 78 | } 79 | 80 | @Override 81 | public boolean equals(Object obj) { 82 | if ( obj == null ) 83 | return false; 84 | if ( obj == this ) 85 | return true; 86 | if ( !(obj instanceof OutputFileFormat) ) 87 | return false; 88 | 89 | OutputFileFormat o = (OutputFileFormat)obj; 90 | if ( stringRepresentation.equals(o.stringRepresentation) && 91 | fileContents.equals(o.fileContents) ) 92 | return true; 93 | return false; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Propagator.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | /** 4 | * This interface represents an object that calculates the next or previous trajectory point. 5 | */ 6 | public interface Propagator { 7 | /** 8 | * Calculates the next trajectory point. 9 | * @param trajectory the trajectory being propagated 10 | * @param point the last point 11 | * @param forwards whether to go forwards (true) or backwards (false) 12 | * @param isNMRpoint whether NMR shieldings should be evaluated for the next point 13 | * @return the next point 14 | */ 15 | public TrajectoryPoint propagate(Trajectory trajectory, TrajectoryPoint point, boolean forwards, boolean isNMRpoint); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Propagators.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import org.apache.commons.math3.geometry.euclidean.threed.*; 5 | 6 | /** 7 | * This enum contains implementations of Propagator. 8 | * Only the Velocity Verlet algorithm is currently implemented. 9 | */ 10 | public enum Propagators implements Propagator { 11 | /** 12 | * Uses the velocity Verlet algorithm to calculate the next point. The algorithm is: 13 | * 14 | * x(t + Dt) = x(t) + v(t) * Dt + 0.5 * a(t) * Dt^2 15 | * v(t + Dt) = v(t) + 0.5 * [ a(t) + a(t+Dt) ] Dt 16 | * 17 | * where Dt is the timestep, x(t) is the position at time t, v(t) is the velocity at time t, and 18 | * a(t) is the acceleration at time t. The acceleration can be derived from the force divided by the mass. 19 | * Unlike the regular Verlet, this algorithm is self-starting. 20 | * 21 | * Note that because we need the accelerations from both points to calculate the new velocity, we have to 22 | * make an intermediate TrajectoryPoint that only has the new positions. This intermediate is not exposed. 23 | */ 24 | VELOCITY_VERLET { 25 | @Override 26 | public TrajectoryPoint propagate(Trajectory trajectory, TrajectoryPoint oldPoint, boolean forwards, boolean isNMRpoint) { 27 | // check for nulls 28 | if (trajectory == null) 29 | throw new NullPointerException("null trajectory"); 30 | if (oldPoint == null) 31 | throw new NullPointerException("null old point"); 32 | 33 | // get required information 34 | Molecule molecule = trajectory.molecule; 35 | CalculationMethod dynamicsMethod = trajectory.dynamicsMethod; 36 | CalculationMethod nmrMethod = trajectory.nmrMethod; 37 | 38 | // set timestep 39 | double timestep = trajectory.timestep; // in fs 40 | if ( timestep < 0.0 ) 41 | throw new IllegalArgumentException("timestep must be positive"); 42 | if ( ! forwards ) 43 | timestep = timestep * -1.0; // go backwards 44 | 45 | // initialize lists to hold new verlet quantities 46 | List x_t = oldPoint.positions; // current positions in angstroms 47 | List v_t = oldPoint.velocities; // current velocities in angstroms/femtosecond 48 | List a_t = oldPoint.forces2; // 1/2 * a * Dt^2 in angstroms 49 | 50 | // calculate new positions 51 | List x_t_plus = new ArrayList(x_t); 52 | for (int i=0; i < x_t_plus.size(); i++) { 53 | Vector3D v = x_t_plus.get(i); 54 | v = v.add( v_t.get(i).scalarMultiply(timestep) ); 55 | v = v.add( a_t.get(i) ); 56 | x_t_plus.set(i, v); 57 | Vector3D delta = v.subtract(x_t.get(i)); 58 | } 59 | 60 | // evaluate forces with new geometry 61 | TrajectoryPoint tempPoint = TrajectoryPoint.create(oldPoint.time + timestep, x_t_plus); 62 | if ( isNMRpoint ) 63 | tempPoint = tempPoint.evaluatePoint(molecule, nmrMethod, timestep); 64 | else 65 | tempPoint = tempPoint.evaluatePoint(molecule, dynamicsMethod, timestep); 66 | 67 | // get accelerations in A/s^2 68 | List oldAccelerations = oldPoint.accelerations; 69 | List newAccelerations = tempPoint.accelerations; 70 | 71 | // calculate new velocities 72 | List v_t_plus = new ArrayList(v_t); 73 | for (int i=0; i < v_t_plus.size(); i++) 74 | { 75 | Vector3D v = v_t_plus.get(i); 76 | Vector3D temp = oldAccelerations.get(i).add( newAccelerations.get(i) ); 77 | temp = temp.scalarMultiply(0.5 * timestep); 78 | v = v.add(temp); 79 | v_t_plus.set(i, v); 80 | } 81 | 82 | // calculate kinetic and total energies 83 | // dimensional analysis: 84 | // 85 | // g A^2 fs^2 1E-20 m^2 kg s^2 J hartree 86 | // ----- * -------- * ---------------------- * ----------- * --------- * -------- * ----------------- = hartree 87 | // mol fs^2 1E-30 s^2 A^2 1000 g kg m^2 J_PER_HARTREE J 88 | // 89 | // = 1E7 / J_PER_HARTREE * timestep^2 90 | double kineticEnergy = 0.0; // in hartree / mol 91 | double conversionFactor = 1E7 / ( Units.J_PER_HARTREE * timestep * timestep ); 92 | for (int i=0; i < molecule.contents.size(); i++) 93 | { 94 | double mass = molecule.contents.get(i).mass; // amu 95 | double velocityNorm = v_t_plus.get(i).getNormSq(); // (A/fs)^2 96 | kineticEnergy += 0.5 * mass * velocityNorm * conversionFactor; // 0.5 * m * v^2 97 | } 98 | double totalEnergy = kineticEnergy + tempPoint.potentialEnergy; // in hartree 99 | 100 | // construct and return new point 101 | return new TrajectoryPoint(tempPoint.time, kineticEnergy, tempPoint.potentialEnergy, totalEnergy, 102 | tempPoint.positions, v_t_plus, tempPoint.accelerations, tempPoint.forces, 103 | tempPoint.forces2, tempPoint.shieldings, tempPoint.evaluationTime); 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/RotationalBoltzmann.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.*; 4 | import org.apache.commons.math3.linear.*; 5 | import org.apache.commons.math3.special.*; 6 | import com.google.common.collect.*; 7 | import java.util.*; 8 | import java.io.*; 9 | import java.util.concurrent.*; 10 | 11 | /** 12 | * A Boltzmann-like distribution of angular momenta for a given molecule. 13 | * This object should be initialized using a list of Atoms, and 14 | * will calculate and store the principal axes, inertia tensor, 15 | * and parameters for a distribution of angular frequency vectors. When the 16 | * getRotation method is called, an angular frequency vector 17 | * is obtained from random distribution. 18 | */ 19 | public class RotationalBoltzmann implements Immutable { 20 | /** 21 | * Multiplication by this constant converts amu * angstrom^2 22 | * to electronVolt * femtosecond^2. Believe it or not, this is 23 | * the only unit conversion factor required, as the Boltzmann 24 | * distribution is calculated in terms of a unitless parameter. 25 | */ 26 | private final static double MAGIC_CONSTANT = 103.642692; 27 | 28 | /** Principal axes. */ 29 | public final Vector3D axis1, axis2, axis3; 30 | 31 | /** Principal moments in amu * angstrom^2 */ 32 | public final double I1, I2, I3; 33 | 34 | /** 35 | * Constructs the distribution from a Molecule. 36 | * @param molecule the input molecule 37 | */ 38 | public RotationalBoltzmann(Molecule molecule) { 39 | this(molecule.contents); 40 | } 41 | 42 | /** 43 | * Constructs the distribution from a list of atoms. 44 | * @param contents the molecular geometry to build the distribution from 45 | */ 46 | public RotationalBoltzmann(List contents) 47 | { 48 | // we want to move the center of mass to the origin for easy calculation 49 | Vector3D centerOfMass = RotationalBoltzmann.centerOfMass(contents); 50 | List shiftedContents = new ArrayList(); 51 | for ( Atom atom : contents ) { 52 | Vector3D oldPosition = atom.position; 53 | Vector3D translation = centerOfMass.negate(); 54 | Vector3D newPosition = oldPosition.add(translation); 55 | Atom newAtom = atom.shift(newPosition); 56 | shiftedContents.add(newAtom); 57 | } 58 | 59 | // we will now calculate moments 60 | double Ixx = 0; 61 | double Iyy = 0; 62 | double Izz = 0; 63 | double Ixy = 0; 64 | double Iyz = 0; 65 | double Ixz = 0; 66 | for ( Atom atom : shiftedContents ) 67 | { 68 | if ( atom.mass == 0 ) 69 | throw new IllegalArgumentException("Atom mass is zero!"); 70 | 71 | Ixx += atom.mass * (atom.position.getY()*atom.position.getY() + atom.position.getZ()*atom.position.getZ()); 72 | Iyy += atom.mass * (atom.position.getX()*atom.position.getX() + atom.position.getZ()*atom.position.getZ()); 73 | Izz += atom.mass * (atom.position.getX()*atom.position.getX() + atom.position.getY()*atom.position.getY()); 74 | Ixy -= atom.mass * atom.position.getX() * atom.position.getY(); 75 | Iyz -= atom.mass * atom.position.getY() * atom.position.getZ(); 76 | Ixz -= atom.mass * atom.position.getX() * atom.position.getZ(); 77 | } 78 | 79 | RealMatrix I = MatrixUtils.createRealMatrix(new double[][]{{Ixx, Ixy, Ixz},{Ixy, Iyy, Iyz},{Ixz, Iyz, Izz}}); 80 | 81 | EigenDecomposition eigenI = new EigenDecomposition(I); 82 | 83 | // get the principal axes from the V matrix 84 | this.axis1 = new Vector3D(eigenI.getV().getColumn(0)).normalize(); 85 | this.axis2 = new Vector3D(eigenI.getV().getColumn(1)).normalize(); 86 | this.axis3 = new Vector3D(eigenI.getV().getColumn(2)).normalize(); 87 | 88 | // read the eigenvalues from the EigenDecomposition. 89 | // certain pathological cases will not have three eigenvalues 90 | // in this cases, we will store a zero value for some eigenvalues, 91 | // and remember to deal with it when obtaining an angular frequency. 92 | // essentially, we will not rotate on axes with zero eigenvalue. 93 | double[] evalues = eigenI.getRealEigenvalues(); 94 | 95 | if ( evalues.length == 0 ) 96 | throw new IllegalArgumentException ("Given atom group has no valid rotations!"); 97 | else if ( evalues.length == 1 ) 98 | {// This case is silly, and will never occur. 99 | I1 = evalues[0]; 100 | I2 = 0; 101 | I3 = 0; 102 | } 103 | else if ( evalues.length == 2 ) 104 | {// This is the linear case. 105 | I1 = evalues[0]; 106 | I2 = evalues[1]; 107 | I3 = 0; 108 | } 109 | else 110 | { 111 | I1 = evalues[0]; 112 | I2 = evalues[1]; 113 | I3 = evalues[2]; 114 | } 115 | } 116 | 117 | /** 118 | * Returns a random angular frequency from the distribution. The user 119 | * inputs a temperature, and three random numbers. These numbers correspond 120 | * to the rotational energy around each of the 3 principal axes. The sign 121 | * of the number specifies the direction, while the magnitude of the number 122 | * represents the fraction of molecules that should have less rotational 123 | * energy in this mode in our distribution. The returned angular momentum 124 | * is in regular Cartesian coordinates -- anyone outside this class 125 | * should not have to think about the principal axes. 126 | * @param kT a double representing the temperature in ELECTRONVOLTS 127 | * @param x1 a random number between -1 and 1 for the first angular frequency component 128 | * @param x2 a random number between -1 and 1 for the second angular frequency component 129 | * @param x3 a random number between -1 and 1 for the third angular frequency component 130 | * @return omega, the angular frequency in radians per FEMTOSECOND 131 | */ 132 | public Vector3D getOmega(double kT, double x1, double x2, double x3) 133 | { 134 | double[] omega = {0,0,0}; 135 | // we remove the signs and put them back later 136 | int sign1 = (int)Math.signum(x1); 137 | int sign2 = (int)Math.signum(x2); 138 | int sign3 = (int)Math.signum(x3); 139 | x1 = Math.abs(x1); 140 | x2 = Math.abs(x2); 141 | x3 = Math.abs(x3); 142 | double energy1 = 0; 143 | double energy2 = 0; 144 | double energy3 = 0; 145 | 146 | if (I1 > 0) 147 | { 148 | energy1 = kT * inverseCumulativeBoltzmann(x1); 149 | omega[0] = sign1 * Math.sqrt((2 * energy1)/(I1 * MAGIC_CONSTANT)); 150 | } 151 | if (I2 > 0) 152 | { 153 | energy2 = kT * inverseCumulativeBoltzmann(x2); 154 | omega[1] = sign2 * Math.sqrt((2 * energy2)/(I2 * MAGIC_CONSTANT)); 155 | } 156 | if (I3 > 0) 157 | { 158 | energy3 = kT * inverseCumulativeBoltzmann(x3); 159 | omega[2] = sign3 * Math.sqrt((2 * energy3)/(I1 * MAGIC_CONSTANT)); 160 | } 161 | 162 | double conversion = 3.8267327959301E-23 * Units.AVOGADROS_NUMBER; 163 | double energy1_kcal = energy1 * conversion; 164 | double energy2_kcal = energy2 * conversion; 165 | double energy3_kcal = energy3 * conversion; 166 | double totalEnergy_kcal = energy1_kcal + energy2_kcal + energy3_kcal; 167 | //System.out.printf("Total rotational energy: %.4f kcal/mol (%.4f axis1, %.4f axis2, %.4f axis3)\n", totalEnergy_kcal, energy1_kcal, energy2_kcal, energy3_kcal); 168 | 169 | // transform these back into regular Cartesian coordinates 170 | return new Vector3D(omega[0], axis1, omega[1], axis2, omega[2], axis3); 171 | } 172 | 173 | /** 174 | * Calculates the center of mass vector for a list of atoms. 175 | */ 176 | private static Vector3D centerOfMass(List contents) 177 | { 178 | double totalMass = 0.0; 179 | Vector3D weightedPosition = Vector3D.ZERO; 180 | for ( Atom atom : contents ) 181 | { 182 | totalMass += atom.mass; 183 | weightedPosition = weightedPosition.add(atom.mass, atom.position); 184 | } 185 | return weightedPosition.scalarMultiply(1.0/totalMass); 186 | } 187 | 188 | /** 189 | * Returns a random energy drawn from a Boltzmann distribution for the specified temperature. 190 | * Will return a mean energy of 0.5 kT. 0.5 kT is about 0.3 kcal/mol at 298 K. 191 | * @param temperature the temperature in K 192 | * @return the random energy in kcal/mol 193 | */ 194 | public static double getRandomBoltzmannEnergy(double temperature) 195 | { 196 | double randomNumber = ThreadLocalRandom.current().nextDouble(); 197 | 198 | // 199 | // kcal 200 | // ------- * K = kcal/mol ---> must use R, not k 201 | // mol K 202 | // 203 | double kT = Units.R_GAS_KCAL * temperature; 204 | double energy = inverseCumulativeBoltzmann(randomNumber) * kT; 205 | return energy; 206 | } 207 | 208 | /** How many kT to search up to. */ 209 | public static final double CUTOFF_ENERGY = 10.0; 210 | 211 | /** 212 | * Inverse Cumulative Boltzmann helper method. Calculates the 213 | * rotational kinetic energy for which x fraction of molecules have less 214 | * kinetic energy. Normalized so that kT = 1. 215 | * Unit conversions are to be made outside the method. 216 | * @param x the fraction of molecules with less rotational energy 217 | * @return energy divided by kT 218 | */ 219 | private static double inverseCumulativeBoltzmann(double x) 220 | { 221 | if (x > 1 || x < 0) 222 | throw new IllegalArgumentException("Illegal rotational energy specification."); 223 | 224 | // Find the inverse of this monotonically increasing function 225 | // in two steps. First scan up to 10 kT with large tolerance, 226 | // then scan in smaller steps around the solution. The cutoff at 10 kT 227 | // is arbitrary, but we should not be using energies that high anyway. 228 | double trialEnergy = -1; 229 | for ( double i = 0; i < CUTOFF_ENERGY; i += 0.01 ) 230 | { 231 | if ( cumulativeBoltzmann(i) > x ) 232 | { 233 | trialEnergy = i - 0.01; 234 | break; 235 | } 236 | // System.out.println( cumulativeBoltzmann(i) ); 237 | } 238 | 239 | // if we failed to find the inverse on this first pass, 240 | // we are in the high energy tail of the distribution, and 241 | // we simply return the max energy 242 | if ( trialEnergy == -1 ) 243 | return CUTOFF_ENERGY; 244 | 245 | // now we scan in small steps. 246 | for ( double i = trialEnergy; i < trialEnergy + 0.01; i += 0.0001 ) 247 | { 248 | if ( cumulativeBoltzmann(i) > x ) 249 | { 250 | trialEnergy = i - 0.00001; 251 | break; 252 | } 253 | } 254 | return trialEnergy - 0.00001; 255 | } 256 | 257 | /** 258 | * Cumulative Boltzmann helper method. Calculates the cumulative 259 | * Boltzmann distribution for a positive energy x = E/kT. 260 | * @param x the energy 261 | * @return the probability of a molecule having kinetic energy less than x 262 | */ 263 | private static double cumulativeBoltzmann(double x) 264 | { 265 | if ( x < 0 ) 266 | throw new IllegalArgumentException("Tried to use Boltzmann cdf with negative energy!"); 267 | return Erf.erf(Math.sqrt(x)); // Kinetic energy is normally distributed. 268 | 269 | //return Math.sqrt(x)*(-2*Math.exp(-x)/Math.sqrt(Math.PI) + Erf.erf(Math.sqrt(x))/Math.sqrt(x)); 270 | } 271 | 272 | /** 273 | * This is a static utility method that calculates velocities for rotation. 274 | * Given a group of atoms and an origin, the method should return a matching 275 | * List of velocities for these atoms that performs a rotation at angular 276 | * frequency omega. 277 | * @param contents a list of atoms to be rotated 278 | * @param omega the angular frequency vector in radians per FEMTOSECOND 279 | * @param origin the origin of rotation; it is recommended that 280 | * this be set to the origin of the coordinate system 281 | * so that center of mass rotations will not be confused 282 | * with other rotations. See alias methods. 283 | * @return vector of velocities in the same order as the original 284 | */ 285 | public static List getVelocities(List contents, Vector3D omega, Vector3D origin) 286 | { 287 | List shiftedContents = new ArrayList(); 288 | for ( Atom atom : contents ) 289 | shiftedContents.add(atom.shift(origin.negate())); 290 | 291 | List returnList = new ArrayList<>(contents.size()); 292 | for ( Atom atom : contents ) 293 | returnList.add(Vector3D.crossProduct(omega, atom.position)); 294 | 295 | return ImmutableList.copyOf(returnList); 296 | } 297 | 298 | /** 299 | * Alias method. Automatically use center of mass as rotation origin. 300 | * @param contents the molecular geometry 301 | * @param omega the angular frequency vector 302 | * @return vector of velocities in same order as the original 303 | */ 304 | public static List getVelocities(List contents, Vector3D omega) { 305 | return getVelocities(contents, omega, RotationalBoltzmann.centerOfMass(contents)); 306 | } 307 | 308 | /** 309 | * Checks equality by checking the contained atoms only. 310 | */ 311 | @Override 312 | public boolean equals(Object obj) { 313 | if ( obj == null ) 314 | return false; 315 | if ( obj == this ) 316 | return true; 317 | if ( !(obj instanceof RotationalBoltzmann) ) 318 | return false; 319 | 320 | RotationalBoltzmann r = (RotationalBoltzmann)obj; 321 | if ( axis1.equals(r.axis1) && 322 | axis2.equals(r.axis2) && 323 | axis3.equals(r.axis3) && 324 | I1 == r.I1 && 325 | I2 == r.I2 && 326 | I3 == r.I3 ) 327 | return true; 328 | return false; 329 | } 330 | 331 | /** 332 | * String representation of the distribution is just the axes and moments. 333 | */ 334 | @Override 335 | public String toString() { 336 | return(I1 + "\n" + axis1 + "\n" 337 | + I2 + "\n" + axis2 + "\n" 338 | + I3 + "\n" + axis3 + "\n"); 339 | } 340 | 341 | /** 342 | * To agree with this.equals, the hash is just that of the atoms. 343 | */ 344 | public int hashCode() { 345 | return Objects.hash(axis1, axis2, axis3, I1, I2, I3); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Singleton.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | /** 4 | * This is a marker interface for classes that must exist as singletons. 5 | */ 6 | public interface Singleton { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/TrajectoryExecutorService.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.io.*; 5 | import java.util.concurrent.*; 6 | 7 | /** 8 | * This class provides static methods to run groups of Trajectories. 9 | */ 10 | public class TrajectoryExecutorService implements Singleton { 11 | 12 | /** Thread pool for doing the work. */ 13 | private static final ThreadPoolExecutor EXECUTOR; 14 | 15 | /** Static initializer. */ 16 | static { 17 | ArrayBlockingQueue queue = new ArrayBlockingQueue<>(100000, true); // fair queue 18 | int NUMBER_OF_THREADS = Loader.getInteger("number_of_simultaneous_trajectories"); 19 | EXECUTOR = new ThreadPoolExecutor(NUMBER_OF_THREADS, NUMBER_OF_THREADS, Long.MAX_VALUE, TimeUnit.SECONDS, queue); 20 | } 21 | 22 | /** Not instantiable. */ 23 | private TrajectoryExecutorService() { 24 | throw new IllegalArgumentException("not instantiable"); 25 | } 26 | 27 | /** 28 | * Runs a group of trajectories in parallel. 29 | * The calling thread blocks until the trajectories are finished. 30 | * If there is a problem running any of the trajectories, the original trajectory is returned. 31 | * @param trajectories the trajectories to run 32 | * @return the completed trajectories 33 | */ 34 | public static List runTrajectories(List trajectories) { 35 | if ( trajectories == null ) 36 | throw new NullPointerException("trajectories is null"); 37 | 38 | // submit work 39 | System.out.printf("Running %d trajectories...\n", trajectories.size()); 40 | List> futures = new LinkedList<>(); 41 | for (Trajectory t : trajectories) { 42 | Future f = EXECUTOR.submit(t); 43 | futures.add(f); 44 | } 45 | 46 | // wait until jobs are done 47 | while (true) { 48 | int done = 0; 49 | for (Future f : futures) { 50 | if ( f.isDone() || f.isCancelled() ) 51 | done++; 52 | } 53 | if ( done == futures.size() ) 54 | break; 55 | try { Thread.sleep(500); } 56 | catch ( InterruptedException e ) {} 57 | } 58 | 59 | // return result 60 | List completedTrajectories = new LinkedList<>(); 61 | for (int i=0; i < trajectories.size(); i++) { 62 | Trajectory originalTrajectory = trajectories.get(i); 63 | Trajectory finishedTrajectory = null; 64 | Future f = futures.get(i); 65 | try { finishedTrajectory = f.get(); } 66 | catch (Exception e) { } 67 | if ( finishedTrajectory != null ) 68 | completedTrajectories.add(finishedTrajectory); 69 | else 70 | completedTrajectories.add(originalTrajectory); 71 | } 72 | return completedTrajectories; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/TrajectoryPoint.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.*; 4 | import java.util.*; 5 | import java.io.*; 6 | import com.google.common.collect.*; 7 | 8 | /** 9 | * This class represents a point along a molecular dynamics trajectory. 10 | */ 11 | public class TrajectoryPoint implements Immutable, Serializable { 12 | 13 | /** For serialization. */ 14 | public static final long serialVersionUID = 1L; 15 | 16 | /** The time in fs. Can be negative for backwards points. */ 17 | public final double time; 18 | 19 | /** The kinetic energy in hartree. */ 20 | public final double kineticEnergy; 21 | 22 | /** The potential energy in hartree. */ 23 | public final double potentialEnergy; 24 | 25 | /** The total energy in kcal. */ 26 | public final double totalEnergy; 27 | 28 | /** The current positions of all atoms in A. */ 29 | public final List positions; 30 | 31 | /** Cartesian velocities of all the atoms in angstroms/femtosecond. */ 32 | public final List velocities; 33 | 34 | /** Cartesian accelerations of all atoms in angstroms/fs^2. */ 35 | public final List accelerations; 36 | 37 | /** Cartesian forces on all atoms in hartrees/bohr. */ 38 | public final List forces; 39 | 40 | /** Cartesian forces on all atoms in (1/2 acceleration timeStep^2 term) in angstroms. */ 41 | public final List forces2; 42 | 43 | /** Absolute NMR shieldings in ppm. */ 44 | public final List shieldings; 45 | 46 | /** Time it took to evaluate this point in seconds. */ 47 | public final double evaluationTime; 48 | 49 | /** 50 | * Constructs a TrajectoryPoint. 51 | * @param time the time in fs (negative for backwards points) 52 | * @param kineticEnergy the kinetic energy in hartree 53 | * @param potentialEnergy the potential energy in hartree 54 | * @param totalEnergy the total energy in kcal 55 | * @param positions the positions of all atoms in Angstroms (cannot be null) 56 | * @param velocities Cartesian velocities of all the atoms in angstroms/femtosecond 57 | * @param accelerations accelerations of all atoms in angstroms/fs^2 58 | * @param forces Cartesian forces on all atoms in hartrees/bohr 59 | * @param forces2 Cartesian forces on all atoms in (1/2 acceleration timeStep^2 term) in angstroms 60 | * @param shieldings absolute NMR shieldings in ppm 61 | * @param evaluationTime time it took to evaluate this point in seconds 62 | */ 63 | public TrajectoryPoint(double time, double kineticEnergy, double potentialEnergy, double totalEnergy, 64 | List positions, List velocities, List accelerations, 65 | List forces, List forces2, List shieldings, 66 | double evaluationTime) { 67 | 68 | // check invariants 69 | this.time = time; 70 | 71 | if ( kineticEnergy < 0.0 ) 72 | throw new IllegalArgumentException("negative kinetic energy"); 73 | this.kineticEnergy = kineticEnergy; 74 | 75 | this.potentialEnergy = potentialEnergy; 76 | this.totalEnergy = totalEnergy; 77 | 78 | if ( positions == null || positions.size() == 0 ) 79 | throw new NullPointerException("null positions"); 80 | this.positions = ImmutableList.copyOf(positions); 81 | 82 | if ( velocities == null ) 83 | this.velocities = null; 84 | else { 85 | if ( velocities.size() == 0 ) 86 | throw new IllegalArgumentException("empty velocities"); 87 | this.velocities = ImmutableList.copyOf(velocities); 88 | } 89 | 90 | if ( accelerations == null ) 91 | this.accelerations = null; 92 | else { 93 | if ( accelerations.size() == 0 ) 94 | throw new IllegalArgumentException("empty accelerations"); 95 | this.accelerations = ImmutableList.copyOf(accelerations); 96 | } 97 | 98 | if ( forces == null ) 99 | this.forces = null; 100 | else { 101 | if ( forces.size() == 0 ) 102 | throw new IllegalArgumentException("empty forces"); 103 | this.forces = ImmutableList.copyOf(forces); 104 | } 105 | 106 | if ( forces2 == null ) 107 | this.forces2 = null; 108 | else { 109 | if ( forces2.size() == 0 ) 110 | throw new IllegalArgumentException("empty forces2"); 111 | this.forces2 = ImmutableList.copyOf(forces2); 112 | } 113 | 114 | if ( shieldings == null ) 115 | this.shieldings = null; 116 | else 117 | this.shieldings = ImmutableList.copyOf(shieldings); 118 | 119 | if (evaluationTime < 0.0) 120 | throw new IllegalArgumentException("negative evaluation time"); 121 | this.evaluationTime = evaluationTime; 122 | } 123 | 124 | /** 125 | * Makes a trajectory point but does not evaluate it. Created with time and positions only. 126 | * @param time the time in fs (negative for backwards points) 127 | * @param positions the current positions of all atoms in Angstroms 128 | * @return a blank TrajectoryPoint 129 | */ 130 | public static TrajectoryPoint create(double time, List positions) { 131 | return new TrajectoryPoint(time, 0.0, 0.0, 0.0, positions, null, null, null, null, null, 0.0); 132 | } 133 | 134 | /** 135 | * Makes a trajectory point but does not evaluate it. Created with time, positions, and velocities only. 136 | * @param time the time in fs (negative for backwards points) 137 | * @param positions the current positions of all atoms in Angstroms 138 | * @param velocities Cartesian velocities of all the atoms in angstroms/femtosecond 139 | * @return a blank TrajectoryPoint 140 | */ 141 | public static TrajectoryPoint create(double time, List positions, List velocities) { 142 | return new TrajectoryPoint(time, 0.0, 0.0, 0.0, positions, velocities, null, null, null, null, 0.0); 143 | } 144 | 145 | /** 146 | * Evaluates the point and returns a replacement that has all the fields all filled out. 147 | * @param molecule needed for header information 148 | * @param calculationMethod level of theory 149 | * @param timestep the timestep between the last point and this point in fs 150 | * @return the evaluated point 151 | */ 152 | public TrajectoryPoint evaluatePoint(Molecule molecule, CalculationMethod calculationMethod, double timestep) { 153 | if ( calculationMethod == null ) 154 | throw new NullPointerException("must specify a calculation method!"); 155 | if ( calculationMethod instanceof GaussianCalculationMethod ) 156 | return evaluateGaussianPoint(molecule, (GaussianCalculationMethod)calculationMethod, timestep); 157 | else 158 | throw new IllegalArgumentException("unrecognized electronic structure program"); 159 | } 160 | 161 | /** 162 | * Evaluates a TrajectoryPoint using Gaussian. 163 | * @param molecule needed for header information 164 | * @param gaussianCalculationMethod level of theory 165 | * @param timestep the timestep between the last point and this point in fs 166 | * @return the evaluated point 167 | */ 168 | private TrajectoryPoint evaluateGaussianPoint(Molecule molecule, GaussianCalculationMethod gaussianCalculationMethod, double timestep) { 169 | // create and run job 170 | GaussianInputFile inputFile = new GaussianInputFile(molecule, positions, gaussianCalculationMethod); 171 | GaussianJob job = new GaussianJob(inputFile); 172 | GaussianResult result = job.call(); 173 | GaussianOutputFile outputFile = result.out; 174 | Molecule m = outputFile.molecule; 175 | 176 | // If available, change from forces, which is in hartree/bohr, to forces2, which is in Angstroms. forces2 is defined as a distance in angstroms: 177 | // 178 | // 0.5 * acceleration * timestep^2 term 179 | // 180 | // This is useful because we need to calculate x(t) = x(0) + velocity(t) * t + 0.5*a*t^2. 181 | // force = mass * acceleration, so divide force by mass to get acceleration and multiply by timestep^2. 182 | // 183 | // Here is the dimensional analysis. Capital letters represent constants in the Units class. Note that a J is kg m^2 s^-2, which cancels 184 | // out the timestep s^2 and the mass kg. 185 | // 186 | // hartree timestep^2 1E-30 s^2 1000 g KCAL_PER_HARTREE kcal 4184 kg 1E20 angstrom^2 bohr 187 | // --------- * ---------------------- * -------- * ----------------------- * -------- -------------------- * ---------------- 188 | // bohr mass * g/mol kg hartree kcal s^2 BOHR angstroms 189 | // 190 | // where I replaced J with kg (1E20 angstrom^2) s^-2 in the third last term. Also note that hartree is implicitly energy/mol. The numbers are: 191 | // 192 | // timestep^2 * 1E-30 * 1000 * KCAL_PER_HARTREE * 4184 * 1E20 timestep^2 * 1E-7 * KCAL_PER_HARTREE * 4184 193 | // = ----------------------------------------------------------- = --------------------------------------------- 194 | // mass * BOHR mass * BOHR 195 | // 196 | // The actual conversion factor needs an extra 0.5. 197 | List newForces = m.forces; // hartree/bohr 198 | List newForces2 = null; // in angstroms 199 | List newAccelerations = null; // in A / fs^2 200 | if ( newForces != null ) { 201 | if ( newForces.size() != molecule.contents.size() ) 202 | throw new IllegalArgumentException("list size mismatch: check that forces were calculated"); 203 | 204 | // calculate 0.5 * a * t^2 term 205 | newForces2 = new ArrayList<>(newForces.size()); 206 | double conversionFactor = 0.5 * timestep * timestep * 1E-7 * Units.KCAL_PER_HARTREE * 4184 / Units.BOHR; 207 | for (int i=0; i < molecule.contents.size(); i++) { 208 | Vector3D rawForce = newForces.get(i); 209 | double mass = molecule.contents.get(i).mass; // in amu 210 | Vector3D newForce = rawForce.scalarMultiply(conversionFactor/mass); 211 | newForces2.add(newForce); 212 | } 213 | 214 | // calculate accelerations in A / fs^2 215 | // 216 | // force = mass * acceleration, so divide force by mass to get acceleration 217 | // 218 | // dimensional analysis: 219 | // 220 | // hartree mol bohr 1000 g J_PER_HARTREE J kg m^2 1 1E20 A^2 1E-30 s^2 221 | // --------- * ----- * ---------------- * -------- * ----------------- * -------- --- * ---------- * ----------- 222 | // bohr g BOHR angstroms kg hartree s^2 J m^2 fs^2 223 | // 224 | // = 1000 * J_PER_HARTREE * 1E-10 / BOHR 225 | newAccelerations = new ArrayList<>(newForces.size()); 226 | conversionFactor = 1E-7 * Units.J_PER_HARTREE / Units.BOHR; 227 | for (int i=0; i < molecule.contents.size(); i++) { 228 | Vector3D rawForce = newForces.get(i); 229 | double mass = molecule.contents.get(i).mass; // in amu 230 | Vector3D acceleration = rawForce.scalarMultiply(conversionFactor/mass); 231 | newAccelerations.add(acceleration); 232 | } 233 | } 234 | 235 | // create new point 236 | // kinetic and total energy set by Propagator 237 | return new TrajectoryPoint(time, 0.0, m.potentialEnergy, 0.0, 238 | positions, velocities, newAccelerations, newForces, newForces2, 239 | m.shieldings, result.elapsedTime); 240 | } 241 | 242 | /** 243 | * Writes a block for a MOLDEN movie. 244 | * @param molecule we need the atom symbols 245 | * @return the traj string 246 | */ 247 | public String toTrajString(Molecule molecule) { 248 | String trajString = positions.size() + "\n" + potentialEnergy + " Jprogdyn t = " + String.format("%.1f\n", time); 249 | for (int i=0; i < positions.size(); i++) { 250 | String symbol = molecule.contents.get(i).symbol; 251 | Vector3D position = positions.get(i); 252 | trajString = trajString + String.format( "%s %.7f %.7f %.7f\n", symbol, position.getX(), position.getY(), position.getZ() ); 253 | } 254 | return trajString; 255 | } 256 | 257 | @Override 258 | public String toString() { 259 | return String.format("Trajectory Point: %.1f fs, PE = %.8f, KE = %.8f, TE = %.8f\n", time, potentialEnergy, kineticEnergy, totalEnergy); 260 | } 261 | 262 | @Override 263 | public int hashCode() { 264 | return Objects.hash(time, kineticEnergy, potentialEnergy, totalEnergy, positions, velocities, accelerations, forces, forces2, shieldings, evaluationTime); 265 | } 266 | 267 | @Override 268 | public boolean equals(Object obj) { 269 | if ( obj == null ) 270 | return false; 271 | if ( obj == this ) 272 | return true; 273 | if ( !(obj instanceof TrajectoryPoint) ) 274 | return false; 275 | 276 | TrajectoryPoint p = (TrajectoryPoint)obj; 277 | if ( time == p.time && 278 | kineticEnergy == p.kineticEnergy && 279 | potentialEnergy == p.potentialEnergy && 280 | totalEnergy == p.totalEnergy && 281 | positions.equals(p.positions) && 282 | Objects.equals(velocities, p.velocities) && 283 | Objects.equals(accelerations, p.accelerations) && 284 | Objects.equals(forces, p.forces) && 285 | Objects.equals(forces2, p.forces2) && 286 | Objects.equals(shieldings, p.shieldings) && 287 | evaluationTime == p.evaluationTime ) 288 | return true; 289 | return false; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/Units.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | /** 4 | * This interface holds some unit conversions. 5 | */ 6 | public interface Units { 7 | 8 | /** speed of light, cm/s */ 9 | public static final double C = 29979245800.0; 10 | 11 | /** Planck's constant, m^2 * kg / s */ 12 | public static final double H = 6.626075E-34; 13 | 14 | /** molar gas constant, J / (mol * K) */ 15 | public static final double R_GAS_J = 8.31447; 16 | 17 | /** molar gas constant, kcal / (mol * K) */ 18 | public static final double R_GAS_KCAL = 0.0019858; 19 | 20 | /** Boltzmann's constant, eV / K */ 21 | public static final double BOLTZMANN_EV = 8.61733238E-5; 22 | 23 | /** Boltzmann's constant, kcal / K */ 24 | public static final double BOLTZMANN_KCAL = 3.29983E-27; 25 | 26 | /** converts J to kcal/mol */ 27 | public static final double J_TO_KCAL_PER_MOL = 6.0221415E23/4184.0; 28 | 29 | /** converts kcal/mol to J */ 30 | public static final double KCAL_PER_MOL_TO_J = 4184.0/6.0221415E23; 31 | 32 | /** Avogadro's number */ 33 | public static final double AVOGADROS_NUMBER = 6.0221415E23; 34 | 35 | /** Angstroms in a bohr */ 36 | public static final double BOHR = 0.52917721092; 37 | 38 | /** kcal per mol/hartree */ 39 | public static final double KCAL_PER_HARTREE = 627.509369; 40 | 41 | /** J per mol/hartree */ 42 | public static final double J_PER_HARTREE = 2625499.62; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/edu/harvard/chemistry/ekwan/Jprogdyn/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the javadoc for Jprogdyn. For general information on 3 | * Jprogdyn, please see the README file. 4 | */ 5 | 6 | package edu.harvard.chemistry.ekwan.Jprogdyn; 7 | -------------------------------------------------------------------------------- /src/test/java/edu/harvard/chemistry/ekwan/Jprogdyn/HarmonicTestGaussian.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.lang.Math; 5 | import org.apache.commons.math3.geometry.euclidean.threed.*; 6 | import java.nio.file.*; 7 | 8 | import junit.framework.Test; 9 | import junit.framework.TestCase; 10 | import junit.framework.TestSuite; 11 | 12 | /** 13 | * This test takes a single normal mode from a molecule, creates positive and negative 14 | * displacements at regular intervals along that mode (up to the classical turning point 15 | * defined by the zero-point energy), and writes the results to a single Gaussian input file 16 | * as a set of --Link1-- commands. 17 | * 18 | * For imaginary frequencies, there is no classical turning point, so displacements are made 19 | * directly in angstroms. 20 | * 21 | * To run: mvn -Dtest=HarmonicTestGaussian test 22 | */ 23 | public class HarmonicTestGaussian extends TestCase 24 | { 25 | /** 26 | * Create the test case 27 | * 28 | * @param testName name of the test case 29 | */ 30 | public HarmonicTestGaussian( String testName ) 31 | { 32 | super( testName ); 33 | } 34 | 35 | /** 36 | * Read the molecule, create displacements, then write the results to one Gaussian input file. 37 | */ 38 | public void testHarmonicGaussian() 39 | { 40 | // displacement parameters 41 | int modeIndex = 0; // zero-indexed 42 | 43 | double relativeShiftMin = -1.0; // most negative fraction of classical turning point to displace by 44 | double relativeShiftMax = 1.0; // most positive fraction of classical turning point to displace by 45 | double relativeShiftInterval = 0.1; // the displacement step interval in fractions of the classical turning point 46 | 47 | double absoluteShiftMin = -0.005; // used for negative frequencies 48 | double absoluteShiftMax = 0.005; // displacement in angstroms 49 | double absoluteShiftInterval = 0.0005; 50 | 51 | // check parameters 52 | if ( relativeShiftMin >= relativeShiftMax ) 53 | throw new IllegalArgumentException("check relative shift min/max"); 54 | if ( relativeShiftInterval < 0.0 ) 55 | throw new IllegalArgumentException("check relative shift interval"); 56 | if ( absoluteShiftMin >= absoluteShiftMax ) 57 | throw new IllegalArgumentException("check absolute shift min/max"); 58 | if ( absoluteShiftInterval < 0.0 ) 59 | throw new IllegalArgumentException("check absolute shift interval"); 60 | 61 | // Gaussian parameters 62 | String outputDirectory = "analysis"; 63 | int processors = 36; 64 | int memory = 24; // in GB 65 | String footer = "\n"; 66 | 67 | // loop through all matching filenames 68 | String path = "test_files"; 69 | String glob = "simple*ts*m062x*631+gd*.out"; 70 | try ( DirectoryStream dirStream = Files.newDirectoryStream( Paths.get(path), glob)) { 71 | for (Path p : dirStream) { 72 | // read molecule 73 | String moleculeFilename = p.toString(); 74 | System.out.printf("\n\nReading data from %s...", moleculeFilename); 75 | GaussianOutputFile frequenciesOutputFile = new GaussianOutputFile(moleculeFilename); 76 | Molecule frequenciesMolecule = frequenciesOutputFile.molecule; 77 | System.out.println("done."); 78 | String routeCard = frequenciesOutputFile.routeCard; 79 | System.out.printf("Original Route: %s\n", routeCard); 80 | routeCard = routeCard.replaceFirst("opt\\S*\\s+?","").replaceFirst("freq\\S*\\s+?","").replaceFirst("#p","#t"); 81 | routeCard = routeCard.replaceFirst("opt\\S*$","").replaceFirst("freq\\S*$",""); 82 | System.out.printf("Modified Route: %s\n", routeCard); 83 | 84 | // read normal modes 85 | NormalMode mode = frequenciesMolecule.modes.get(modeIndex); 86 | List normalModeCoordinates = mode.coordinates; 87 | 88 | // get normal mode information 89 | double reducedMass = mode.reducedMass; // amu 90 | double forceConstant = mode.forceConstant; // mDyne/A 91 | double frequency = mode.frequency; // cm^-1 92 | double ZPE = -1.0; 93 | double maxShift = -1.0; 94 | System.out.printf("Will use mode %d, which has a frequency of %.1f cm-1.\n", modeIndex+1, mode.frequency); 95 | 96 | // determine shift bounds 97 | double thisShift = 0.0; 98 | double thisMaxShift = 0.0; 99 | double thisShiftStep = 0.0; 100 | double ZPEMaxShift = 0.0; 101 | if ( frequency < 0.0 ) { 102 | thisShift = absoluteShiftMin; 103 | thisMaxShift = absoluteShiftMax; 104 | thisShiftStep = absoluteShiftInterval; 105 | } 106 | else { 107 | thisShift = relativeShiftMin; 108 | thisMaxShift = relativeShiftMax; 109 | thisShiftStep = relativeShiftInterval; 110 | ZPEMaxShift = HarmonicOscillatorDistribution.getClassicalTurningPoint(ZPE, forceConstant); // in Angstroms 111 | } 112 | 113 | // generate output file header 114 | StringBuilder outputStringBuilder = new StringBuilder(); 115 | int numberOfGeometries = 0; 116 | for (; thisShift <= thisMaxShift+0.00001; thisShift += thisShiftStep) { 117 | // calculate displacements 118 | numberOfGeometries++; 119 | List finalPositions = new ArrayList<>(frequenciesMolecule.contents.size()); 120 | for (int i=0; i < normalModeCoordinates.size(); i++) { 121 | Vector3D direction = normalModeCoordinates.get(i); 122 | Vector3D currentPosition = frequenciesMolecule.contents.get(i).position; 123 | double scalar = frequency < 0.0 ? thisShift : thisShift * ZPEMaxShift; 124 | Vector3D displacement = direction.scalarMultiply(scalar); 125 | Vector3D finalPosition = currentPosition.add(displacement); 126 | finalPositions.add(finalPosition); 127 | } 128 | 129 | boolean firstGeometry = outputStringBuilder.length() == 0; 130 | if ( ! firstGeometry ) 131 | outputStringBuilder.append("--Link1--\n"); 132 | outputStringBuilder.append("%chk=checkpoint.chk\n"); 133 | outputStringBuilder.append(String.format("%%mem=%dGB\n", memory)); 134 | outputStringBuilder.append(String.format("%%nprocshared=%d\n", processors)); 135 | if ( firstGeometry ) 136 | outputStringBuilder.append(routeCard + "\n"); 137 | else 138 | outputStringBuilder.append(routeCard + " guess=read\n"); 139 | if ( frequency < 0.0 ) 140 | outputStringBuilder.append(String.format("\ndisplacement: %.6f A\n\n", thisShift)); 141 | else 142 | outputStringBuilder.append(String.format("\ndisplacement: %.6f A\n\n", thisShift * ZPEMaxShift)); 143 | outputStringBuilder.append(String.format("%d %d\n", frequenciesMolecule.charge, frequenciesMolecule.multiplicity)); 144 | for (int i=0; i < frequenciesMolecule.contents.size(); i++) { 145 | String symbol = frequenciesMolecule.contents.get(i).symbol; 146 | double x = finalPositions.get(i).getX(); 147 | double y = finalPositions.get(i).getY(); 148 | double z = finalPositions.get(i).getZ(); 149 | outputStringBuilder.append(String.format(" %-5s %15.10f %15.10f %15.10f\n", symbol, x, y, z)); 150 | } 151 | if ( footer.trim().length() > 0 ) 152 | outputStringBuilder.append(footer + "\n"); 153 | outputStringBuilder.append("\n\n"); 154 | } 155 | 156 | // write out file 157 | String outputFilename = p.getFileName().toString().replaceFirst(".out",".gjf").replaceFirst("-ts-","-displacements-"); 158 | String outputFullFilename = String.format("%s/%s", outputDirectory, outputFilename); 159 | InputFileFormat.writeStringToDisk(outputStringBuilder.toString(),outputFullFilename); 160 | System.out.printf(">>> Wrote %d geometries to %s.\n", numberOfGeometries, outputFullFilename); 161 | } 162 | } 163 | catch (Exception e) { e.printStackTrace(); } 164 | assertTrue( true ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/test/java/edu/harvard/chemistry/ekwan/Jprogdyn/InitializerTest.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.chemistry.ekwan.Jprogdyn; 2 | 3 | import java.util.*; 4 | import java.lang.Math; 5 | import org.apache.commons.math3.geometry.euclidean.threed.*; 6 | import com.google.common.collect.*; 7 | 8 | import junit.framework.Test; 9 | import junit.framework.TestCase; 10 | import junit.framework.TestSuite; 11 | 12 | /** 13 | * This test generates thermal initializations from a file and writes the 14 | * geometries to a set of files. These files are intended as input for 15 | * other programs like Gaussian. 16 | * 17 | * The first structure will be unperturbed. 18 | * 19 | * To run: mvn -Dtest=InitializerTest test 20 | */ 21 | public class InitializerTest extends TestCase 22 | { 23 | /** 24 | * Create the test case 25 | * 26 | * @param testName name of the test case 27 | */ 28 | public InitializerTest( String testName ) 29 | { 30 | super( testName ); 31 | } 32 | 33 | /** 34 | * 35 | */ 36 | public void testInitializer() 37 | { 38 | // displacement parameters 39 | String moleculeFilename = "test_files/methane_b3lyp_midix.out"; // filename to read modes from 40 | double temperature = 298.0; // in K 41 | int numberOfInitializations = 1; // how many files to make 42 | Map specialModeInitializationMap = new HashMap<>(); // zero-indexed mode number --> initialization type 43 | //specialModeInitializationMap.put(0,Initializer.VibrationalInitializationType.NONE); // don't displace the TS mode 44 | 45 | // Gaussian parameters 46 | String outputPrefix = moleculeFilename.replace("test_files","analysis").replace(".out",""); 47 | String footer = "\n"; 48 | 49 | // read molecule 50 | System.out.println("Loading data...\n"); 51 | GaussianOutputFile frequenciesOutputFile = new GaussianOutputFile(moleculeFilename); 52 | Molecule molecule = frequenciesOutputFile.molecule; 53 | System.out.printf("Molecule read from %s:\n", moleculeFilename); 54 | System.out.println(molecule); 55 | System.out.println(); 56 | System.out.println("Normal modes:"); 57 | for (int i=0; i < molecule.modes.size(); i++) { 58 | NormalMode mode = molecule.modes.get(i); 59 | System.out.printf("Mode %4d : %.0f cm-1\n", i, mode.frequency); 60 | } 61 | 62 | // make dummy dynamics method 63 | CalculationMethod dynamicsMethod = new GaussianCalculationMethod(CalculationMethod.CalculationType.ENERGY_AND_FORCE, 64 | 3, 4, "#p", footer); 65 | 66 | // make Initializer object 67 | Initializer initializer = new Initializer(molecule, // the molecule with frequencies to initialize with 68 | temperature, // in K 69 | 1.0, // timestep in fs (irrelevant here) 70 | Initializer.VibrationalInitializationType.QUASICLASSICAL, // default vibrational initialization type 71 | Initializer.RotationalInitializationType.NONE, // no need for rotations here 72 | specialModeInitializationMap, // treat some modes differently as specified here 73 | 0.1, // harmonic tolerance in percent (irrelevant) 74 | dynamicsMethod, // dynamics method (irrelevant) 75 | 1.0); // frequency scaling factor (irrelevant) 76 | 77 | // generate input files 78 | // first structure will be unperturbed 79 | for (int i=0; i < numberOfInitializations; i++) { 80 | System.out.printf("\n>>> Iteration %3d of %3d <<<\n", i+1, numberOfInitializations); 81 | 82 | // generate a new initialization 83 | Molecule newMolecule = molecule; 84 | //if ( i>0 ) 85 | newMolecule = initializer.generateStructure(molecule); 86 | //else 87 | // System.out.println("[ Not perturbing molecule for first iteration. ]"); 88 | 89 | // write out molecule 90 | StringBuilder s = new StringBuilder(); 91 | s.append(String.format("%d %d\n", molecule.charge, molecule.multiplicity)); 92 | for (int j=0; j < molecule.contents.size(); j++) { 93 | String symbol = newMolecule.contents.get(j).symbol; 94 | double x = newMolecule.contents.get(j).position.getX(); 95 | double y = newMolecule.contents.get(j).position.getY(); 96 | double z = newMolecule.contents.get(j).position.getZ(); 97 | s.append(String.format(" %-5s %15.10f %15.10f %15.10f\n", symbol, x, y, z)); 98 | } 99 | String filename = String.format("%s-init_%03d.gjf", outputPrefix, i); 100 | InputFileFormat.writeStringToDisk(s.toString(),filename); 101 | System.out.printf("> Wrote to %s.\n\n", filename); 102 | } 103 | 104 | assertTrue( true ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tutorials/methane_NMR_analysis.config: -------------------------------------------------------------------------------- 1 | ### Jprogdyn Configuration File ### 2 | 3 | # This file controls the behavior of Jprogdyn. 4 | # By default, Jprogdyn will use "Jprogdyn.conf" as the current configuration file. 5 | # 6 | # To switch to a custom file: 7 | # mvn exec:java -Dconfig.filename="another.config" 8 | 9 | # Blank lines and comments starting with # are ignored. 10 | # Comments may be placed after any configuration value with a #. 11 | # All options must be specified on one line. 12 | 13 | 14 | # File/Directory Locations 15 | working_directory : use_current # specify full path or "use_current" to use the current working directory 16 | 17 | frequency_directory : test_files # path relative to working_directory where Jprogdyn should expect the 18 | # Gaussian output files to use for initializing trajectories 19 | 20 | frequency_file : methane_b3lyp_midix.out # the file to read in frequencies from (Gaussian output with freq=hpmodes) 21 | 22 | gaussian_directory : gaussian # path relative to working_directory in which to run Gaussian jobs 23 | # run_gaussian.sh is expected in this directory 24 | 25 | gaussian_max_filenames : 10000 # maximum number of job filenames (should not need to be changed) 26 | 27 | 28 | # Threading Options 29 | number_of_simultaneous_trajectories : 9 # how many trajectories to run simultaneously 30 | 31 | 32 | # Gaussian Options 33 | number_of_processors_per_trajectory : 4 # how many processors to use per Gaussian job 34 | 35 | memory_per_trajectory : 3 # how much RAM to use per trajectory in GB 36 | 37 | gaussian_force_route_card : b3lyp/midix pop=none # the route card to pass to Gaussian for a regular force job 38 | # Jprogdyn will add all other necessary keywords -- just put in 39 | # the level of theory/solvation 40 | 41 | gaussian_force_footer : @blank # stuff to put at the end of every force job (one line please) 42 | 43 | 44 | # Trajectory Options 45 | job_type : analysis # specify "trajectory" to run trajectories or "analysis" 46 | # to analyze existing checkpoints 47 | 48 | trajectory_type : nmr # specify "reaction" to run/analyze a reaction trajectory or 49 | # specify "nmr" to run/analyze an NMR trajectory 50 | 51 | number_of_total_trajectories : 25 # how many trajectories to run in total 52 | 53 | checkpoint_directory : checkpoints # path relative to working_directory where Jprogdyn should expect the 54 | # any existing trajectory checpoints to be (progress will also be 55 | # saved here) 56 | 57 | checkpoint_prefix : methane # all checkpoint files will have this prefix 58 | 59 | checkpoint_interval : 5 # save progress every n points, must be between 1 and 50 inclusive 60 | 61 | temperature : 298.0 # in K 62 | 63 | timestep : 1.0 # timestep in fs 64 | 65 | number_of_forward_points : 125 # how many forward points to compute 66 | 67 | number_of_backward_points : 125 # how many backward points to compute 68 | 69 | # Initialization Options 70 | maximum_number_of_initialization_attempts : 50 # try to initialize this many times per trajectory then give up 71 | # must be at least 10 72 | 73 | harmonic_tolerance : 0.01 # the maximum allowable difference between the desired and actual 74 | # energies of the initial structure in percent 75 | # must be at least 0.00001 and less than 10.0 76 | # 0.01 is a good starting value 77 | 78 | scale_factor : 1.0 # vibrational frequencies will be scaled by this factor 79 | # (1.0 recommended, which is to say no scaling) 80 | # must be between 0.5 and 1.5 81 | 82 | # draw the initial displacements from... 83 | # quasiclassical = a quantum mechanical canonical ensemble 84 | # classical = a classical mechanical canonical ensemble 85 | # uniform = a uniform classical distribution 86 | # ts_positive = zero displacement but forward velocity 87 | # ts_negative = zero displacement but negative velocity 88 | # ts_random = zero displacement but random velocity sign 89 | # none = do not displace 90 | 91 | vibrational_initialization_default : quasiclassical # all vibrational modes will be initialized this way unless specified 92 | # otherwise below 93 | 94 | vibrational_initialization_override : no_overrides # set to no_overrides or a semicolon-separated list of the form 95 | # 0:ts_positive;1:none 96 | # which means initialize mode 0 with no displacements and positive 97 | # velocity and initialize mode 1 with no displacements and no velocity 98 | # modes are 0-indexed 99 | 100 | rotational_initialization_type : classical # what kind of rotational initialization to do (classical or none) 101 | 102 | 103 | # Reaction Trajectory Termination Conditions 104 | # Note: these options will be ignored if trajectory_type is set to "nmr." 105 | # 106 | # If you want to run all points, comment out all these lines. 107 | # If you want to specify more than one termination condition, add multiple lines that start with 108 | # "termination_condition". If any condition is met, the trajectory will stop. 109 | # 110 | # To use no termination conditions, write a single line: 111 | # termination_condition : no_termination_conditions 112 | # 113 | # In this example, the first condition means to stop the trajectory if the C-F bond distance 114 | # between atoms 8 and 13 exceeds 3 A. Atom numbers are 1-indexed. 115 | # 116 | # termination_condition : bond_length, 8, 13, C-F, greater_than, 3.0 117 | # 118 | # These termination conditions will also be used to assign trajectory outcomes if 119 | # summarize_trajectories_to_screen is set to "yes". 120 | 121 | # description of fields: 122 | termination_condition : no_termination_conditions # bond_length, bond_angle, torsion 123 | # next fields = atom numbers (2, 3, or 4 fields) 124 | # description = one word 125 | # last two fields: 126 | # stop when the internal coordinate is greater_than, equal, or 127 | # less_than to the given number 128 | 129 | # NMR Calculations 130 | # Note: these options will be ignored if trajectory_type is set to "reaction". 131 | 132 | nmr_point_interval : 8 # how often in number of trajectory points to calculate NMR shieldings 133 | # (8 is recommended) 134 | 135 | shieldings_file : methane_b3lyp_midix_NMR_b3lyp_dz.out # the file containing the NMR shieldings for the stationary point 136 | # at the NMR level of theory 137 | # (expected in working_directory/frequency_directory) 138 | 139 | gaussian_nmr_route_card : b3lyp/cc-pvdz # the route card to pass to Gaussian for an NMR job 140 | # place in quotes, all one line, use \n for new lines 141 | # Jprogdyn will add all other necessary keywords -- just put in 142 | # the level of theory/solvation, don't add NMR keyword 143 | 144 | gaussian_nmr_footer : @blank # stuff to put at the end of every NMR job (one line please) 145 | 146 | symmetry_groups : @blank # list of sets of symmetry-related atoms to average shieldings over 147 | # use a semicolon to specify multiple groups 148 | 149 | 150 | # Analysis Options 151 | # Note: these options will only be processed if job_type is set to "analysis." 152 | analysis_directory : analysis # path relative to working_directory where Jprogdyn will save 153 | # MOLDEN movies to 154 | 155 | make_molden_movies : yes # if "yes" will make movies of each trajectory and save it to 156 | # analysis_directory (any existing movies will be overwritten) 157 | # files will be named checkpoint_filename.traj 158 | 159 | summarize_trajectories_to_screen : yes # if "yes" parse the list of analysis_coordinates below, 160 | # analyze each trajectory, and print the report to the screen 161 | 162 | summary_interval : 20 # when printing out a report of the trajectories to the screen, 163 | # how often to print out updates, given in trajectory points 164 | # recommended: 20 165 | 166 | # place multiple analysis_coordinate entries on separate lines 167 | # to use no analysis coordinates, write a single line: 168 | # analysis_coordinate : no_analysis_coordinates 169 | # description of fields: 170 | analysis_coordinate : bond_length, 1, 2, C-H2 # bond_length, bond_angle, torsion 171 | analysis_coordinate : bond_angle, 2, 1, 3, H2-C-H3 # next fields = atom numbers (2, 3, or 4 fields) 172 | # description = one word 173 | 174 | write_analysis_csv : yes # if "yes", write a comma-separated file containing the 175 | # above coordinates as a function of time for each trajectory 176 | # to a analysis_directory/checkpoint_filename.dat 177 | # (this means one csv file per trajectory) 178 | -------------------------------------------------------------------------------- /tutorials/methane_NMR_trajectories.config: -------------------------------------------------------------------------------- 1 | ### Jprogdyn Configuration File ### 2 | 3 | # This file controls the behavior of Jprogdyn. 4 | # By default, Jprogdyn will use "Jprogdyn.conf" as the current configuration file. 5 | # 6 | # To switch to a custom file: 7 | # mvn exec:java -Dconfig.filename="another.config" 8 | 9 | # Blank lines and comments starting with # are ignored. 10 | # Comments may be placed after any configuration value with a #. 11 | # All options must be specified on one line. 12 | 13 | 14 | # File/Directory Locations 15 | working_directory : use_current # specify full path or "use_current" to use the current working directory 16 | 17 | frequency_directory : test_files # path relative to working_directory where Jprogdyn should expect the 18 | # Gaussian output files to use for initializing trajectories 19 | 20 | frequency_file : methane_b3lyp_midix.out # the file to read in frequencies from (Gaussian output with freq=hpmodes) 21 | 22 | gaussian_directory : gaussian # path relative to working_directory in which to run Gaussian jobs 23 | # run_gaussian.sh is expected in this directory 24 | 25 | gaussian_max_filenames : 10000 # maximum number of job filenames (should not need to be changed) 26 | 27 | 28 | # Threading Options 29 | number_of_simultaneous_trajectories : 9 # how many trajectories to run simultaneously 30 | 31 | 32 | # Gaussian Options 33 | number_of_processors_per_trajectory : 4 # how many processors to use per Gaussian job 34 | 35 | memory_per_trajectory : 3 # how much RAM to use per trajectory in GB 36 | 37 | gaussian_force_route_card : b3lyp/midix pop=none # the route card to pass to Gaussian for a regular force job 38 | # Jprogdyn will add all other necessary keywords -- just put in 39 | # the level of theory/solvation 40 | 41 | gaussian_force_footer : @blank # stuff to put at the end of every force job (one line please) 42 | 43 | 44 | # Trajectory Options 45 | job_type : trajectory # specify "trajectory" to run trajectories or "analysis" 46 | # to analyze existing checkpoints 47 | 48 | trajectory_type : nmr # specify "reaction" to run/analyze a reaction trajectory or 49 | # specify "nmr" to run/analyze an NMR trajectory 50 | 51 | number_of_total_trajectories : 25 # how many trajectories to run in total 52 | 53 | checkpoint_directory : checkpoints # path relative to working_directory where Jprogdyn should expect the 54 | # any existing trajectory checpoints to be (progress will also be 55 | # saved here) 56 | 57 | checkpoint_prefix : methane # all checkpoint files will have this prefix 58 | 59 | checkpoint_interval : 5 # save progress every n points, must be between 1 and 50 inclusive 60 | 61 | temperature : 298.0 # in K 62 | 63 | timestep : 1.0 # timestep in fs 64 | 65 | number_of_forward_points : 125 # how many forward points to compute 66 | 67 | number_of_backward_points : 125 # how many backward points to compute 68 | 69 | # Initialization Options 70 | maximum_number_of_initialization_attempts : 50 # try to initialize this many times per trajectory then give up 71 | # must be at least 10 72 | 73 | harmonic_tolerance : 0.01 # the maximum allowable difference between the desired and actual 74 | # energies of the initial structure in percent 75 | # must be at least 0.00001 and less than 10.0 76 | # 0.01 is a good starting value 77 | 78 | scale_factor : 1.0 # vibrational frequencies will be scaled by this factor 79 | # (1.0 recommended, which is to say no scaling) 80 | # must be between 0.5 and 1.5 81 | 82 | # draw the initial displacements from... 83 | # quasiclassical = a quantum mechanical canonical ensemble 84 | # classical = a classical mechanical canonical ensemble 85 | # uniform = a uniform classical distribution 86 | # ts_positive = zero displacement but forward velocity 87 | # ts_negative = zero displacement but negative velocity 88 | # ts_random = zero displacement but random velocity sign 89 | # none = do not displace 90 | 91 | vibrational_initialization_default : quasiclassical # all vibrational modes will be initialized this way unless specified 92 | # otherwise below 93 | 94 | vibrational_initialization_override : no_overrides # set to no_overrides or a semicolon-separated list of the form 95 | # 0:ts_positive;1:none 96 | # which means initialize mode 0 with no displacements and positive 97 | # velocity and initialize mode 1 with no displacements and no velocity 98 | # modes are 0-indexed 99 | 100 | rotational_initialization_type : classical # what kind of rotational initialization to do (classical or none) 101 | 102 | 103 | # Reaction Trajectory Termination Conditions 104 | # Note: these options will be ignored if trajectory_type is set to "nmr." 105 | # 106 | # If you want to run all points, comment out all these lines. 107 | # If you want to specify more than one termination condition, add multiple lines that start with 108 | # "termination_condition". If any condition is met, the trajectory will stop. 109 | # 110 | # To use no termination conditions, write a single line: 111 | # termination_condition : no_termination_conditions 112 | # 113 | # In this example, the first condition means to stop the trajectory if the C-F bond distance 114 | # between atoms 8 and 13 exceeds 3 A. Atom numbers are 1-indexed. 115 | # 116 | # termination_condition : bond_length, 8, 13, C-F, greater_than, 3.0 117 | # 118 | # These termination conditions will also be used to assign trajectory outcomes if 119 | # summarize_trajectories_to_screen is set to "yes". 120 | 121 | # description of fields: 122 | termination_condition : no_termination_conditions # bond_length, bond_angle, torsion 123 | # next fields = atom numbers (2, 3, or 4 fields) 124 | # description = one word 125 | # last two fields: 126 | # stop when the internal coordinate is greater_than, equal, or 127 | # less_than to the given number 128 | 129 | 130 | # NMR Calculations 131 | # Note: these options will be ignored if trajectory_type is set to "reaction". 132 | 133 | nmr_point_interval : 8 # how often in number of trajectory points to calculate NMR shieldings 134 | # (8 is recommended) 135 | 136 | shieldings_file : methane_b3lyp_midix_NMR_b3lyp_dz.out # the file containing the NMR shieldings for the stationary point 137 | # at the NMR level of theory 138 | # (expected in working_directory/frequency_directory) 139 | 140 | gaussian_nmr_route_card : b3lyp/cc-pvdz # the route card to pass to Gaussian for an NMR job 141 | # place in quotes, all one line, use \n for new lines 142 | # Jprogdyn will add all other necessary keywords -- just put in 143 | # the level of theory/solvation, don't add NMR keyword 144 | 145 | gaussian_nmr_footer : @blank # stuff to put at the end of every NMR job (one line please) 146 | 147 | symmetry_groups : @blank # list of sets of symmetry-related atoms to average shieldings over 148 | # use a semicolon to specify multiple groups 149 | 150 | 151 | # Analysis Options 152 | # Note: these options will only be processed if job_type is set to "analysis." 153 | analysis_directory : analysis # path relative to working_directory where Jprogdyn will save 154 | # MOLDEN movies to 155 | 156 | make_molden_movies : yes # if "yes" will make movies of each trajectory and save it to 157 | # analysis_directory (any existing movies will be overwritten) 158 | # files will be named checkpoint_filename.traj 159 | 160 | summarize_trajectories_to_screen : yes # if "yes" parse the list of analysis_coordinates below, 161 | # analyze each trajectory, and print the report to the screen 162 | 163 | summary_interval : 20 # when printing out a report of the trajectories to the screen, 164 | # how often to print out updates, given in trajectory points 165 | # recommended: 20 166 | 167 | # place multiple analysis_coordinate entries on separate lines 168 | # to use no analysis coordinates, write a single line: 169 | # analysis_coordinate : no_analysis_coordinates 170 | # description of fields: 171 | analysis_coordinate : bond_length, 1, 2, C-H2 # bond_length, bond_angle, torsion 172 | analysis_coordinate : bond_angle, 2, 1, 3, H2-C-H3 # next fields = atom numbers (2, 3, or 4 fields) 173 | # description = one word 174 | 175 | write_analysis_csv : yes # if "yes", write a comma-separated file containing the 176 | # above coordinates as a function of time for each trajectory 177 | # to a analysis_directory/checkpoint_filename.dat 178 | # (this means one csv file per trajectory) 179 | -------------------------------------------------------------------------------- /tutorials/reaction_tutorial_analysis.config: -------------------------------------------------------------------------------- 1 | ### Jprogdyn Configuration File ### 2 | 3 | # This file controls the behavior of Jprogdyn. 4 | # By default, Jprogdyn will use "Jprogdyn.conf" as the current configuration file. 5 | # 6 | # To switch to a custom file: 7 | # mvn exec:java -Dconfig.filename="another.config" 8 | 9 | # Blank lines and comments starting with # are ignored. 10 | # Comments may be placed after any configuration value with a #. 11 | # All options must be specified on one line. 12 | 13 | 14 | # File/Directory Locations 15 | working_directory : use_current # specify full path or "use_current" to use the current working directory 16 | 17 | frequency_directory : test_files # path relative to working_directory where Jprogdyn should expect the 18 | # Gaussian output files to use for initializing trajectories 19 | 20 | frequency_file : dinitro_Cl_F-1H2O-ts-b3lyp_d3bj-631+gd-dmf_pcm.out # the file to read in frequencies from (Gaussian output with freq=hpmodes) 21 | 22 | gaussian_directory : gaussian # path relative to working_directory in which to run Gaussian jobs 23 | # run_gaussian.sh is expected in this directory 24 | 25 | gaussian_max_filenames : 10000 # maximum number of job filenames (should not need to be changed) 26 | 27 | 28 | # Threading Options 29 | number_of_simultaneous_trajectories : 4 # how many trajectories to run simultaneously 30 | 31 | 32 | # Gaussian Options 33 | number_of_processors_per_trajectory : 9 # how many processors to use per Gaussian job 34 | 35 | memory_per_trajectory : 24 # how much RAM to use per trajectory in GB 36 | 37 | # the route card to pass to Gaussian for a regular force job 38 | gaussian_force_route_card : b3lyp scrf=(solvent=n,n-dimethylformamide,pcm) empiricaldispersion=gd3bj 6-31+g* pop=none 39 | # Jprogdyn will add all other necessary keywords -- just put in 40 | # the level of theory/solvation 41 | 42 | gaussian_force_footer : @blank # stuff to put at the end of every force job (one line please) 43 | 44 | 45 | # Trajectory Options 46 | job_type : analysis # specify "trajectory" to run trajectories or "analysis" 47 | # to analyze existing checkpoints 48 | 49 | trajectory_type : reaction # specify "reaction" to run/analyze a reaction trajectory or 50 | # specify "nmr" to run/analyze an NMR trajectory 51 | 52 | number_of_total_trajectories : 4 # how many trajectories to run in total 53 | 54 | checkpoint_directory : checkpoints # path relative to working_directory where Jprogdyn should expect the 55 | # any existing trajectory checpoints to be (progress will also be 56 | # saved here) 57 | 58 | checkpoint_prefix : SNAr_tutorial # all checkpoint files will have this prefix 59 | 60 | checkpoint_interval : 5 # save progress every n points, must be between 1 and 50 inclusive 61 | 62 | temperature : 298.0 # in K 63 | 64 | timestep : 1.0 # timestep in fs 65 | 66 | number_of_forward_points : 500 # how many forward points to compute 67 | 68 | number_of_backward_points : 500 # how many backward points to compute 69 | 70 | # Initialization Options 71 | maximum_number_of_initialization_attempts : 50 # try to initialize this many times per trajectory then give up 72 | # must be at least 10 73 | 74 | harmonic_tolerance : 0.0005 # the maximum allowable difference between the desired and actual 75 | # energies of the initial structure in percent 76 | # must be at least 0.00001 and less than 10.0 77 | # 0.01 is a good starting value 78 | 79 | scale_factor : 1.0 # vibrational frequencies will be scaled by this factor 80 | # (1.0 recommended, which is to say no scaling) 81 | # must be between 0.5 and 1.5 82 | 83 | # draw the initial displacements from... 84 | # quasiclassical = a quantum mechanical canonical ensemble 85 | # classical = a classical mechanical canonical ensemble 86 | # uniform = a uniform classical distribution 87 | # ts_positive = zero displacement but forward velocity 88 | # ts_negative = zero displacement but negative velocity 89 | # ts_random = zero displacement but random velocity sign 90 | # none = do not displace 91 | 92 | vibrational_initialization_default : quasiclassical # all vibrational modes will be initialized this way unless specified 93 | # otherwise below 94 | 95 | vibrational_initialization_override : 0:ts_positive # set to no_overrides or a semicolon-separated list of the form 96 | # 0:ts_positive;1:none 97 | # which means initialize mode 0 with no displacements and positive 98 | # velocity and initialize mode 1 with no displacements and no velocity 99 | # modes are 0-indexed 100 | 101 | rotational_initialization_type : classical # what kind of rotational initialization to do (classical or none) 102 | 103 | 104 | # Reaction Trajectory Termination Conditions 105 | # Note: these options will be ignored if trajectory_type is set to "nmr." 106 | # 107 | # If you want to run all points, comment out all these lines. 108 | # If you want to specify more than one termination condition, add multiple lines that start with 109 | # "termination_condition". If any condition is met, the trajectory will stop. 110 | # 111 | # To use no termination conditions, write a single line: 112 | # termination_condition : no_termination_conditions 113 | # 114 | # In this example, the first condition means to stop the trajectory if the C-F bond distance 115 | # between atoms 8 and 13 exceeds 3 A. Atom numbers are 1-indexed. 116 | # 117 | # termination_condition : bond_length, 8, 13, C-F, greater_than, 3.0 118 | # 119 | # These termination conditions will also be used to assign trajectory outcomes if 120 | # summarize_trajectories_to_screen is set to "yes". 121 | 122 | termination_condition : bond_length, 8, 13, starting_material, greater_than, 3.0 # description of fields: 123 | termination_condition : bond_length, 8, 17, product, greater_than, 4.0 # bond_length, bond_angle, torsion 124 | # next fields = atom numbers (2, 3, or 4 fields) 125 | # description = one word 126 | # last two fields: 127 | # stop when the internal coordinate is greater_than, equal, or 128 | # less_than to the given number 129 | 130 | # NMR Calculations 131 | # Note: these options will be ignored if trajectory_type is set to "reaction". 132 | 133 | nmr_point_interval : 8 # how often in number of trajectory points to calculate NMR shieldings 134 | # (8 is recommended) 135 | 136 | shieldings_file : @blank # the file containing the NMR shieldings for the stationary point 137 | # at the NMR level of theory 138 | # (expected in working_directory/frequency_directory) 139 | 140 | gaussian_nmr_route_card : b3lyp/cc-pvdz # the route card to pass to Gaussian for an NMR job 141 | # place in quotes, all one line, use \n for new lines 142 | # Jprogdyn will add all other necessary keywords -- just put in 143 | # the level of theory/solvation, don't add NMR keyword 144 | 145 | gaussian_nmr_footer : @blank # stuff to put at the end of every NMR job (one line please) 146 | 147 | symmetry_groups : @blank # list of sets of symmetry-related atoms to average shieldings over 148 | # use a semicolon to specify multiple groups 149 | 150 | 151 | # Analysis Options 152 | # Note: these options will only be processed if job_type is set to "analysis." 153 | analysis_directory : analysis # path relative to working_directory where Jprogdyn will save 154 | # MOLDEN movies to 155 | 156 | make_molden_movies : yes # if "yes" will make movies of each trajectory and save it to 157 | # analysis_directory (any existing movies will be overwritten) 158 | # files will be named checkpoint_filename.traj 159 | 160 | summarize_trajectories_to_screen : yes # if "yes" parse the list of analysis_coordinates below, 161 | # analyze each trajectory, and print the report to the screen 162 | 163 | summary_interval : 100 # when printing out a report of the trajectories to the screen, 164 | # how often to print out updates, given in trajectory points 165 | # recommended: 20 166 | 167 | # place multiple analysis_coordinate entries on separate lines 168 | # to use no analysis coordinates, write a single line: 169 | # analysis_coordinate : no_analysis_coordinates 170 | # description of fields: 171 | analysis_coordinate : bond_length, 8, 13, C-F # bond_length, bond_angle, torsion 172 | analysis_coordinate : bond_length, 8, 17, C-Cl # next fields = atom numbers (2, 3, or 4 fields) 173 | # description = one word 174 | 175 | write_analysis_csv : yes # if "yes", write a comma-separated file containing the 176 | # above coordinates as a function of time for each trajectory 177 | # to a analysis_directory/checkpoint_filename.dat 178 | # (this means one csv file per trajectory) 179 | -------------------------------------------------------------------------------- /tutorials/reaction_tutorial_trajectories.config: -------------------------------------------------------------------------------- 1 | ### Jprogdyn Configuration File ### 2 | 3 | # This file controls the behavior of Jprogdyn. 4 | # By default, Jprogdyn will use "Jprogdyn.conf" as the current configuration file. 5 | # 6 | # To switch to a custom file: 7 | # mvn exec:java -Dconfig.filename="another.config" 8 | 9 | # Blank lines and comments starting with # are ignored. 10 | # Comments may be placed after any configuration value with a #. 11 | # All options must be specified on one line. 12 | 13 | 14 | # File/Directory Locations 15 | working_directory : use_current # specify full path or "use_current" to use the current working directory 16 | 17 | frequency_directory : test_files # path relative to working_directory where Jprogdyn should expect the 18 | # Gaussian output files to use for initializing trajectories 19 | 20 | frequency_file : dinitro_Cl_F-1H2O-ts-b3lyp_d3bj-631+gd-dmf_pcm.out # the file to read in frequencies from (Gaussian output with freq=hpmodes) 21 | 22 | gaussian_directory : gaussian # path relative to working_directory in which to run Gaussian jobs 23 | # run_gaussian.sh is expected in this directory 24 | 25 | gaussian_max_filenames : 10000 # maximum number of job filenames (should not need to be changed) 26 | 27 | 28 | # Threading Options 29 | number_of_simultaneous_trajectories : 4 # how many trajectories to run simultaneously 30 | 31 | 32 | # Gaussian Options 33 | number_of_processors_per_trajectory : 9 # how many processors to use per Gaussian job 34 | 35 | memory_per_trajectory : 24 # how much RAM to use per trajectory in GB 36 | 37 | # the route card to pass to Gaussian for a regular force job 38 | gaussian_force_route_card : b3lyp scrf=(solvent=n,n-dimethylformamide,pcm) empiricaldispersion=gd3bj 6-31+g* pop=none 39 | # Jprogdyn will add all other necessary keywords -- just put in 40 | # the level of theory/solvation 41 | 42 | gaussian_force_footer : @blank # stuff to put at the end of every force job (one line please) 43 | 44 | 45 | # Trajectory Options 46 | job_type : trajectory # specify "trajectory" to run trajectories or "analysis" 47 | # to analyze existing checkpoints 48 | 49 | trajectory_type : reaction # specify "reaction" to run/analyze a reaction trajectory or 50 | # specify "nmr" to run/analyze an NMR trajectory 51 | 52 | number_of_total_trajectories : 4 # how many trajectories to run in total 53 | 54 | checkpoint_directory : checkpoints # path relative to working_directory where Jprogdyn should expect the 55 | # any existing trajectory checpoints to be (progress will also be 56 | # saved here) 57 | 58 | checkpoint_prefix : SNAr_tutorial # all checkpoint files will have this prefix 59 | 60 | checkpoint_interval : 5 # save progress every n points, must be between 1 and 50 inclusive 61 | 62 | temperature : 298.0 # in K 63 | 64 | timestep : 1.0 # timestep in fs 65 | 66 | number_of_forward_points : 500 # how many forward points to compute 67 | 68 | number_of_backward_points : 500 # how many backward points to compute 69 | 70 | # Initialization Options 71 | maximum_number_of_initialization_attempts : 50 # try to initialize this many times per trajectory then give up 72 | # must be at least 10 73 | 74 | harmonic_tolerance : 0.0005 # the maximum allowable difference between the desired and actual 75 | # energies of the initial structure in percent 76 | # must be at least 0.00001 and less than 10.0 77 | # 0.01 is a good starting value 78 | 79 | scale_factor : 1.0 # vibrational frequencies will be scaled by this factor 80 | # (1.0 recommended, which is to say no scaling) 81 | # must be between 0.5 and 1.5 82 | 83 | # draw the initial displacements from... 84 | # quasiclassical = a quantum mechanical canonical ensemble 85 | # classical = a classical mechanical canonical ensemble 86 | # uniform = a uniform classical distribution 87 | # ts_positive = zero displacement but forward velocity 88 | # ts_negative = zero displacement but negative velocity 89 | # ts_random = zero displacement but random velocity sign 90 | # none = do not displace 91 | 92 | vibrational_initialization_default : quasiclassical # all vibrational modes will be initialized this way unless specified 93 | # otherwise below 94 | 95 | vibrational_initialization_override : 0:ts_positive # set to no_overrides or a semicolon-separated list of the form 96 | # 0:ts_positive;1:none 97 | # which means initialize mode 0 with no displacements and positive 98 | # velocity and initialize mode 1 with no displacements and no velocity 99 | # modes are 0-indexed 100 | 101 | rotational_initialization_type : classical # what kind of rotational initialization to do (classical or none) 102 | 103 | 104 | # Reaction Trajectory Termination Conditions 105 | # Note: these options will be ignored if trajectory_type is set to "nmr." 106 | # 107 | # If you want to run all points, comment out all these lines. 108 | # If you want to specify more than one termination condition, add multiple lines that start with 109 | # "termination_condition". If any condition is met, the trajectory will stop. 110 | # 111 | # To use no termination conditions, write a single line: 112 | # termination_condition : no_termination_conditions 113 | # 114 | # In this example, the first condition means to stop the trajectory if the C-F bond distance 115 | # between atoms 8 and 13 exceeds 3 A. Atom numbers are 1-indexed. 116 | # 117 | # termination_condition : bond_length, 8, 13, C-F, greater_than, 3.0 118 | # 119 | # These termination conditions will also be used to assign trajectory outcomes if 120 | # summarize_trajectories_to_screen is set to "yes". 121 | 122 | # description of fields: 123 | termination_condition : no_termination_conditions # bond_length, bond_angle, torsion 124 | # next fields = atom numbers (2, 3, or 4 fields) 125 | # description = one word 126 | # last two fields: 127 | # stop when the internal coordinate is greater_than, equal, or 128 | # less_than to the given number 129 | 130 | # NMR Calculations 131 | # Note: these options will be ignored if trajectory_type is set to "reaction". 132 | 133 | nmr_point_interval : 8 # how often in number of trajectory points to calculate NMR shieldings 134 | # (8 is recommended) 135 | 136 | shieldings_file : @blank # the file containing the NMR shieldings for the stationary point 137 | # at the NMR level of theory 138 | # (expected in working_directory/frequency_directory) 139 | 140 | gaussian_nmr_route_card : b3lyp/cc-pvdz # the route card to pass to Gaussian for an NMR job 141 | # place in quotes, all one line, use \n for new lines 142 | # Jprogdyn will add all other necessary keywords -- just put in 143 | # the level of theory/solvation, don't add NMR keyword 144 | 145 | gaussian_nmr_footer : @blank # stuff to put at the end of every NMR job (one line please) 146 | 147 | symmetry_groups : @blank # list of sets of symmetry-related atoms to average shieldings over 148 | # use a semicolon to specify multiple groups 149 | 150 | 151 | # Analysis Options 152 | # Note: these options will only be processed if job_type is set to "analysis." 153 | analysis_directory : analysis # path relative to working_directory where Jprogdyn will save 154 | # MOLDEN movies to 155 | 156 | make_molden_movies : yes # if "yes" will make movies of each trajectory and save it to 157 | # analysis_directory (any existing movies will be overwritten) 158 | # files will be named checkpoint_filename.traj 159 | 160 | summarize_trajectories_to_screen : yes # if "yes" parse the list of analysis_coordinates below, 161 | # analyze each trajectory, and print the report to the screen 162 | 163 | summary_interval : 100 # when printing out a report of the trajectories to the screen, 164 | # how often to print out updates, given in trajectory points 165 | # recommended: 20 166 | 167 | # place multiple analysis_coordinate entries on separate lines 168 | # to use no analysis coordinates, write a single line: 169 | # analysis_coordinate : no_analysis_coordinates 170 | # description of fields: 171 | analysis_coordinate : bond_length, 8, 13, C-F # bond_length, bond_angle, torsion 172 | analysis_coordinate : bond_length, 8, 17, C-Cl # next fields = atom numbers (2, 3, or 4 fields) 173 | # description = one word 174 | 175 | write_analysis_csv : yes # if "yes", write a comma-separated file containing the 176 | # above coordinates as a function of time for each trajectory 177 | # to a analysis_directory/checkpoint_filename.dat 178 | # (this means one csv file per trajectory) 179 | --------------------------------------------------------------------------------