├── analysis
└── placeholder
├── checkpoints
└── placeholder
├── img
├── reaction.png
├── velocity_verlet_velocity.svg
└── velocity_verlet_position.svg
├── src
├── main
│ └── java
│ │ └── edu
│ │ └── harvard
│ │ └── chemistry
│ │ └── ekwan
│ │ └── Jprogdyn
│ │ ├── FileFormat.java
│ │ ├── Singleton.java
│ │ ├── Immutable.java
│ │ ├── package-info.java
│ │ ├── Propagator.java
│ │ ├── Units.java
│ │ ├── Element.java
│ │ ├── CalculationMethod.java
│ │ ├── InputFileFormat.java
│ │ ├── GaussianCalculationMethod.java
│ │ ├── GaussianResult.java
│ │ ├── TrajectoryExecutorService.java
│ │ ├── NormalMode.java
│ │ ├── Atom.java
│ │ ├── AsciiBar.java
│ │ ├── OutputFileFormat.java
│ │ ├── GaussianInputFile.java
│ │ ├── GaussianJob.java
│ │ ├── Propagators.java
│ │ ├── Molecule.java
│ │ ├── InternalCoordinate.java
│ │ ├── NMRTrajectoryAnalyzer.java
│ │ ├── HarmonicOscillatorDistribution.java
│ │ ├── TrajectoryPoint.java
│ │ └── RotationalBoltzmann.java
└── test
│ └── java
│ └── edu
│ └── harvard
│ └── chemistry
│ └── ekwan
│ └── Jprogdyn
│ ├── InitializerTest.java
│ └── HarmonicTestGaussian.java
├── .gitignore
├── gaussian
└── run_gaussian.sh
├── pom.xml
├── LICENSE.txt
└── tutorials
├── methane_NMR_analysis.config
├── methane_NMR_trajectories.config
├── reaction_tutorial_analysis.config
└── reaction_tutorial_trajectories.config
/analysis/placeholder:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/checkpoints/placeholder:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/reaction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekwan/Jprogdyn/HEAD/img/reaction.png
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/img/velocity_verlet_velocity.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/img/velocity_verlet_position.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------