├── .gitignore
├── DETAILS.md
├── LICENSE.txt
├── README.md
├── img
├── claisen_scheme.png
└── claisen_table4.png
├── scripts
├── SNIP.md
├── pyquiver.ipynb
├── skodgetruhlar.ipynb
└── snip.awk
├── src
├── AUTOQUIVER.md
├── autoquiver.py
├── config.py
├── constants.py
├── kie.py
├── orca.py
├── quiver.py
├── settings.py
├── utility.py
└── weights.dat
└── tutorial
├── TUTORIAL.md
├── gaussian
├── claisen_demo.config
├── claisen_gs.out
└── claisen_ts.out
├── orca
├── claisen_demo.config
├── claisen_gs.xyz
├── claisen_gs_freq.hess
├── claisen_gs_freq.inp
├── claisen_gs_freq.out
├── claisen_ts.xyz
├── claisen_ts_freq.hess
├── claisen_ts_freq.inp
└── claisen_ts_freq.out
└── pyquiver
├── claisen_demo.config
├── claisen_gs.qin
└── claisen_ts.qin
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *#*
3 | .ipynb_checkpoints/*
4 | *.pyc
5 | *.csv
6 | *DS_Store*
7 | .ipynb_checkpoints
8 |
--------------------------------------------------------------------------------
/DETAILS.md:
--------------------------------------------------------------------------------
1 | ## Technical Details
2 |
3 | - [Compatibility with QUIVER](#compatibility-with-quiver)
4 | - [How to Invoke PyQuiver](#how-to-invoke-pyquiver)
5 | - [`.config` Files](#config-files)
6 | - [Input Files](#input-files)
7 | - [PyQuiver Standard](#pyquiver-standard)
8 | - [Miscellaneous Notes](#miscellaneous-notes)
9 | - [References](#references)
10 |
11 | ### Compatibility with QUIVER
12 |
13 | The development of *PyQuiver* was inspired by the [original](#ref2) Fortran program [QUIVER](https://github.com/ekwan/quiver). *PyQuiver* is designed to be as compatible as possible with the original QUIVER program, but to clarify some ambiguity in choice of masses, configuration files need to be updated for use with *PyQuiver*. See the [Configuration](#config-files) section for detail.
14 |
15 | ### How to Invoke PyQuiver
16 |
17 | *PyQuiver* can be controlled from the command line or its Python API.
18 |
19 | To run *PyQuiver* from the command line, issue the following command from the `src/` directory:
20 |
21 | ```
22 | python quiver.py config_file ground_state_file transition_state_file
23 | ```
24 |
25 | For more details, run `python quiver.py -h` to display the following help message:
26 |
27 | ```
28 | usage: quiver.py [-h] [-v] [-s STYLE] config gs ts
29 |
30 | PyQuiver calculates KIEs and EIEs based on a ground and transition state file.
31 |
32 | positional arguments:
33 | config configuration file path
34 | gs ground state file path
35 | ts transition state file path
36 |
37 | optional arguments:
38 | -h, --help show this help message and exit
39 | -v, --verbose when the verbose flag is set debug information is
40 | printed
41 | -s STYLE, --style STYLE
42 | style of input files (g09, orca, or pyquiver)
43 | ```
44 |
45 | This command will calculate the KIEs or EIEs associated with the isotopic substitutions specified in the configuration file. For details, see the tutorial above.
46 |
47 | *PyQuiver* also has a Python API. The advantage of this interface is that it exposes the underlying Python objects. This allows you to run custom calculations, automate routine calculations, and inspect the internal data directly.
48 |
49 | An IPython Notebook is provided as a demonstration. To try it out, move to the `scripts/` directory and run the command `ipython notebook`. Then open the `pyquiver.ipynb` notebook file. An IPython Notebook `skodgetruhlar.ipynb` is also provided to calculate KIEs corrected with the Skodge Truhlar tunnelling equation. (see ref. 4)
50 |
51 | ### .config Files
52 |
53 | Calculations performed in *PyQuiver* require a configuration file to specify the parameters (such as scaling factor and temperature) and isotopologue substitution rules.
54 |
55 | Each configuration file is a plain text file with the following properties:
56 |
57 | * blank lines and lines starting with `#` are ignored
58 | * anything after `#` within a line is assumed to be a comment
59 | * fields in directives are separated by spaces.
60 |
61 | Valid configuration files have all of the following directives:
62 |
63 | * `scaling`: a linear factor by which to scale the frequencies
64 | * `imag_threshold`: the threshold (in units cm^-1) that defines the cutoff between small and large imaginary frequencies used to distinguish EIE from KIE calculations (typical value: 50)
65 | * `temperature`: the temperature in Kelvin
66 | * `reference_isoto[pomer/logue]`: possible values are `none` or the name of an isotopologue. If `none` is specified, the absolute KIEs will be reported. If the name of an isotopologue is specified, all KIEs will be divided the KIE values for this isotopologue.
67 | * `mass_override_isot[pomer/logue]`: possible values are "default" or the name of an isotopologue. If the value "default" is specified, the masses of the light isotopologue will be the defaults found in `weights.dat`. If the name of an isotopolgoue is given, then that isotopologue is then used to replace the default mass behaviour of PyQuiver at a particular atom. For example, if the isotopomer `C2` replaces carbon 2 with 13C, then specifying `mass_overide_isotopomer C2` will place carbon-13 at C2 *for every KIE calculation*.
68 | * `isoto[pomer/logue]`: the rule used for isotopic substitution. The expected fields are `name ground_state_atom_number transition_state_atom_number substitution`. The final field, `substitution` must correspond to a valid substitution weight. These weights are specified in `weights.dat` (e.g., `13C`, `18O`, `2D`).
69 |
70 | ### Input Files
71 |
72 | *PyQuiver* assumes that the ground state file and transition state file are the outputs of a Gaussian09 `freq` job. To change this assumption *PyQuiver* can accept an additional command-line argument corresponding to the input file style.
73 |
74 | Currently, *PyQuiver* can automatically read output files from the following formats:
75 | * Gaussian 2009. Style name: `g09` (This also works with Gaussian 2016.)
76 | * ORCA. Style name: `orca` (Reads the `.hess` files generated by any frequency job.)
77 | * PyQuiver Standard. Style name: `pyquiver`
78 |
79 | To specify a format other than `g09` from the command line, run with the `-s` flag. For instance:
80 |
81 | ```
82 | python quiver.py -s pyquiver ../tutorial/pyquiver/claisen_demo.config ../tutorial/pyquiver/claisen_gs.qin ../pyquiver/claisen_ts.qin
83 | ```
84 |
85 | would execute the example *PyQuiver* job on the Claisen system using the *PyQuiver* standard input files. This allows you to adapt PyQuiver to other electronic structure programs. (We would be pleased to offer some advice on how to accomplish this.)
86 |
87 | ### PyQuiver Standard
88 |
89 | The *PyQuiver* Standard is a generic format for the output of an electronic structure program in plain-text outlined as follows:
90 |
91 | * The first line of a file contains the number of atoms (*n*).
92 | * The next *n* lines define the geometry. Each line should be of the form:
93 |
94 | ```
95 | CenterNumber,AtomicNumber,XPosition,YPosition,ZPosition
96 | ```
97 |
98 | The positions should be provided in units of Angstroms. The center number is the atom index (ranging from 0 and *n-1*, inclusive).
99 | * The next line should contain the lower triangular Cartesian Hessian matrix with no line breaks. In particular, if *H* is the Hessian matrix then *H3p+i,3q+j* corresponds to taking derivatives with respect to atom *p* moving in the *i*-th coordinate and atom *q* moving in the *j*-th coordinate (*i* and *j* run across the three cartesian coordinates). The entries in the serialized form should be separated by commas. The serialization should occur by stringing together the rows (truncated at the main diagonal). For example, suppose the following is the lower-right triangular form of the Cartesian Hessian for a one atom system:
100 |
101 | ```
102 | 1.0
103 | 2.0 3.0
104 | 4.0 5.0 6.0
105 | ```
106 |
107 | then the *PyQuiver* would expect the following line:
108 |
109 | ```
110 | 1.0,2.0,3.0,4.0,5.0,6.0
111 | ```
112 |
113 | * Example *PyQuiver* standard input files are available in the `tutorial/pyquiver/` directory. The files `claisen_gs.qin` and `claisen_ts.qin` are *PyQuiver* input files corresponding to the example Claisen system discussed in the tutorial.
114 |
115 | If input files are provided in a known format other than the *PyQuiver* standard, *PyQuiver* can dump the appropriate *PyQuiver* input files. To do this load the appropriate system (ex. `gs = System("./ground_state.g09")`) and then run `gs.dump_pyquiver_input_file()` which will create the appropriate input file at the same path with the extension `.qin`.
116 |
117 | ### Miscellaneous Notes
118 |
119 | The internal workings of *PyQuiver* are relatively simple. Essentially, a KIE calculation involves parsing the Hessian, mass-weighting, diagonalization with `np.eigvalsh`, calculation of the reduced isotopic partition functions, substitution into the Bigeleisen-Mayer equation, and tunnelling corrections. The tunnelling corrections are expected to work well for heavy atoms, but poorly for hydrogen, especially when considering primary KIEs.
120 |
121 | The frequencies can optionally be scaled (see ref. 3), but this will probably not help much (the harmonic approximation is usually quite adequate). Note that *PyQuiver* is recognizees linear vs. non-linear molecules and removes the appropriate number of small translational/rotational modes. (The `frequency_threshold` keyword has been deprecated and is now ignored.)
122 |
123 | The performance of *PyQuiver* is generally excellent, even for large systems. This is largely because of the efficiency of the `np.eighvalsh` implementation. Note that when multiple isotopomers are calculated using the same configuration file, *PyQuiver* will recalculate the frequencies for the reference isotopomer repeatedly (i.e., once for every isotopomer). This should not be relevant for routine use. However, it could be avoided by using the *PyQuiver* API.
124 |
125 | There is a known issue with getting PyQuiver to work on Cygwin systems due to a problem processing file paths correctly. Additionally it seems to be hard to get NumPy installed properly on such systems. We are working on a fix--please contact me if you require this urgently. (The program works properly on all other kinds unix/linux, as far as we know).
126 |
127 | Please note that the verbose flag should be set in the Gaussian route card. For example:
128 | ` #p b3lyp 6-31g* freq=noraman`
129 | This will ensure that Gaussian places the Hessian in the archive string at the end of the file. (Not including the verbose flag will cause an error.)
130 |
131 | Tunnelling corrections work best for heavy-atom KIEs. H/D KIEs are more challenging (see the work of the Singleton and Truhlar groups for more details).
132 |
133 | ### References
134 |
135 | 1. **Bigeleisen-Mayer theory:**
136 | * Bigeleisen, J.; Mayer, M.G. *J. Chem. Phys.* **1947**, *15*, 261.
137 | * Wolfsberg, M. *Acc. Chem. Res.*, **1972**, *5*, 225.
138 | * Wolfsberg, M. *et al.* Isotope Effects in the Chemical, Geological, and Bio Sciences
139 | 2. **QUIVER:**
140 | * Saunders, M.; Laidig, K.E. Wolfsberg, M. *J. Am. Chem. Soc.*, **1988**, *111*, 8989.
141 | 3. **Scaling Factors:**
142 | * Wong *et al.* *Chem. Phys. Lett.* **1996**, *256*, 391-399.
143 | * Radom *et al.* *J Phys. Chem.* **1996**, *100*, 16502.
144 | 4. **Tunnelling Corrections:**
145 | * Bell, R.P. *Chem. Soc. Rev.* **1974**, *3*, 513.
146 | * Skodge, R.T.; Truhlar, D.G. *J. Phys. Chem.* **1981**, *85*, 624-628
147 | 5. **Claisen Rearragenent KIEs:**
148 | * Meyer, M.P.; DelMonte, A.J.; Singleton, D.A. *J. Am. Chem. Soc.*, **1999**, *121*, 10865-10874.
149 |
--------------------------------------------------------------------------------
/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 | Copyright 2016 Thayer L. Anderson and Eugene E. Kwan
179 |
180 | Licensed under the Apache License, Version 2.0 (the "License");
181 | you may not use this file except in compliance with the License.
182 | You may obtain a copy of the License at
183 |
184 | http://www.apache.org/licenses/LICENSE-2.0
185 |
186 | Unless required by applicable law or agreed to in writing, software
187 | distributed under the License is distributed on an "AS IS" BASIS,
188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189 | See the License for the specific language governing permissions and
190 | limitations under the License.
191 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # *PyQuiver*
2 |
3 | *A user-friendly program for calculating isotope effects.*
4 |
5 | *PyQuiver* is an open-source Python program for calculating kinetic isotope effects (KIEs) and equilibrium isotope effects (EIEs) using harmonic frequencies and the Bigeleisen-Mayer equation. *PyQuiver* requires Cartesian Hessian matrices, which can be calculated using any electronic structure program.
6 |
7 | ### Features
8 |
9 | * automatically read frequencies from [`Gaussian`](http://www.gaussian.com/g_prod/g09.htm) and [`ORCA`](https://orcaforum.kofo.mpg.de/app.php/portal) output files
10 | * excellent performance for larger systems
11 | * custom isotopic substitutions and arbitrary temperature
12 | * tunnelling corrections: Wigner and Bell inverted parabola
13 | * run via command line or simple Python API
14 |
15 | ### Installation
16 |
17 | *PyQuiver* requires Python 3 and [`numpy`](https://numpy.org). No other libraries are necessary.
18 |
19 | 1. Install [Python](https://www.continuum.io/downloads) if necesary. The standard Anaconda distribution will contain the necessary dependencies.
20 | 2. Install [git](https://git-scm.com/downloads). git comes pre-installed on most Linux distributions and Macs.
21 | 3. Clone the repository: `git clone https://github.com/ekwan/PyQuiver.git`. (If you don't want to deal with `git`, click on the green "clone or download" button on this github repository page and click on "Download ZIP" to receive an archive.)
22 |
23 | *PyQuiver* has been tested on PC, Mac, and Linux platforms.
24 |
25 | ## Tutorial
26 |
27 | To learn how to use *PyQuiver*, please look at the [tutorial](tutorial/TUTORIAL.md).
28 |
29 | ## Further Reading
30 |
31 | * [Technical Details](DETAILS.md) (compatibility with QUIVER, how to invoke PyQuiver, `.config` files, input files, the PyQuiver Standard format, miscellaneous notes)
32 | * [AutoQuiver](src/AUTOQUIVER.md) (how to run PyQuiver on many related files)
33 | * [Snipping Utility](scripts/SNIP.md) (how to "snip" out only the relevant parts of a Gaussian output file for publication or archiving purposes)
34 |
35 | ## Authors
36 |
37 | *PyQuiver* was written by Thayer Anderson and Eugene Kwan. Please email `ekwan16@gmail.com` with any questions. We will gladly try to help you.
38 |
39 | We thank Gregor Giesen for writing the code to read ORCA output. We thank Corin Wagen for miscellaneous optimization. We also thank Christoph Riplinger and Giovanni Bistoni for valuable discussions.
40 |
41 | ## How to Cite
42 |
43 | Anderson, T.L.; Kwan, E.E. *PyQuiver* **2020**, `www.github.com/ekwan/PyQuiver`
44 |
45 | ## License
46 |
47 | This project is licensed under the Apache License, Version 2.0. See `LICENSE.txt` for full terms and conditions.
48 |
49 | *Copyright 2020 by Thayer L. Anderson and Eugene E. Kwan*
50 |
--------------------------------------------------------------------------------
/img/claisen_scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekwan/PyQuiver/1e01d59d4ee54fdce5c1719ad74eeea651a00867/img/claisen_scheme.png
--------------------------------------------------------------------------------
/img/claisen_table4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ekwan/PyQuiver/1e01d59d4ee54fdce5c1719ad74eeea651a00867/img/claisen_table4.png
--------------------------------------------------------------------------------
/scripts/SNIP.md:
--------------------------------------------------------------------------------
1 | ### Snipping Utility
2 |
3 | The AWK script `snip.awk` is provided to convert Gaussian `.out` files into `.snip` files. These "snips" are contain only the geometry, energies, and frequencies, but retain enough of the Gaussian format for PyQuiver to read them. This feature is useful if you have many files and want to "compress" them to save room.
4 |
5 | To convert a collection of `.out` files to their corresponding `.snip` files:
6 |
7 | `awk -f snip.awk *.out`
8 |
9 | Note that this will overwrite any existing snips with the same name without warning. (`awk` is a standard scripting langauge available on any platform.)
10 |
--------------------------------------------------------------------------------
/scripts/pyquiver.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# *PyQuiver*\n",
8 | "This is an IPython Notebook interface for the *PyQuiver* package. The code below will guide you through using *PyQuiver* through a native Python interface. The same steps could be reproduced in the Python interpreter or in a `.py` file."
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": 1,
14 | "metadata": {
15 | "collapsed": false
16 | },
17 | "outputs": [
18 | {
19 | "name": "stdout",
20 | "output_type": "stream",
21 | "text": [
22 | "Read atomic weight data for 32 elements.\n"
23 | ]
24 | }
25 | ],
26 | "source": [
27 | "# import the necessary package elements\n",
28 | "import numpy as np\n",
29 | "import sys\n",
30 | "sys.path.append(\"../src\")\n",
31 | "from kie import KIE_Calculation"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "## A Simple Calculation\n",
39 | "\n",
40 | "We can reproduce the command-line interface by creating a KIE_Calculation object:"
41 | ]
42 | },
43 | {
44 | "cell_type": "code",
45 | "execution_count": 2,
46 | "metadata": {
47 | "collapsed": false
48 | },
49 | "outputs": [
50 | {
51 | "name": "stdout",
52 | "output_type": "stream",
53 | "text": [
54 | "\n",
55 | "Reading configuration from ../tutorial/gaussian/claisen_demo.config\n",
56 | "Reading data from ../tutorial/gaussian/claisen_gs.out... with style g09\n",
57 | "Molecule is not linear.\n",
58 | "Reading data from ../tutorial/gaussian/claisen_ts.out... with style g09\n",
59 | "Molecule is not linear.\n",
60 | "Config file: ../tutorial/gaussian/claisen_demo.config\n",
61 | "Temperature: 393.0 K\n",
62 | "Scaling: 0.961\n",
63 | "Reference Isotopologue: C5\n",
64 | "Imag threshold (cm-1): 50\n",
65 | " Isotopologue C5, replacement 1: replace gs atom 5 and ts atom 5 with 13C\n",
66 | " Isotopologue C1, replacement 1: replace gs atom 1 and ts atom 1 with 13C\n",
67 | " Isotopologue C2, replacement 1: replace gs atom 2 and ts atom 2 with 13C\n",
68 | " Isotopologue C4, replacement 1: replace gs atom 4 and ts atom 4 with 13C\n",
69 | " Isotopologue C6, replacement 1: replace gs atom 6 and ts atom 6 with 13C\n",
70 | " Isotopologue H/D, replacement 1: replace gs atom 7 and ts atom 7 with 2D\n",
71 | " Isotopologue H/D, replacement 2: replace gs atom 8 and ts atom 8 with 2D\n",
72 | " Isotopologue O3, replacement 1: replace gs atom 3 and ts atom 3 with 17O\n"
73 | ]
74 | }
75 | ],
76 | "source": [
77 | "claisen_calculation = KIE_Calculation(\"../tutorial/gaussian/claisen_demo.config\", \"../tutorial/gaussian/claisen_gs.out\", \"../tutorial/gaussian/claisen_ts.out\", style=\"g09\")"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "To print the KIEs:"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": 3,
90 | "metadata": {
91 | "collapsed": false
92 | },
93 | "outputs": [
94 | {
95 | "name": "stdout",
96 | "output_type": "stream",
97 | "text": [
98 | "The claisen_calculation object:\n",
99 | "\n",
100 | "=== PyQuiver Analysis ===\n",
101 | "Isotopologue uncorrected Wigner inverted parabola\n",
102 | " KIE KIE KIE\n",
103 | "Isotopologue C1 1.0108 1.0125 1.0128 \n",
104 | "Isotopologue C2 1.0000 1.0000 1.0000 \n",
105 | "Isotopologue O3 1.0169 1.0184 1.0187 \n",
106 | "Isotopologue C4 1.0278 1.0305 1.0310 \n",
107 | "Isotopologue C6 1.0129 1.0146 1.0149 \n",
108 | "Isotopologue H/D 0.9530 0.9545 0.9547 \n",
109 | "\n",
110 | "KIEs referenced to isotopologue C5, whose absolute KIEs are:\n",
111 | "Isotopologue C5 1.0019 1.0019 1.0019 \n"
112 | ]
113 | }
114 | ],
115 | "source": [
116 | "print(\"The claisen_calculation object:\")\n",
117 | "print(claisen_calculation)"
118 | ]
119 | },
120 | {
121 | "cell_type": "markdown",
122 | "metadata": {},
123 | "source": [
124 | "We can also access the KIEs directly via the `KIE` dictionary belonging to our `KIE_Calculation`. This can be useful for automating KIE analyses over a large number of files. This functionality is further developed in the `autoquiver` routine (see below)."
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": 4,
130 | "metadata": {
131 | "collapsed": false
132 | },
133 | "outputs": [
134 | {
135 | "name": "stdout",
136 | "output_type": "stream",
137 | "text": [
138 | "Iterating through the KIEs dictionary:\n",
139 | "Isotopologue C1 1.0108 1.0125 1.0128 \n",
140 | "\n",
141 | "C1\n",
142 | "[1.01083044 1.0124856 1.01277832]\n"
143 | ]
144 | }
145 | ],
146 | "source": [
147 | "print(\"Iterating through the KIEs dictionary:\")\n",
148 | "for name,kie_object in claisen_calculation.KIES.items():\n",
149 | " # pretty print the underlying KIE object:\n",
150 | " print(kie_object)\n",
151 | " # or pull the name and value directly:\n",
152 | " print(type(kie_object))\n",
153 | " print(kie_object.name)\n",
154 | " print(kie_object.value)\n",
155 | " break"
156 | ]
157 | },
158 | {
159 | "cell_type": "markdown",
160 | "metadata": {},
161 | "source": [
162 | "## `System` objects\n",
163 | "\n",
164 | "We didn't have to specify file paths as the targets of our `KIE_Calculation` constructor. Instead, we can work directly with `System` objects, which contain the geometry and Hessian as fields. Below, we load the Claisen ground state and transition state and print the position of the first atom in the ground state and print the first row of the transition state Hessian."
165 | ]
166 | },
167 | {
168 | "cell_type": "code",
169 | "execution_count": 5,
170 | "metadata": {
171 | "collapsed": false
172 | },
173 | "outputs": [
174 | {
175 | "name": "stdout",
176 | "output_type": "stream",
177 | "text": [
178 | "Reading data from ../tutorial/gaussian/claisen_gs.out... with style g09\n",
179 | "Molecule is not linear.\n",
180 | "Reading data from ../tutorial/gaussian/claisen_ts.out... with style g09\n",
181 | "Molecule is not linear.\n",
182 | "Position of atom 0 in the ground state:\n",
183 | "[ 4.77979210e+00 6.82814755e-01 -3.77945233e-05]\n",
184 | "First row of the Carteisan transition state Hessian:\n",
185 | "[ 2.5705678e-01 -2.3007491e-01 -1.6192179e-01 -7.6561780e-02\n",
186 | " 8.4893130e-02 4.6126500e-03 2.1299140e-02 2.0270870e-02\n",
187 | " -9.6391200e-03 -2.2122500e-02 -1.9531000e-02 2.2995300e-03\n",
188 | " -2.5246920e-02 3.7391980e-02 3.6582000e-04 4.6899040e-02\n",
189 | " -4.4453100e-03 2.2383900e-03 -1.5100128e-01 1.2099792e-01\n",
190 | " 8.6293350e-02 -4.7478530e-02 3.2266700e-03 7.3894740e-02\n",
191 | " 6.5812100e-03 -8.0738400e-03 5.6873800e-03 2.4898000e-04\n",
192 | " -2.9655000e-04 2.0240000e-04 1.2257700e-03 3.9790000e-04\n",
193 | " 4.2782000e-04 7.7369000e-04 2.6580000e-05 1.3262000e-04\n",
194 | " -4.6166200e-03 -1.5256600e-03 -2.7586400e-03 -7.0569600e-03\n",
195 | " -3.2577800e-03 -1.8351500e-03]\n"
196 | ]
197 | }
198 | ],
199 | "source": [
200 | "from quiver import System\n",
201 | "\n",
202 | "gs = System(\"../tutorial/gaussian/claisen_gs.out\")\n",
203 | "ts = System(\"../tutorial/gaussian/claisen_ts.out\")\n",
204 | "print(\"Position of atom 0 in the ground state:\")\n",
205 | "print(gs.positions[0])\n",
206 | "print(\"First row of the Carteisan transition state Hessian:\")\n",
207 | "print(ts.hessian[0])"
208 | ]
209 | },
210 | {
211 | "cell_type": "markdown",
212 | "metadata": {},
213 | "source": [
214 | "### Running calculations with `System` objects\n",
215 | "\n",
216 | "To run a KIE calculation with two `System` objects, we simply pass them into the relevant fields of a `KIE_Calculation` object:"
217 | ]
218 | },
219 | {
220 | "cell_type": "code",
221 | "execution_count": 6,
222 | "metadata": {
223 | "collapsed": false
224 | },
225 | "outputs": [
226 | {
227 | "name": "stdout",
228 | "output_type": "stream",
229 | "text": [
230 | "\n",
231 | "Reading configuration from ../tutorial/gaussian/claisen_demo.config\n",
232 | "Config file: ../tutorial/gaussian/claisen_demo.config\n",
233 | "Temperature: 393.0 K\n",
234 | "Scaling: 0.961\n",
235 | "Reference Isotopologue: C5\n",
236 | "Imag threshold (cm-1): 50\n",
237 | " Isotopologue C5, replacement 1: replace gs atom 5 and ts atom 5 with 13C\n",
238 | " Isotopologue C1, replacement 1: replace gs atom 1 and ts atom 1 with 13C\n",
239 | " Isotopologue C2, replacement 1: replace gs atom 2 and ts atom 2 with 13C\n",
240 | " Isotopologue C4, replacement 1: replace gs atom 4 and ts atom 4 with 13C\n",
241 | " Isotopologue C6, replacement 1: replace gs atom 6 and ts atom 6 with 13C\n",
242 | " Isotopologue H/D, replacement 1: replace gs atom 7 and ts atom 7 with 2D\n",
243 | " Isotopologue H/D, replacement 2: replace gs atom 8 and ts atom 8 with 2D\n",
244 | " Isotopologue O3, replacement 1: replace gs atom 3 and ts atom 3 with 17O\n",
245 | "\n",
246 | "=== PyQuiver Analysis ===\n",
247 | "Isotopologue uncorrected Wigner inverted parabola\n",
248 | " KIE KIE KIE\n",
249 | "Isotopologue C1 1.0108 1.0125 1.0128 \n",
250 | "Isotopologue C2 1.0000 1.0000 1.0000 \n",
251 | "Isotopologue O3 1.0169 1.0184 1.0187 \n",
252 | "Isotopologue C4 1.0278 1.0305 1.0310 \n",
253 | "Isotopologue C6 1.0129 1.0146 1.0149 \n",
254 | "Isotopologue H/D 0.9530 0.9545 0.9547 \n",
255 | "\n",
256 | "KIEs referenced to isotopologue C5, whose absolute KIEs are:\n",
257 | "Isotopologue C5 1.0019 1.0019 1.0019 \n"
258 | ]
259 | }
260 | ],
261 | "source": [
262 | "claisen_calculation2 = KIE_Calculation(\"../tutorial/gaussian/claisen_demo.config\", gs, ts)\n",
263 | "print(claisen_calculation2)"
264 | ]
265 | },
266 | {
267 | "cell_type": "markdown",
268 | "metadata": {},
269 | "source": [
270 | "## Building `Isotopologue`s\n",
271 | "Once we have access to the underlying `System` objects, it is easy to make substituted `Isotopologue`s and perform frequency calculations ourselves. To make an `Isotopologue` we need to provide a name, a corresponding `System`, and a list of masses - one for each atom in the `System`.\n",
272 | "\n",
273 | "Let's build the default light ground state `Isotopologue`:"
274 | ]
275 | },
276 | {
277 | "cell_type": "code",
278 | "execution_count": 7,
279 | "metadata": {
280 | "collapsed": false
281 | },
282 | "outputs": [
283 | {
284 | "name": "stdout",
285 | "output_type": "stream",
286 | "text": [
287 | "Isotopologue: the light ground state\n",
288 | " masses: [12.0, 12.0, 15.9949146, 12.0, 12.0, 12.0, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825]\n"
289 | ]
290 | }
291 | ],
292 | "source": [
293 | "from quiver import Isotopologue\n",
294 | "from constants import DEFAULT_MASSES\n",
295 | "\n",
296 | "# we build the default masses by using the DEFAULT_MASSES dictionary which maps from atomic numbers to masses\n",
297 | "# default masses in DEFAULT_MASSES are the average atomic weight of each element\n",
298 | "gs_masses = [DEFAULT_MASSES[z] for z in gs.atomic_numbers]\n",
299 | "\n",
300 | "gs_light = Isotopologue(\"the light ground state\", gs, gs_masses)\n",
301 | "print(gs_light)"
302 | ]
303 | },
304 | {
305 | "cell_type": "markdown",
306 | "metadata": {},
307 | "source": [
308 | "### `Isotopologue`s with substitutions\n",
309 | "Now that we know how to make `Isotopologue`s it's very easy to specify isotopic substitutions. Let's put the mysterious isotope carbon-5000 at atom 4:"
310 | ]
311 | },
312 | {
313 | "cell_type": "code",
314 | "execution_count": 8,
315 | "metadata": {
316 | "collapsed": false
317 | },
318 | "outputs": [],
319 | "source": [
320 | "# make a copy of gs_masses\n",
321 | "sub_masses = list(gs_masses)\n",
322 | "# index 3 corresponds to atom number 4\n",
323 | "sub_masses[3] = 5000.0\n",
324 | "\n",
325 | "gs_heavy = Isotopologue(\"the super heavy ground state\", gs, sub_masses)"
326 | ]
327 | },
328 | {
329 | "cell_type": "markdown",
330 | "metadata": {},
331 | "source": [
332 | "### Calculating Reduced Isotopic Partition Function Ratios\n",
333 | "\n",
334 | "KIEs are essentially ratios of reduced isotopic partition functions (RPFRs). To calculate these, we use the function `calculate_rpfr` from the `kie` module. This function takes a tuple of the form `(light, heavy)`, a frequency threshold, a scaling factor, and a temperature. (All of these are discussed in detail in the README.)\n",
335 | "\n",
336 | "`calculate_rpfr` returns four values in a tuple: the first value is the RPFR, the second value is a ratio of large imaginary frequencies (if present), the third value is the frequencies in the light isotopomer, and the fourth value is the frequencies in the heavy isotopomer.\n",
337 | "\n",
338 | "To print the individual contributions to the RPFR, set `quiver.DEBUG = True` in `quiver.py` and restart the kernel. "
339 | ]
340 | },
341 | {
342 | "cell_type": "code",
343 | "execution_count": 9,
344 | "metadata": {
345 | "collapsed": false
346 | },
347 | "outputs": [
348 | {
349 | "name": "stdout",
350 | "output_type": "stream",
351 | "text": [
352 | "11.208123665562292\n",
353 | "[ 61.631434 131.33940975 149.25478244 235.05384691 253.61000919\n",
354 | " 388.68321807 538.23713827 573.1098416 689.54514791 693.15891096\n",
355 | " 713.75792653 824.87106694 863.15186782 950.01325243 1000.01764911\n",
356 | " 1000.40305289 1034.40340406 1075.63192671 1229.08835092 1266.54977859\n",
357 | " 1298.58236833 1332.35577789 1376.57176191 1456.12070386 1466.42176654\n",
358 | " 1501.64974966 1712.50915119 1738.46586076 2871.66940343 2922.94354739\n",
359 | " 3159.73242676 3181.30165023 3195.17469476 3209.76123403 3263.87810693\n",
360 | " 3278.47225297]\n"
361 | ]
362 | }
363 | ],
364 | "source": [
365 | "from kie import calculate_rpfr\n",
366 | "gs_rpfr, gs_imag_ratio, gs_light_freqs, gs_heavy_freqs = calculate_rpfr((gs_light, gs_heavy), 50.0, 1.0, 273)\n",
367 | "print(gs_rpfr)\n",
368 | "print(gs_light_freqs)"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "## Calculating KIEs from Isotopologues\n",
376 | "It's nice to be able to calculate RPFRs by hand, but there is also an object available to calculate KIEs automatically: the `KIE` class.\n",
377 | "\n",
378 | "We need to create a `KIE` object by passing it a pair of ground states (light and heavy) and a pair of transition states (light and heavy) as well as some temperature information.\n",
379 | "\n",
380 | "We will make the desired substitution at Carbon 4 in the transition state and use a `KIE` object to calculate the KIE."
381 | ]
382 | },
383 | {
384 | "cell_type": "code",
385 | "execution_count": 10,
386 | "metadata": {
387 | "collapsed": false
388 | },
389 | "outputs": [
390 | {
391 | "name": "stdout",
392 | "output_type": "stream",
393 | "text": [
394 | "Isotopologue: the light transition state\n",
395 | " masses: [12.0, 12.0, 15.9949146, 12.0, 12.0, 12.0, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825]\n",
396 | "\n",
397 | "Isotopologue: the heavy transition state\n",
398 | " masses: [12.0, 12.0, 15.9949146, 5000.0, 12.0, 12.0, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825, 1.007825]\n"
399 | ]
400 | }
401 | ],
402 | "source": [
403 | "ts_masses = [DEFAULT_MASSES[z] for z in gs.atomic_numbers]\n",
404 | "ts_light = Isotopologue(\"the light transition state\", ts, ts_masses)\n",
405 | "print(ts_light)\n",
406 | "print()\n",
407 | "ts_sub_masses = list(ts_masses)\n",
408 | "ts_sub_masses[3] = 5000.0\n",
409 | "ts_heavy = Isotopologue(\"the heavy transition state\", ts, ts_sub_masses)\n",
410 | "print(ts_heavy)\n",
411 | "\n",
412 | "gs_tuple = (gs_light, gs_heavy)\n",
413 | "ts_tuple = (ts_light, ts_heavy)\n",
414 | "\n",
415 | "from kie import KIE\n",
416 | "\n",
417 | "# we make a KIE object using the gs and ts tuples from above for a calculation at 273 degrees K, with a scaling factor of 1.0, and a frequency threshold of 50 cm^-1\n",
418 | "carbon5000_kie = KIE(\"Carbon 5000 at C4 KIE\", gs_tuple, ts_tuple, 273, 1.0, 50.0)"
419 | ]
420 | },
421 | {
422 | "cell_type": "markdown",
423 | "metadata": {},
424 | "source": [
425 | "### Displaying KIEs\n",
426 | "Now, just like before, we have access to a KIE object (earlier we pulled them from the KIES dictionary). These can be printed prettily or you can take the value directly. As expected, we have large normal isotope effects because we used a carbon-5000 nucleus! (The three numbers correspond to the uncorrected, Wigner, and Bell KIEs.)"
427 | ]
428 | },
429 | {
430 | "cell_type": "code",
431 | "execution_count": 11,
432 | "metadata": {
433 | "collapsed": false
434 | },
435 | "outputs": [
436 | {
437 | "name": "stdout",
438 | "output_type": "stream",
439 | "text": [
440 | "Isotopologue Carbon 5000 at C4 KIE 2.1324 2.3449 2.4192 \n",
441 | "[2.13240475 2.34485535 2.41923705]\n"
442 | ]
443 | }
444 | ],
445 | "source": [
446 | "print(carbon5000_kie)\n",
447 | "print(carbon5000_kie.value)"
448 | ]
449 | },
450 | {
451 | "cell_type": "markdown",
452 | "metadata": {},
453 | "source": [
454 | "## *PyQuiver* with multiple files: `autoquiver.py`\n",
455 | "A common problem when computing KIEs is the need to screen a large number of levels of theory with numerous ground and transition state files. So far, we have only seen how to make a `KIE_Calculation` object that corresponds to a single configuration, ground state, and transition state.\n",
456 | "\n",
457 | "The `autoquiver` module implements functionality to perform screening over a large number of files. Its command line use is described in depth in the README. Here we will see how to leverage Python to have more sophisticated uses for `autoquiver`. Suppose we have the following files that we want to use as inputs to `PyQuiver`:\n",
458 | "\n",
459 | "```\n",
460 | "config.config\n",
461 | "\n",
462 | "ground_state1.out ts-1.out\n",
463 | "gs1rotomer.out tsrotomer1.out\n",
464 | "GROUNDSTATE2.out transition_state2.out\n",
465 | "gs3.out ts3.out\n",
466 | "```\n",
467 | "\n",
468 | "We want to run *PyQuiver* on pair of files above in the same row using the configuration file `config.config`. The command line functionality of `autoquiver` is great when the file names have a consistent pattern, but here it's not obvious how to generate the proper pairings or detect which files are ground or transition states."
469 | ]
470 | },
471 | {
472 | "cell_type": "markdown",
473 | "metadata": {},
474 | "source": [
475 | "### The Arguments\n",
476 | "The function `autoquiver` (which is the only object available in the `autoquiver` module) requires the following arguments:\n",
477 | "\n",
478 | "* `filepath`: this is the target directory.\n",
479 | "* `config_path`: the location of the configuration file to use for all *PyQuiver* calculations\n",
480 | "* `input_extension`: the file extension for ground and transition state files (default=`.out`)\n",
481 | "* `style`: the style of the ground and transition state files (default=`g09`)\n",
482 | "\n",
483 | "In the README, we discussed the command:\n",
484 | "\n",
485 | "```\n",
486 | "python src/autoquiver.py -e .output auto/ auto/substitutions.config gs ts -\n",
487 | "```\n",
488 | "\n",
489 | "The strings \"gs\" and \"ts\" were used to find ground and transition files. A ground state file needed to have \"gs\" as a substring and likewise for \"gs\" and transition state files. The character \"-\" was used as a field delimiter and \"gs\" and \"ts\" matched if all fields after the first were identical.\n",
490 | "\n",
491 | "That is some sensible default behaviour, but for more complex cases, we can provide the following functions:\n",
492 | "\n",
493 | "* `gs_p`: a function that takes a filename and returns a boolean value. `True` if the filename corresponds to a ground state and `False` if it does not.\n",
494 | "* `ts_p`: a function that takes a filename and returns a boolean value. `True` if the filename corresponds to a transition state and `False` if it does not.\n",
495 | "* `gs_ts_match_p`: a function that takes a ground state filename and a transition state filename and returns a boolean value. `True` if the ground state and transition state match and `Fase` if they do not.\n",
496 | "\n",
497 | "#### `gs_p` and `ts_p`\n",
498 | "For the previous example, we need to detect that all of the following are ground state files:\n",
499 | "\n",
500 | "```\n",
501 | "ground_state1.out\n",
502 | "gs1_rotomer.out\n",
503 | "GROUNDSTATE2.out\n",
504 | "gs3.out\n",
505 | "```\n",
506 | "\n",
507 | "We can accomplish this with the following Python function:"
508 | ]
509 | },
510 | {
511 | "cell_type": "code",
512 | "execution_count": 12,
513 | "metadata": {
514 | "collapsed": false
515 | },
516 | "outputs": [],
517 | "source": [
518 | "def gs_p(filename):\n",
519 | " # convert to lowercase\n",
520 | " filename = filename.lower()\n",
521 | " # remove the _ character\n",
522 | " filename = filename.replace(\"_\", \"\")\n",
523 | " # replace groundstate with gs\n",
524 | " filename = filename.replace(\"groundstate\", \"gs\")\n",
525 | " # check if the modified filename begins with the string \"gs\"\n",
526 | " if filename[:2] == \"gs\":\n",
527 | " return True\n",
528 | " else:\n",
529 | " return False"
530 | ]
531 | },
532 | {
533 | "cell_type": "markdown",
534 | "metadata": {},
535 | "source": [
536 | "Let's test this on all the filenames in the example:"
537 | ]
538 | },
539 | {
540 | "cell_type": "code",
541 | "execution_count": 13,
542 | "metadata": {
543 | "collapsed": false
544 | },
545 | "outputs": [
546 | {
547 | "name": "stdout",
548 | "output_type": "stream",
549 | "text": [
550 | "Checking ground state matches:\n",
551 | "True\n",
552 | "True\n",
553 | "True\n",
554 | "True\n",
555 | "Checking transition state matches:\n",
556 | "False\n",
557 | "False\n",
558 | "False\n",
559 | "False\n"
560 | ]
561 | }
562 | ],
563 | "source": [
564 | "gs_filenames = [\"ground_state1.out\", \"gs1_rotomer.out\", \"GROUNDSTATE2.out\", \"gs3.out\"]\n",
565 | "ts_filenames = [\"ts-1.out\", \"tsrotomer1.out\", \"transition_state2.out\", \"ts3.out\"]\n",
566 | "\n",
567 | "print(\"Checking ground state matches:\")\n",
568 | "for n in gs_filenames:\n",
569 | " print(gs_p(n))\n",
570 | "print(\"Checking transition state matches:\")\n",
571 | "for n in ts_filenames:\n",
572 | " print(gs_p(n))"
573 | ]
574 | },
575 | {
576 | "cell_type": "markdown",
577 | "metadata": {},
578 | "source": [
579 | "Lo and behold! The function successfully separated the ground state and transition state files. We can similarly implement `ts_p`:"
580 | ]
581 | },
582 | {
583 | "cell_type": "code",
584 | "execution_count": 14,
585 | "metadata": {
586 | "collapsed": true
587 | },
588 | "outputs": [],
589 | "source": [
590 | "def ts_p(filename):\n",
591 | " # convert to lowercase\n",
592 | " filename = filename.lower()\n",
593 | " # remove the _ character\n",
594 | " filename = filename.replace(\"_\", \"\")\n",
595 | " # replace groundstate with gs\n",
596 | " filename = filename.replace(\"transitionstate\", \"ts\")\n",
597 | " # check if the modified filename begins with the string \"gs\"\n",
598 | " if filename[:2] == \"ts\":\n",
599 | " return True\n",
600 | " else:\n",
601 | " return False"
602 | ]
603 | },
604 | {
605 | "cell_type": "markdown",
606 | "metadata": {},
607 | "source": [
608 | "#### `gs_ts_match_p`\n",
609 | "Now all we have to do is write a function `gs_ts_match_p` that can take two filenames (one ground state and one transition state) and detect if they are matches. There are numerous ways to accomplish this. We will use a basic regular expression to highlight a common method for writing these functions."
610 | ]
611 | },
612 | {
613 | "cell_type": "code",
614 | "execution_count": 15,
615 | "metadata": {
616 | "collapsed": false
617 | },
618 | "outputs": [
619 | {
620 | "name": "stdout",
621 | "output_type": "stream",
622 | "text": [
623 | "Match: ground_state1.out ts-1.out\n",
624 | "Match: ground_state1.out tsrotomer1.out\n",
625 | "Match: gs1_rotomer.out ts-1.out\n",
626 | "Match: gs1_rotomer.out tsrotomer1.out\n",
627 | "Match: GROUNDSTATE2.out transition_state2.out\n",
628 | "Match: gs3.out ts3.out\n"
629 | ]
630 | }
631 | ],
632 | "source": [
633 | "gs_filenames = [\"ground_state1.out\", \"gs1_rotomer.out\", \"GROUNDSTATE2.out\", \"gs3.out\"]\n",
634 | "ts_filenames = [\"ts-1.out\", \"tsrotomer1.out\", \"transition_state2.out\", \"ts3.out\"]\n",
635 | "\n",
636 | "import re\n",
637 | "def gs_ts_p(gs_name, ts_name):\n",
638 | " # a regular expression that finds and extracts the first integer substring in a filename\n",
639 | " gs_match = re.search(\".*([0-9]+).*\", gs_name)\n",
640 | " gs_number = gs_match.group(1)\n",
641 | " \n",
642 | " ts_match = re.search(\".*([0-9]+).*\", ts_name)\n",
643 | " ts_number = ts_match.group(1)\n",
644 | " \n",
645 | " if ts_number == gs_number:\n",
646 | " return True\n",
647 | " else:\n",
648 | " return False\n",
649 | " \n",
650 | "import itertools\n",
651 | "for gs_n, ts_n in itertools.product(gs_filenames, ts_filenames):\n",
652 | " if gs_ts_p(gs_n, ts_n):\n",
653 | " print(\"Match: {0} {1}\".format(gs_n, ts_n))"
654 | ]
655 | },
656 | {
657 | "cell_type": "code",
658 | "execution_count": 16,
659 | "metadata": {
660 | "collapsed": true
661 | },
662 | "outputs": [],
663 | "source": [
664 | "# missing example of how to actually use autoquiver"
665 | ]
666 | }
667 | ],
668 | "metadata": {
669 | "kernelspec": {
670 | "display_name": "python_3_7",
671 | "language": "python",
672 | "name": "python_3_7"
673 | },
674 | "language_info": {
675 | "codemirror_mode": {
676 | "name": "ipython",
677 | "version": 3
678 | },
679 | "file_extension": ".py",
680 | "mimetype": "text/x-python",
681 | "name": "python",
682 | "nbconvert_exporter": "python",
683 | "pygments_lexer": "ipython3",
684 | "version": "3.7.1"
685 | }
686 | },
687 | "nbformat": 4,
688 | "nbformat_minor": 2
689 | }
690 |
--------------------------------------------------------------------------------
/scripts/skodgetruhlar.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# *Skodge Truhlar KIE correction*\n",
9 | "This is an IPython Notebook interface to calculate tunnelling corrections via the Skodge Truhlar equations (*J. Phys. Chem.* **1981**, 85, 624-626.) using the *PyQuiver* and *cctk* packages. The code below will guide you through using *PyQuiver* and *cctk* through a native Python interface. The same steps could be reproduced in the Python interpreter or in a `.py` file.\n",
10 | "\n",
11 | "The initial KIE calculation requires hessians for the transition state and the starting material. The Skodge Truhlar equation additionally requires the energies of each SM/PR/TS (single point electronic energies). For Gaussian calculations this will require only the output files for the transition state and pre-transition state calculations. For Orca calculations the .hess and .out files are necessary"
12 | ]
13 | },
14 | {
15 | "attachments": {},
16 | "cell_type": "markdown",
17 | "metadata": {},
18 | "source": [
19 | "### Setup\n",
20 | "Import necessary package elements.\n",
21 | "sys.path.append should point to the src directory of PyQuiver. Note: Paths for all operating systems (including Windows) should use '/'\n",
22 | "\n",
23 | "cctk can be installed via \"pip install cctk\" or from github (ekwan/cctk)"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "import numpy as np\n",
33 | "import sys\n",
34 | "sys.path.append(\"../src\")\n",
35 | "from kie import KIE_Calculation\n",
36 | "import cctk\n",
37 | "\n",
38 | "\"\"\"Constants are imported from PHYSICAL_CONSTANTS (PyQuiver/src)\n",
39 | "\n",
40 | "Constants:\n",
41 | " h: Planck's constant in J * s\n",
42 | " c: speed of light in cm / s\n",
43 | " kB: Boltzmann's constant in J / K\n",
44 | " Eh: energy of a hartree in J\n",
45 | "\"\"\"\n",
46 | "from constants import PHYSICAL_CONSTANTS\n",
47 | "h = PHYSICAL_CONSTANTS[\"h\"]\n",
48 | "c = PHYSICAL_CONSTANTS[\"c\"]\n",
49 | "kB = PHYSICAL_CONSTANTS[\"kB\"]\n",
50 | "Eh = PHYSICAL_CONSTANTS[\"Eh\"]"
51 | ]
52 | },
53 | {
54 | "attachments": {},
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "### Extracting SPE energy from electronic structure calculations\n",
59 | "\n",
60 | "Electronic structure output files are read by cctk depending on the value of the *software* variable. The final molecule is chosen, and its energy is returned."
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": null,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "def get_energy(software,path):\n",
70 | " \"\"\"Function to get energy from electronic structure calculation\n",
71 | " Args:\n",
72 | " software (string): the electronic structure package used. supported values: `orca` or `gauss`\n",
73 | " path (string): relative or exact path to output file \n",
74 | " \n",
75 | " Returns:\n",
76 | " float: energy in Eh\n",
77 | "\n",
78 | " Raises:\n",
79 | " Exception: if software value passed is not as expected\n",
80 | " \"\"\"\n",
81 | " if software == 'orca':\n",
82 | " ensemble = cctk.OrcaFile.read_file(path).ensemble\n",
83 | " elif software == 'gauss':\n",
84 | " ensemble = cctk.GaussianFile.read_file(path).ensemble\n",
85 | " else:\n",
86 | " raise Exception(\"Check software variable passed\")\n",
87 | " molecule = ensemble.molecules[-1]\n",
88 | " properties_dict = ensemble.get_properties_dict(molecule)\n",
89 | " return properties_dict[\"energy\"]"
90 | ]
91 | },
92 | {
93 | "attachments": {},
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "### The Skodge Truhlar equation\n",
98 | "\n",
99 | "The Skodge Truhlar equation is an alternative approximation for calculating transmission coefficients. (*J. Phys. Chem.* **1981**, 85, 624-626.) This strategy might be more applicable in cases of very large frequencies.\n",
100 | "\n",
101 | "The the natural vibrational frequency is calculated from the reduced mass ($\\mu$) and the force constant (k) of the imaginary mode. This is handled automatically in PyQuiver.\n",
102 | "$$ \\nu^\\ddag = {1 \\over{2 \\pi}} * \\sqrt{k^\\ddag \\over \\mu^\\ddag} $$\n",
103 | "\n",
104 | "The ST equation is definited by two key terms $\\alpha$ and $\\beta$, where $ \\alpha = {{2\\pi}\\over{h\\nu^\\ddag}} $ and $ \\beta = {1\\over{k_BT}}$ as well as the barrier height (electronic energy) in the exothermic direction ($V = V_{TS} - max(V_{SM},V_{PR})$):\n",
105 | "\n",
106 | "\n",
107 | "\n",
108 | "The ST equation has two forms depending on the vibrational frequency and temperature.\n",
109 | "\n",
110 | "In cases of a relatively small $\\nu^\\ddag$ and high T ($\\alpha > \\beta$):\n",
111 | "$$ \\kappa = {{\\beta\\pi/\\alpha}\\over{sin(\\beta\\pi/\\alpha)}} - {\\beta\\over{\\alpha - \\beta}}e^{(\\beta-\\alpha)V} $$\n",
112 | "\n",
113 | "Conversely, in cases of a relatively large $\\nu^\\ddag$ and low T ($\\alpha < \\beta$):\n",
114 | "\n",
115 | "$$ \\kappa = {\\beta\\over{\\beta-\\alpha}}(e^{(\\beta-\\alpha)V}-1) $$\n",
116 | "\n",
117 | "In states where $\\alpha = \\beta$ :\n",
118 | "$$ \\kappa = {{\\beta\\pi/\\alpha}\\over{sin(\\beta\\pi/\\alpha)}}$$\n",
119 | "\n",
120 | "The tunnelling correction is defined as $\\kappa_{light}/\\kappa_{heavy}$\n"
121 | ]
122 | },
123 | {
124 | "cell_type": "code",
125 | "execution_count": null,
126 | "metadata": {},
127 | "outputs": [],
128 | "source": [
129 | "def st_factor(imag_cm,temp_K,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh):\n",
130 | " \"\"\"This function calculates the Skodge Truhlar transmission coefficient\n",
131 | " Args:\n",
132 | " imag_cm (float): natural vibrational frequency of the transition state mode in 1 / cm\n",
133 | " temp_K (int): temperature of reaction in K \n",
134 | " sm_energy_Eh (float): energy of starting structure in hartree\n",
135 | " pr_energy_Eh (float): energy of product structure in hartree\n",
136 | " ts_energy_Eh (float): energy of transition state in hartree\n",
137 | "\n",
138 | " Returns:\n",
139 | " float: Skodge Truhlar transmission coefficient\n",
140 | " \"\"\"\n",
141 | "\n",
142 | " #: natural vibrational frequency is converted to hertz with the speed of light and made positive\n",
143 | " imag_hz = np.abs(imag_cm * c)\n",
144 | "\n",
145 | " #: sm/pr/ts energies are converted to J\n",
146 | " pr_energy_J = pr_energy_Eh * Eh\n",
147 | " ts_energy_J = ts_energy_Eh * Eh\n",
148 | " sm_energy_J = sm_energy_Eh * Eh\n",
149 | "\n",
150 | " #: barrier is calculated in the exothermic direction in J\n",
151 | " barr_J = ts_energy_J - max(pr_energy_J,sm_energy_J)\n",
152 | "\n",
153 | " #: ST parameters alpha and beta are calculated both in J**-1\n",
154 | " alpha = 2 * np.pi / (h * imag_hz)\n",
155 | " beta = 1 / (kB * temp_K)\n",
156 | "\n",
157 | " #: Equation used is dependent on relation of alpha and beta, both are unitless\n",
158 | " if alpha == beta:\n",
159 | " return (beta * np.pi / alpha)/(np.sin(beta * np.pi / alpha))\n",
160 | " elif alpha > beta:\n",
161 | " return (beta * np.pi / alpha)/(np.sin(beta * np.pi / alpha)) - beta/(alpha-beta)*np.exp((beta-alpha)*barr_J)\n",
162 | " else:\n",
163 | " return beta/(beta-alpha)*(np.exp((beta-alpha)*barr_J)-1)\n",
164 | "\n",
165 | "def st_corrrection(imag_light_cm,imag_heavy_cm,temp_K,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh):\n",
166 | " \"\"\"This function uses st_factor to calculate the ratio of tunnelling coefficient for the light and heavy isotopes\n",
167 | " Args:\n",
168 | " imag_light_cm (float): light isotope natural vibrational frequency of the transition state mode in 1 / cm\n",
169 | " imag_heavy_cm (float): heavy isotope natural vibrational frequency of the transition state mode in 1 / cm\n",
170 | " temp_K (int): temperature of reaction in K \n",
171 | " sm_energy_Eh (float): energy of starting structure in hartree\n",
172 | " pr_energy_Eh (float): energy of product structure in hartree\n",
173 | " ts_energy_Eh (float): energy of transition state in hartree\n",
174 | "\n",
175 | " Returns:\n",
176 | " float: Skodge Truhlar tunnelling correction factor\n",
177 | " \"\"\"\n",
178 | " light_factor = st_factor(imag_light_cm,temp_K,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh)\n",
179 | " heavy_factor = st_factor(imag_heavy_cm,temp_K,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh)\n",
180 | " return light_factor/heavy_factor"
181 | ]
182 | },
183 | {
184 | "attachments": {},
185 | "cell_type": "markdown",
186 | "metadata": {},
187 | "source": [
188 | "### PyQuiver implementation\n",
189 | "The following function uses the above function for the ST tunnelling correction to a PyQuiver generated list of KIEs."
190 | ]
191 | },
192 | {
193 | "cell_type": "code",
194 | "execution_count": null,
195 | "metadata": {},
196 | "outputs": [],
197 | "source": [
198 | "def calc_frequency(kie,kiecalc):\n",
199 | " \"\"\"This function uses PyQuiver to calculate the natural vibrational frequencies\n",
200 | " Args:\n",
201 | " kie (KIE): isotopologue to calculate frequency of\n",
202 | " kiecalc (KIE_CALCULATION): kie calculation to extract configuration parameters from\n",
203 | "\n",
204 | " Returns:\n",
205 | " light_imag_freq (float), heavy_imag_freq (float): light and heavy imaginary frequencies in 1 / cm\n",
206 | " \"\"\"\n",
207 | "\n",
208 | " light_imag_freqs = kie.ts_tuple[0].calculate_frequencies(kiecalc.config.imag_threshold, scaling=kiecalc.config.scaling)[1]\n",
209 | " heavy_imag_freqs = kie.ts_tuple[1].calculate_frequencies(kiecalc.config.imag_threshold, scaling=kiecalc.config.scaling)[1]\n",
210 | " return min(light_imag_freqs), min(heavy_imag_freqs)\n",
211 | "\n",
212 | "def st_KIE(kiecalc,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh):\n",
213 | " \"\"\"This function calculates Skodge Truhlar corrected KIEs from a KIE_CALCULATION\n",
214 | " Args:\n",
215 | " kiecalc (KIE_Calculation): KIE Calculation\n",
216 | " sm_energy_Eh (float): energy of starting structure in hartree\n",
217 | " pr_energy_Eh (float): energy of product structure in hartree\n",
218 | " ts_energy_Eh (float): energy of transition state in hartree\n",
219 | "\n",
220 | " Returns:\n",
221 | " dictionary{isotopologue}: dictionary of isotopologues\n",
222 | "\n",
223 | " Raise:\n",
224 | " Exception: if KIE calculation is determined to be an EIE calculation\n",
225 | " \"\"\"\n",
226 | "\n",
227 | " sk_kie_dict = {}\n",
228 | "\n",
229 | " if kiecalc.eie_flag != 0:\n",
230 | " raise Exception(\"ST Correction not appropriate for EIE calculation\")\n",
231 | "\n",
232 | " #: Check whether to use relative or absolute KIEs, calculate reference tunnelling correction if relative\n",
233 | " if kiecalc.config.reference_isotopologue != \"default\" and kiecalc.config.reference_isotopologue != \"none\":\n",
234 | " ref = kiecalc.KIES[kiecalc.config.reference_isotopologue]\n",
235 | " imag_light_cm, imag_heavy_cm = calc_frequency(ref,kiecalc)\n",
236 | " ref_tunnel_corr = st_corrrection(imag_light_cm,imag_heavy_cm,kiecalc.config.temperature,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh)\n",
237 | " sk_kie_dict[kiecalc.config.reference_isotopologue] = ref.value[0] * ref_tunnel_corr\n",
238 | " else:\n",
239 | " ref_tunnel_corr = 1\n",
240 | "\n",
241 | " for key, value in kiecalc.KIES.items():\n",
242 | " if key != kiecalc.config.reference_isotopologue:\n",
243 | " #: extract light and heavy imaginary frequencies from pyquiver\n",
244 | " imag_light_cm, imag_heavy_cm = calc_frequency(value,kiecalc)\n",
245 | " \n",
246 | " #: calculate tunnelling correction and print result\n",
247 | " tuncorrection = st_corrrection(imag_light_cm,imag_heavy_cm,kiecalc.config.temperature,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh) / ref_tunnel_corr\n",
248 | " sk_kie_dict[key] = value.value[0] * tuncorrection\n",
249 | "\n",
250 | " return sk_kie_dict\n",
251 | "\n",
252 | "def print_st(kiecalc,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh):\n",
253 | " \"\"\"This function prints Skodge Truhlar corrected KIEs from a KIE_CALCULATION\n",
254 | " Args:\n",
255 | " kiecalc (KIE_Calculation): KIE Calculation\n",
256 | " sm_energy_Eh (float): energy of starting structure in hartree\n",
257 | " pr_energy_Eh (float): energy of product structure in hartree\n",
258 | " ts_energy_Eh (float): energy of transition state in hartree\n",
259 | " \"\"\"\n",
260 | " \n",
261 | " print(\"Skodge Truhlar Corrected Absolute KIE:\")\n",
262 | " print(\"Isotopologue uncorrected Skodge Truhlar\")\n",
263 | " print(\" KIE KIE\")\n",
264 | "\n",
265 | " sk_kie_dict = st_KIE(kiecalc,sm_energy_Eh,pr_energy_Eh,ts_energy_Eh)\n",
266 | "\n",
267 | " #: Iterate through Isotopologues to print uncorrected and ST corrected KIEs\n",
268 | " for key, value in kiecalc.KIES.items():\n",
269 | " if key != kiecalc.config.reference_isotopologue:\n",
270 | " #: extract light and heavy imaginary frequencies from pyquiver\n",
271 | " \n",
272 | " spaces = \" \" * max((10 - len(key)),1)\n",
273 | " print(\"isotopologue:\" + spaces + key, end =\" \")\n",
274 | " print(\"%0.4f\" % value.value[0], end =\" \")\n",
275 | " print(\"%0.4f\" % sk_kie_dict[key])\n",
276 | " \n",
277 | " #: display reference information\n",
278 | " if kiecalc.config.reference_isotopologue != \"default\" and kiecalc.config.reference_isotopologue != \"none\":\n",
279 | " print()\n",
280 | " print(\"KIEs referenced to isotopologue \"+ kiecalc.config.reference_isotopologue +\", whose absolute KIEs are:\")\n",
281 | " spaces = \" \" * max((10 - len(kiecalc.config.reference_isotopologue)),1)\n",
282 | " print(\"isotopologue:\" + spaces + kiecalc.config.reference_isotopologue, end =\" \")\n",
283 | " print(\"%0.4f\" % kiecalc.KIES[kiecalc.config.reference_isotopologue].value[0], end =\" \")\n",
284 | " print(\"%0.4f\" % sk_kie_dict[kiecalc.config.reference_isotopologue])\n"
285 | ]
286 | },
287 | {
288 | "attachments": {},
289 | "cell_type": "markdown",
290 | "metadata": {},
291 | "source": [
292 | "### Running calculation\n",
293 | "With the necessary functions defined, the correction can be run on electronic structure data. Here the paths reference the tutorial data for the Claisen reaction. Note that for this reaction the ST corrected KIEs nearly exactly match the inverted parabola values.\n",
294 | "\n",
295 | "First the software is set which allows cctk to appropriately extract the energies. Then, the path to the *PyQuiver* configuration file is defined (see tutorial for details). Finally, relevent electronic structure calculations are set. For generality the extension is ommitted and added as needed. Alternatively, these paths can of course be manually set below when calling the appropriate function.\n",
296 | "\n",
297 | "The first example is for Orca and the second for Gaussian:"
298 | ]
299 | },
300 | {
301 | "cell_type": "code",
302 | "execution_count": null,
303 | "metadata": {},
304 | "outputs": [],
305 | "source": [
306 | "# set computational software\n",
307 | "# gauss for Gaussian\n",
308 | "# orca for Orca\n",
309 | "software = \"orca\"\n",
310 | "\n",
311 | "#location of pyquiver configuration file, see documentation/tutorial for details\n",
312 | "pyquivconf = \"../tutorial/orca/claisen_demo.config\"\n",
313 | "\n",
314 | "#include basename not extension\n",
315 | "smpath = \"../tutorial/orca/claisen_gs_freq\"\n",
316 | "prpath = \"../tutorial/orca/claisen_gs_freq\"\n",
317 | "tspath = \"../tutorial/orca/claisen_ts_freq\"\n",
318 | "\n",
319 | "kiecalc = KIE_Calculation(pyquivconf, smpath + \".hess\", tspath + \".hess\", style=\"orca\")\n",
320 | "print(kiecalc)\n",
321 | "print()\n",
322 | "print_st(kiecalc,get_energy(software,smpath+\".out\"),get_energy(software,prpath+\".out\"),get_energy(software,tspath+\".out\"))"
323 | ]
324 | },
325 | {
326 | "attachments": {},
327 | "cell_type": "markdown",
328 | "metadata": {},
329 | "source": [
330 | "It is also possible to directly extract the KIEs (in addition to printing them) as a dictionary of isotopologues (relative to reference isotopologue, which is recorded in the dictionary as an absolute KIE)\n",
331 | "\n",
332 | "Here we extract and print the ST corrected C4 KIE:"
333 | ]
334 | },
335 | {
336 | "cell_type": "code",
337 | "execution_count": null,
338 | "metadata": {},
339 | "outputs": [],
340 | "source": [
341 | "st = st_KIE(kiecalc,get_energy(software,smpath+\".out\"),get_energy(software,prpath+\".out\"),get_energy(software,tspath+\".out\"))\n",
342 | "print(\"%0.4f\" % st[\"C4\"])"
343 | ]
344 | },
345 | {
346 | "attachments": {},
347 | "cell_type": "markdown",
348 | "metadata": {},
349 | "source": [
350 | "Here is the same calculation as above, except using Gaussian output files"
351 | ]
352 | },
353 | {
354 | "cell_type": "code",
355 | "execution_count": null,
356 | "metadata": {},
357 | "outputs": [],
358 | "source": [
359 | "# set computational software\n",
360 | "# gauss for Gaussian\n",
361 | "# orca for Orca\n",
362 | "software = \"gauss\"\n",
363 | "\n",
364 | "#location of pyquiver configuration file, see documentation/tutorial for details\n",
365 | "pyquivconf = \"../tutorial/gaussian/claisen_demo.config\"\n",
366 | "\n",
367 | "#In this case the paths can include the extension since pyquiver and gaussian both use the gaussian output files\n",
368 | "smpath = \"../tutorial/gaussian/claisen_gs.out\"\n",
369 | "prpath = \"../tutorial/gaussian/claisen_gs.out\"\n",
370 | "tspath = \"../tutorial/gaussian/claisen_ts.out\"\n",
371 | "\n",
372 | "kiecalc = KIE_Calculation(pyquivconf, smpath, tspath)\n",
373 | "print(kiecalc)\n",
374 | "print()\n",
375 | "print_st(kiecalc,get_energy(software,smpath),get_energy(software,prpath),get_energy(software,tspath))"
376 | ]
377 | }
378 | ],
379 | "metadata": {
380 | "kernelspec": {
381 | "display_name": "cctk",
382 | "language": "python",
383 | "name": "python3"
384 | },
385 | "language_info": {
386 | "codemirror_mode": {
387 | "name": "ipython",
388 | "version": 3
389 | },
390 | "file_extension": ".py",
391 | "mimetype": "text/x-python",
392 | "name": "python",
393 | "nbconvert_exporter": "python",
394 | "pygments_lexer": "ipython3",
395 | "version": "3.8.16"
396 | },
397 | "orig_nbformat": 4,
398 | "vscode": {
399 | "interpreter": {
400 | "hash": "50c68f4a39ea646ed7d3e4e0d35a44bd89267cbb0b9077d25aee25f802178e8f"
401 | }
402 | }
403 | },
404 | "nbformat": 4,
405 | "nbformat_minor": 2
406 | }
407 |
--------------------------------------------------------------------------------
/scripts/snip.awk:
--------------------------------------------------------------------------------
1 | # Generates .snip files from g09 .out files.
2 | #
3 | # usage: awk -f snip.awk *.out
4 | #
5 | # Takes all *.out files and turns them into *.snip files.
6 | # (Will overwrite any existing .snips)
7 | #
8 | # These files can be used with PyQuiver, but not with the
9 | # old version of Quiver.
10 | BEGIN {
11 | if ( ARGC < 2 ) {
12 | print "Error: please specify some filenames."
13 | print ""
14 | print "snip.awk: generates .snip files from Gaussian .out files"
15 | print "These files will be compatible with PyQuiver."
16 | print ""
17 | print "usage: awk -f snip.awk *.out"
18 | exit
19 | }
20 | }
21 |
22 | FNR == 1 {
23 | fileCount++
24 | filenames[fileCount]=FILENAME
25 | }
26 |
27 | /Standard orientation/,/Rotational constants/ {
28 | if ( NF == 6 && match($0,"[a-zA-Z]") == 0 )
29 | {
30 | symbol[fileCount,$1]=$2
31 | x[fileCount,$1]=$4
32 | y[fileCount,$1]=$5
33 | z[fileCount,$1]=$6
34 | atoms[fileCount]=$1
35 | }
36 | }
37 |
38 | /SCF Done/ {
39 | energy[fileCount]=$5
40 | }
41 |
42 | /NAtoms=/ {
43 | split($0, natomsplit, "NAtoms=")
44 | split(natomsplit[2], natomsplitsplit, " ")
45 | numberOfAtoms[fileCount]=natomsplitsplit[1]
46 | }
47 |
48 | /Zero-point correction/ {
49 | energy_field_count = 0
50 | }
51 |
52 | /Zero-point correction/,/Sum of electronic and thermal Free Energies/ {
53 | split($0, line, "=")
54 | #print trim(line[2])
55 | energy_field[fileCount, energy_field_count] = trim(line[2])
56 | energy_field_count ++
57 | }
58 |
59 | /l9999.exe/ {
60 | archive[fileCount] = ""
61 | }
62 |
63 | /l9999.exe/,/\\\@/ {
64 | #archive[fileCount] = archive[fileCount] trim($0)
65 | s = $0
66 | gsub(/%/, "%%", s)
67 | #n = split(s, terminations, "l9999")
68 | #archiveString = "l9999" terminations[n]
69 | archive[fileCount] = archive[fileCount] s
70 | }
71 |
72 | END {
73 | if ( exitCode != 0 )
74 | exit
75 |
76 | split("Zero-point correction=@ Thermal correction to Energy=@ Thermal correction to Enthalpy=@ Thermal correction to Gibbs Free Energy=@ Sum of electronic and zero-point Energies=@ Sum of electronic and thermal Energies=@ Sum of electronic and thermal Enthalpies=@ Sum of electronic and thermal Free Energies=", energy_lines, "@")
77 | for (i=1; i <= fileCount; i++)
78 | {
79 | filename = filenames[i]
80 | gsub(".out",".snip",filename)
81 | print filename
82 |
83 | print "" > filename
84 |
85 | printf "Electronic energy: %15.8f\n", energy[i] >> filename
86 |
87 | for (j=0; j<=8; j++)
88 | printf "%s %s\n", energy_lines[j+1], energy_field[i,j] >> filename
89 |
90 | print "Standard orientation" >> filename
91 | for (j=1; j <= atoms[i]; j++)
92 | printf "%d %2d 0 %15.8f %15.8f %15.8f\n", j, symbol[i,j], x[i,j], y[i,j], z[i,j] >> filename
93 | print "Rotational constants (GHZ)" >> filename
94 | printf "NAtoms= %d \n", numberOfAtoms[i] >> filename
95 | printf "SCF Done Field3 Field4 %15.8f \n", energy[i] >> filename
96 | printf archive[i] >> filename
97 | printf "\n\n" >> filename
98 | }
99 | }
100 |
101 | # functions to trim whitespace
102 | function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s }
103 | function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s }
104 | function trim(s) { return rtrim(ltrim(s)); }
105 |
--------------------------------------------------------------------------------
/src/AUTOQUIVER.md:
--------------------------------------------------------------------------------
1 | ### `autoquiver.py`
2 |
3 | `autoquiver` allows KIEs to be calculated over many ground state and transition state files that share a common set of desired isotopic substitutions. (The `autoquiver` module has additional functionality when used through a Python interface (read the `scripts/pyquiver.ipynb` if interested) but the command line interface should suffice for most purposes.)
4 |
5 | Suppose we have a directory, `auto/`, with the following files:
6 |
7 | ```
8 | substitutions.config
9 | gs-type1.output ts-type1.output
10 | gs-type2.output ts-type2.output
11 | gs-type3.output ts-type3.output
12 | gs-type4.output ts-type4.output
13 | ```
14 |
15 | We might want to run *PyQuiver* using the `substitutions.config` file on all pairs of ground states and transition states. For example, these pairs may be the same calculation run at many levels of theory. Note that it is crucial that all files have a consistent atom numbering scheme.
16 |
17 | To accomplish this we run `autoquiver.py` as follows:
18 |
19 | ```
20 | python src/autoquiver.py -e .output auto/ auto/substitutions.config gs ts -
21 | ```
22 |
23 | The arguments are:
24 |
25 | * `-e .output`: a flag to look for files with the extension `.output` as the frequency jobs for the ground and transitions states.
26 | * `auto/`: look for files in the `auto/` directory.
27 | * `auto/substitutions.config`: use `auto/substitutions.config` as the configuration file.
28 | * `gs`: use the string "gs" to find ground state files. All files with the appropriate extension that contain the substring "gs" will be treated as ground state files.
29 | * `ts`: use the string "ts" to find transition state files.
30 | * `-`: use the field delimiter "-" to test if a ground state and transition states match. All fields after the first "-" must be identical. This means that `gs-type1.output` and `ts-type1.output` will match but `gs-type1.output` and `ts-type2.output` won't.
31 |
32 | The output of autoquiver is directed to a number of csv files corresponding to each configuration file given. These filenames are printed when autoquiver exits.
33 |
34 | For more information, the output of `python autoquiver.py -h` has been reproduced below:
35 |
36 | ```
37 | usage: autoquiver.py [-h] [-v] [-s STYLE] [-e EXT]
38 | config target gs_p ts_p delimiter
39 |
40 | A program to automatically run PyQuiver on a config file and all ground state
41 | and transition states matching certain constraints.
42 |
43 | positional arguments:
44 | config configuration file path
45 | target target directory file path (where the ground and
46 | transition state files live
47 | gs_p substring in ground state files
48 | ts_p substring in transition state files
49 | delimiter delimiter used to match ground and transition state
50 | files (all fields separated by the delimiter after the
51 | first must match)
52 |
53 | optional arguments:
54 | -h, --help show this help message and exit
55 | -v, --verbose when the verbose flag is set debug information is
56 | printed
57 | -s STYLE, --style STYLE
58 | style of input files
59 | -e EXT, --extension EXT
60 | extension of input files
61 | ```
62 |
--------------------------------------------------------------------------------
/src/autoquiver.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import re
4 | import subprocess
5 | import argparse
6 |
7 | import settings
8 | import quiver
9 |
10 | from kie import KIE_Calculation
11 |
12 | import glob
13 |
14 | def autoquiver(filepath, config_path, gs_p, ts_p, gs_ts_match_p, input_extension='.out', style='g09', report_tunnelling=False):
15 | if type(gs_p) is str:
16 | gs_str = gs_p
17 | gs_p = lambda x: (gs_str in x)
18 | elif hasattr(gs_p, '__call__'):
19 | pass
20 | else:
21 | raise TypeError("gs_p must either be a string or a function that returns a boolean value.")
22 | if type(ts_p) is str:
23 | ts_str = ts_p
24 | ts_p = lambda x: (ts_str in x)
25 | elif hasattr(ts_p, '__call__'):
26 | pass
27 | else:
28 | raise TypeError("ts_p must either be a string or a function that returns a boolean value.")
29 | if type(gs_ts_match_p) is str:
30 | delimiter = gs_ts_match_p
31 | gs_ts_match_p = lambda x,y: (x.split(delimiter)[1:] == y.split(delimiter)[1:])
32 | elif hasattr(ts_p, '__call__'):
33 | pass
34 | else:
35 | raise TypeError("gs_ts_p must either be a string or a function that returns a boolean value.")
36 |
37 | os.chdir(filepath)
38 | print("Starting AutoQuiver analysis...\n")
39 | for config in glob.glob("*.config"):
40 | if os.path.samefile(config_path, config):
41 | eie_flag = -1
42 | title_start = "ground_state,transition_state,"
43 | title = title_start
44 | table = ""
45 | for gs in glob.glob("*"+input_extension):
46 | if gs_p(gs):
47 | for ts in glob.glob("*"+input_extension):
48 | if ts_p(ts) and gs_ts_match_p(gs,ts):
49 | print("%-50s - %-50s : " % (gs[-50:], ts[-50:]), end=' ')
50 | try:
51 | kie = KIE_Calculation(config, gs, ts, style=style)
52 | except:
53 | print("error")
54 | continue
55 | title_row, row, eie_p = kie.get_row(report_tunnelling=report_tunnelling)
56 |
57 | # print the KIEs (minus the comma at the end)
58 | print(row[:-1])
59 |
60 | if eie_flag == -1:
61 | eie_flag = eie_p
62 | else:
63 | if eie_flag != eie_p:
64 | raise ValueError("some calculations represented EIEs and others represented KIEs.")
65 | if title is title_start:
66 | title = title + title_row
67 | table = title + "\n" + table
68 | else:
69 | if title_start + title_row != title:
70 | raise ValueError("the alignment of the table columns is incorrect.")
71 |
72 | table += gs + "," + ts + "," + row + "\n"
73 |
74 | with open(os.path.splitext(config)[0]+"-kies.csv", 'w') as f:
75 | f.write(table)
76 | print("\nAutoQuiver has completed execution.\nResult written to {0}.".format(os.path.splitext(config)[0]+"-kies.csv"))
77 |
78 | if __name__ == "__main__":
79 | parser = argparse.ArgumentParser(description="A program to automatically run PyQuiver on a config file and all ground state and transition states matching certain constraints.")
80 | parser.add_argument('-v', '--verbose', dest="debug", help='when the verbose flag is set debug information is printed', action='count')
81 | parser.add_argument('-s', '--style', dest="style", default='g09', help='style of input files')
82 | parser.add_argument('-e', '--extension', dest="ext", default='.out', help='extension of input files')
83 | parser.add_argument('-t', '--report_tunnelling', dest="report_tunnelling", action='store_true', help='report the tunnelling correction')
84 | parser.add_argument('config', help='configuration file path')
85 | parser.add_argument('target', help='target directory file path (where the ground and transition state files live')
86 | parser.add_argument('gs_p', help='substring in ground state files')
87 | parser.add_argument('ts_p', help='substring in transition state files')
88 | parser.add_argument('delimiter', help='delimiter used to match ground and transition state files (all fields separated by the delimiter after the first must match)')
89 | parser.set_defaults(report_tunnelling=False)
90 |
91 | args = parser.parse_args()
92 | settings.DEBUG = 0
93 | if args.debug:
94 | settings.DEBUG += args.debug
95 | print("Debug level is %d" % settings.DEBUG)
96 |
97 | autoquiver(args.target, args.config, args.gs_p, args.ts_p, args.delimiter, style=args.style, input_extension=args.ext, report_tunnelling=args.report_tunnelling)
98 |
--------------------------------------------------------------------------------
/src/config.py:
--------------------------------------------------------------------------------
1 | # This file reads PyQuiver configuration files.
2 | import sys
3 | import re
4 | import settings
5 | from constants import REPLACEMENTS, REPLACEMENTS_Z
6 | from collections import OrderedDict
7 |
8 | # Reads PyQuiver .config files.
9 | class Config(object):
10 | def __init__(self,filename):
11 | expected_fields = "scaling temperature mass_override_isotopologue reference_isotopologue imag_threshold frequency_threshold".split(" ")
12 | config = { i : None for i in expected_fields }
13 | config["filename"] = filename
14 |
15 | print_message = True
16 | try:
17 | f=list(sys._current_frames().values())[0]
18 | if "autoquiver" in f.f_back.f_back.f_globals['__file__']:
19 | print_message = False
20 | except:
21 | pass
22 | if print_message and settings.DEBUG >= 2:
23 | print("\nReading configuration from {0}".format(filename))
24 |
25 | # a list of isotopologues
26 | # each entry is a list of tuples
27 | # each tuple is (from_atom_number, to_atom_number, replacement_isotope)
28 | # this format allows for multiple replacements in one isotopologue
29 | isotopologues = OrderedDict()
30 |
31 | # read file
32 | for line in open(filename, "r"):
33 | # ignore comments and blank lines
34 | line = line.strip()
35 | if len(line) == 0 or line[0] == "#":
36 | continue
37 | line = line.split("#", 1)[0]
38 |
39 | # read space-delimited data
40 | fields = [_f for _f in line.split(" ") if _f]
41 |
42 | # for backwards compatibility with quiver
43 | if fields[0] == "isotopomer":
44 | fields[0] = "isotopologue"
45 | elif fields[0] == "reference_isotopomer":
46 | fields[0] = "reference_isotopologue"
47 | elif fields[0] == "mass_override_isotopomer":
48 | fields[0] = "mass_override_isotopologue"
49 |
50 |
51 | # parse
52 | if fields[0] == "isotopologue":
53 | # parse isotopologues, checking for sanity
54 | if len(fields) != 5:
55 | raise ValueError("unexpected number of fields for isotopologue in config file:\n%s" % line)
56 | isotopologue_id, from_atom_number, to_atom_number, replacement = str(fields[1]), int(fields[2]), int(fields[3]), str(fields[4])
57 | if isotopologue_id == "default":
58 | raise ValueError("name default is reserved.")
59 | if from_atom_number < 1 or to_atom_number < 1:
60 | raise ValueError("check atom numbers:\n%s" % line)
61 | if replacement not in list(REPLACEMENTS.keys()):
62 | raise ValueError("invalid isotopic replacement:\n%s" % line)
63 |
64 | # allows for the fact that isotopologues can make multiple replacements
65 | try:
66 | isotopologues[isotopologue_id].append((from_atom_number, to_atom_number, replacement))
67 | except KeyError:
68 | isotopologues[isotopologue_id] = [(from_atom_number, to_atom_number, replacement)]
69 |
70 | elif len(fields) == 2:
71 | # read regular configuration fields that have only one entry
72 | fields = [ str(i) for i in fields ]
73 | if fields[0] not in expected_fields:
74 | raise ValueError("unexpected config field:\n%s" % line)
75 | config[fields[0]] = fields[1]
76 | else:
77 | raise ValueError("unexpected number of fields in config file:\n%s" % line)
78 |
79 | # ensure we have all the fields we are supposed to
80 | for k,v in config.items():
81 | if k == "frequency_threshold" and v is not None:
82 | print("*** Warning: frequency_threshold is now deprecated and will be ignored. ***")
83 | if v is None:
84 | if k == "frequency_threshold":
85 | config["frequency_threshold"] = 0.0
86 | else:
87 | raise ValueError("missing config file field: %s" % k)
88 | if len(isotopologues) == 0:
89 | raise ValueError("must specify at least one isotopologue")
90 |
91 | # check some of the other fields
92 | config["temperature"] = float(config["temperature"])
93 | if config["temperature"] < 0.0:
94 | raise ValueError("check temperature")
95 | config["scaling"] = float(config["scaling"])
96 | if config["scaling"] < 0.5 or config["scaling"] > 1.5:
97 | raise ValueError("check scaling factor")
98 |
99 | try:
100 | config["reference_isotopologue"]
101 | except KeyError:
102 | raise ValueError("check reference isotopologue is valid")
103 | config["reference_isotopologue"] = str(config["reference_isotopologue"])
104 |
105 | try:
106 | config["mass_override_isotopologue"]
107 | except KeyError:
108 | raise ValueError("check reference isotopologue is valid")
109 | config["mass_override_isotopologue"] = str(config["mass_override_isotopologue"])
110 |
111 | config["frequency_threshold"] = float(config["frequency_threshold"])
112 |
113 | config["imag_threshold"] = float(config["imag_threshold"])
114 | if config["imag_threshold"] > 300.0:
115 | raise ValueError("imag threshold is too high")
116 |
117 | # store all the information in the object dictionary
118 | config["isotopologues"] = isotopologues
119 | self.__dict__ = config
120 |
121 | # checks if this config file is compatible with a pair of ground and transition state systems
122 | def check(self, gs, ts, verbose=False):
123 | n_gs_atoms = len(gs.atomic_numbers)
124 | n_ts_atoms = len(ts.atomic_numbers)
125 | # check that the isotopic replacements make sense
126 | isotopologues = self.isotopologues
127 | for i,isotopologue in isotopologues.items():
128 | for r in range(len(isotopologue)):
129 | # this replacement changes gs atom number from_atom
130 | # and ts atom number to_atom
131 | # to replacement (like "2D")
132 | from_atom = isotopologue[r][0]
133 | to_atom = isotopologue[r][1]
134 | replacement = isotopologue[r][2]
135 | replacement_string = "%s %d %d %s" % (i, from_atom, to_atom, replacement)
136 | # get the atomic numbers of from_atom and to_atom
137 | # must subtract one to convert from atom numbers to indices
138 | from_atomZ = gs.atomic_numbers[from_atom-1]
139 | to_atomZ = ts.atomic_numbers[to_atom-1]
140 | replacementZ = REPLACEMENTS_Z[replacement]
141 | if from_atomZ != replacementZ:
142 | raise ValueError("gs atomic number and replacement atomic number do not match for {0}\n".format(replacement_string))
143 | if to_atomZ != replacementZ:
144 | raise ValueError("ts atomic number and replacement atomic number do not match for {0}\n".format(replacement_string))
145 | if (from_atomZ != to_atomZ):
146 | raise ValueError("gs and ts atomic number do not match for\n" % replacement_string)
147 |
148 | if verbose:
149 | print("Config file %s makes sense with gs file %s and ts file %s.\n" % (self.filename, gs.filename, ts.filename))
150 |
151 | # convert to human-readable format
152 | def __str__(self):
153 | to_string = "Config file: %s\nTemperature: %.1f K\nScaling: %.3f\nReference Isotopologue: %s\nImag threshold (cm-1): %d\n" % \
154 | (self.filename, self.temperature, self.scaling, self.reference_isotopologue, self.imag_threshold)
155 | if self.frequency_threshold != 0:
156 | to_string += "Frequency threshold (cm-1): %d\n" % self.frequency_threshold
157 |
158 | keys = list(self.isotopologues.keys())
159 | if self.reference_isotopologue != "default" and self.reference_isotopologue != "none":
160 | try:
161 | keys.remove(self.reference_isotopologue)
162 | except:
163 | print("\nCould not find the following reference isotopologue: %s" % self.reference_isotopologue)
164 | sys.exit(1)
165 | keys.sort()
166 |
167 | if self.reference_isotopologue == "default" or self.reference_isotopologue == "none":
168 | to_string += " No reference isotopologue.\n"
169 | else:
170 | keys = [self.reference_isotopologue] + keys
171 | for i in keys:
172 | isotopologue = self.isotopologues[i]
173 |
174 | for j in range(len(isotopologue)):
175 | to_string += " Isotopologue {0: >10s}, replacement {1: >2d}: replace gs atom {2: ^3d} and ts atom {3: ^3d} with {4: >3s}\n".format(i, j+1, isotopologue[j][0], isotopologue[j][1], isotopologue[j][2])
176 |
177 | return to_string[:-1]
178 |
--------------------------------------------------------------------------------
/src/constants.py:
--------------------------------------------------------------------------------
1 | # This file holds physical constants and reads atomic weights.
2 | import sys
3 | import re
4 | import os
5 | import inspect
6 | ###############
7 |
8 | # Physical Constants
9 |
10 | PHYSICAL_CONSTANTS = {
11 | 'h' : 6.626070E-34, # Planck's constants in J * s
12 | 'c' : 2.997925E+10, # speed of light in units of cm/s
13 | 'Eh' : 4.359745E-18, # energy of a hartree in units of J = kg m^2/s^2
14 | 'a0' : 5.291772E-11, # bohr radius in m
15 | 'atb': 5.291772E-01, # angstroms per bohr
16 | 'amu': 1.660468E-27, # atomic mass unit in units kg
17 | 'kB' : 1.380649E-23 # Boltzmann's constant in J/K
18 | }
19 | #CM/2.998E10/,EM/1.440E13/,HBC/1.4387/
20 |
21 | ###############
22 |
23 | # Atomic Weight Information
24 |
25 | class Element(object):
26 | def __init__(self, full_name, atomic_number, symbol, default_mass):
27 | # the name of this element, like "hydrogen"
28 | full_name = str(full_name)
29 | self.full_name = full_name
30 | if re.match("[^a-z]", full_name):
31 | print("Unexpected non-lowercase character in element name: %s" % full_name)
32 | print("Quitting.")
33 | sys.exit(1)
34 |
35 | # the symbol of this element, like "H"
36 | symbol = str(symbol)
37 | self.symbol = symbol
38 | if re.match("[^a-zA-Z]", symbol):
39 | print("Unexpected non-letter character in element symbol: %s" % symbol)
40 | print("Quitting.")
41 | sys.exit(1)
42 | if len(symbol) < 1 or len(symbol) > 2:
43 | print("Unexpected length of element symbol (must be 1 or 2): %s" % symbol)
44 | print("Quitting.")
45 | sys.exit(1)
46 |
47 | # the atomic number of this element, like 1
48 | atomic_number = int(atomic_number)
49 | self.atomic_number = atomic_number
50 | if atomic_number < 1 or atomic_number > 200:
51 | print("Unexpected atomic number: %d" % atomic_number)
52 | print("Quitting.")
53 | sys.exit(1)
54 |
55 | # the average weight for this element, like 1.00783
56 | default_mass = float(default_mass)
57 | self.default_mass = default_mass
58 | if default_mass < 0.0 or default_mass > 500.0:
59 | print("Unexpected default mass: %d" % default_mass)
60 | print("Quitting.")
61 | sys.exit(1)
62 |
63 | # pairs of tuples strings (like "2H") to masses (like 2.0141)
64 | self.replacements = []
65 |
66 | def __str__(self):
67 | string = "%s (%s, Z=%d, default mass = %.4f" % (self.full_name.capitalize(), self.symbol, self.atomic_number, self.default_mass)
68 | if len(self.replacements) == 0:
69 | string += ", no isotopic replacements possible)\n"
70 | else:
71 | string += ")\n"
72 | for s,m in self.replacements:
73 | string += " %2s : %.4f\n" % (s,m)
74 | return string[:-1]
75 |
76 | def add_replacement(self, symbol, mass):
77 | symbol = str(symbol)
78 | if re.match("[^a-zA-Z0-9]", symbol):
79 | print("Unexpected non-letter character in isotopic replacement symbol: %s" % symbol)
80 | print("Quitting.")
81 | sys.exit(1)
82 | if len(symbol) < 1 or len(symbol) > 4:
83 | print("Unexpected length of element symbol in replacement (must be 1-4 inclusive, found %d): %s" % (len(symbol), symbol))
84 | print("Quitting.")
85 | sys.exit(1)
86 | for s,m in self.replacements:
87 | if s == symbol:
88 | print("Must use a unique symbol for every isotopic replacement: %s" % s)
89 | sys.exit(1)
90 | mass = float(mass)
91 | if mass < 0.0 or mass > 500.0:
92 | print("Unexpected isotopic replacement mass: %f" % mass)
93 | sys.exit(1)
94 | self.replacements.append((symbol,mass))
95 |
96 | # read in atomic weight data
97 | elements = []
98 |
99 | root = os.path.split(os.path.abspath(__file__))[0]
100 |
101 | for line in open(root + "/weights.dat", "r"):
102 | # ignore comments and blank lines
103 | line = line.strip()
104 | if len(line) == 0 or line[0] == "#":
105 | continue
106 | line = line.split("#",1)[0]
107 |
108 | # parse
109 | fields = line.split(",") #line.encode("ascii","ignore").split(",")
110 | if len(fields) < 4:
111 | print("Error: not enough data on this line of weights.dat:")
112 | print(line)
113 | print("\nQuitting.")
114 | sys.exit(1)
115 | element = Element(*fields[0:4])
116 | if (len(fields)-4) % 2 != 0:
117 | print("Unexpected number of isotopic replacement fields on this line of weights.dat.")
118 | print("The number of fields after the first four must be a multiple of 2 (found %d)." % (len(fields)-4))
119 | print(line)
120 | print("\nQuitting.")
121 | sys.exit(1)
122 | if (len(fields) > 4):
123 | for i in range(4, len(fields), 2):
124 | element.add_replacement(fields[i], fields[i+1])
125 | elements.append(element)
126 | #print element
127 | print("Read atomic weight data for %d elements." % len(elements))
128 |
129 | # map from atomic number to default masses
130 | DEFAULT_MASSES = { e.atomic_number : e.default_mass for e in elements }
131 |
132 | # map from valid isotopic replacements to masses
133 | REPLACEMENTS = {}
134 | for e in elements:
135 | for replacement,mass in e.replacements:
136 | REPLACEMENTS[replacement] = mass
137 |
138 | # map from isotopic replacements to atomic numbers
139 | REPLACEMENTS_Z = {}
140 | for e in elements:
141 | for replacement,mass in e.replacements:
142 | REPLACEMENTS_Z[replacement]=e.atomic_number
143 |
144 |
145 | # threshold to separate linear molecules from non-linear molecules
146 | LINEARITY_THRESHOLD = 1e-06
147 | DROP_NUM_LINEAR = 5
148 | # DROP_NUM_NONLINEAR = 6
149 |
--------------------------------------------------------------------------------
/src/kie.py:
--------------------------------------------------------------------------------
1 | # This calculates KIEs based on the Bigeleisen-Mayer equation.
2 | import numpy as np
3 | #from quiver import System, Isotopologue, DEBUG
4 | import quiver
5 | import settings
6 | from config import Config
7 | from constants import DEFAULT_MASSES
8 | from collections import OrderedDict
9 |
10 | # load constants
11 | from constants import PHYSICAL_CONSTANTS, REPLACEMENTS
12 | h = PHYSICAL_CONSTANTS["h"] # in J . s
13 | c = PHYSICAL_CONSTANTS["c"] # in cm . s
14 | kB = PHYSICAL_CONSTANTS["kB"] # in J/K
15 |
16 | class KIE_Calculation(object):
17 | def __init__(self, config, gs, ts, style="g09"):
18 | # check the types of config, gs, and ts parsing files if necessary and copying fields if not
19 | if type(config) is str:
20 | self.config = Config(config)
21 | elif type(config) is Config:
22 | self.config = Config
23 | else:
24 | raise TypeError("config argument must be either a filepath or Config object.")
25 |
26 | if type(gs) is str:
27 | self.gs_system = quiver.System(gs, style=style)
28 | elif type(gs) is quiver.System:
29 | self.gs_system = gs
30 | else:
31 | raise TypeError("gs argument must be either a filepath or quiver.System object.")
32 |
33 | if type(ts) is str:
34 | self.ts_system = quiver.System(ts, style=style)
35 | elif type(ts) is quiver.System:
36 | self.ts_system = ts
37 | else:
38 | raise TypeError("ts argument must be either a filepath or quiver.System object.")
39 |
40 | # set the eie_flag to the recognized uninitialized value (used for checking if there are inconsistent calculation types)
41 | self.eie_flag = -1
42 |
43 | if settings.DEBUG != 0:
44 | print(self.config)
45 |
46 | KIES = OrderedDict()
47 |
48 | if self.config.frequency_threshold:
49 | print("WARNING: config file uses the frequency_threshold parameter. This has been deprecated and low frequencies are dropped by linearity detection.")
50 |
51 | for p in self.make_isotopologues():
52 | gs_tuple, ts_tuple = p
53 | name = gs_tuple[1].name
54 | kie = KIE(name, gs_tuple, ts_tuple, self.config.temperature, self.config.scaling, self.config.imag_threshold)
55 | KIES[name] = kie
56 |
57 | for name,k in KIES.items():
58 | if name != self.config.reference_isotopologue:
59 | if self.eie_flag == -1:
60 | eie_flag_iso = name
61 | self.eie_flag = k.eie_flag
62 | else:
63 | if self.eie_flag != k.eie_flag:
64 | if eie_flag == 1:
65 | raise ValueError("quiver attempted to run a KIE calculation (isotopomer {0}) after an EIE calculation (isotopomer {1}). Check the frequency threshold.".format(name, eie_flag_iso))
66 | else:
67 | raise ValueError("quiver attempted to run an EIE calculation (isotopomer {0}) after a KIE calculation (isotopomer {1}). Check the frequency threshold.".format(name, eie_flag_iso))
68 |
69 | if self.config.reference_isotopologue != "default" and self.config.reference_isotopologue != "none":
70 | k.apply_reference(KIES[self.config.reference_isotopologue])
71 |
72 | self.KIES = KIES
73 |
74 | # retrieves KIEs for autoquiver output
75 | # if report_tunnelling = True, the first number will be the inverted parabola KIE
76 | # and the second number will be the tunnelling correction
77 | def get_row(self, report_tunnelling=False):
78 | title_row = ""
79 | row = ""
80 | keys = list(self.KIES.keys())
81 |
82 | # don't report the reference isotoplogue
83 | if self.config.reference_isotopologue != "default" and self.config.reference_isotopologue != "none":
84 | keys.remove(self.config.reference_isotopologue)
85 |
86 | if self.config.mass_override_isotopologue != "default" and self.config.reference_isotopologue != "none":
87 | keys.remove(self.config.mass_override_isotopologue)
88 |
89 | # for each isotopologue, add the KIEs
90 | for name in keys:
91 | if report_tunnelling:
92 | title_row += "%s,%s," % (name + "_uncorr", name + "_inf_para")
93 | else:
94 | title_row += "{0},".format(name)
95 |
96 | if self.eie_flag == 0:
97 | # this is a KIE calculation
98 | if report_tunnelling:
99 | row += "%.4f,%.4f," % ( self.KIES[name].value[-1], self.KIES[name].value[-1]-self.KIES[name].value[-3] )
100 | else:
101 | row += "{0:.4f},".format(self.KIES[name].value[-1])
102 | else:
103 | # this is an EIE calculation
104 | row += "{0:.4f},".format(self.KIES[name].value)
105 | #if settings.DEBUG >= 2:
106 | # if self.eie_flag == 0:
107 | # print "KIE calculation detected using {0}th tunneling correction".format(len(self.KIES[name].value))
108 | # row += "{0:.3f},".format(self.KIES[name].value[-1])
109 | # else:
110 | # row += "{0:.3f},".format(self.KIES[name].value)
111 |
112 | return (title_row, row, self.eie_flag)
113 |
114 | def build_mass_override_masses(self):
115 | config = self.config
116 | gs_system = self.gs_system
117 | ts_system = self.ts_system
118 |
119 | gs_masses = self.build_default_masses(gs_system)
120 | ts_masses = self.build_default_masses(ts_system)
121 |
122 | if config.mass_override_isotopologue != "default":
123 | iso = self.config.isotopologues[config.mass_override_isotopologue]
124 | gs_rules, ts_rules = self.compile_mass_rules(iso)
125 |
126 | gs_masses = self.apply_mass_rules(gs_masses, gs_rules)
127 | ts_masses = self.apply_mass_rules(ts_masses, ts_rules)
128 |
129 | return gs_masses, ts_masses
130 |
131 | def build_default_masses(self, system):
132 | masses = []
133 | #print "---start---"
134 | #for k in DEFAULT_MASSES:
135 | # print k, DEFAULT_MASSES[k]
136 | #print "---"
137 | for i in range(system.number_of_atoms):
138 | #print i, system.atomic_numbers[i], DEFAULT_MASSES[system.atomic_numbers[i]]
139 | if not system.atomic_numbers[i] in DEFAULT_MASSES:
140 | raise ValueError("Default mass not available for atomic number %d at atom number %d in %s!" % (system.atomic_numbers[i], i+1, system.filename))
141 | masses.append(DEFAULT_MASSES[system.atomic_numbers[i]])
142 | #print "--end--"
143 | return np.array(masses)
144 |
145 | def apply_mass_rules(self, prev_masses, rules):
146 | masses = list(prev_masses)
147 | for k,v in rules.items():
148 | masses[k] = v
149 | return masses
150 |
151 | def compile_mass_rules(self, iso_rules):
152 | gs_rules = {}
153 | ts_rules = {}
154 | for replacement in iso_rules:
155 | gs_atom_number, ts_atom_number, replacement_label = replacement
156 | replacement_mass = REPLACEMENTS[replacement_label]
157 | gs_rules[gs_atom_number-1] = replacement_mass
158 | ts_rules[ts_atom_number-1] = replacement_mass
159 | return gs_rules, ts_rules
160 |
161 | # make the requested isotopic substitutions
162 | # yields tuples of tuples of the form ((gs_sub, gs_ref), (ts_sub, ts_ref))
163 | def make_isotopologues(self):
164 | config = self.config
165 | gs_system = self.gs_system
166 | ts_system = self.ts_system
167 | config.check(gs_system, ts_system)
168 |
169 | mass_override_gs_masses, mass_override_ts_masses = self.build_mass_override_masses()
170 |
171 | default_gs = quiver.Isotopologue("default", gs_system, mass_override_gs_masses)
172 | default_ts = quiver.Isotopologue("default", ts_system, mass_override_ts_masses)
173 |
174 | for id_,iso in config.isotopologues.items():
175 | if id_ != config.mass_override_isotopologue:
176 | gs_rules, ts_rules = self.compile_mass_rules(iso)
177 | gs_masses = self.apply_mass_rules(mass_override_gs_masses, gs_rules)
178 | ts_masses = self.apply_mass_rules(mass_override_ts_masses, ts_rules)
179 | sub_gs = quiver.Isotopologue(id_, gs_system, gs_masses)
180 | sub_ts = quiver.Isotopologue(id_, ts_system, ts_masses)
181 | yield ((default_gs, sub_gs), (default_ts, sub_ts))
182 |
183 | def __str__(self):
184 | string = "\n=== PyQuiver Analysis ===\n"
185 | if self.eie_flag == 0:
186 | string += "Isotopologue uncorrected Wigner inverted parabola\n"
187 | string += " KIE KIE KIE"
188 | else:
189 | string += "Isotopologue EIE"
190 | keys = list(self.KIES.keys())
191 | if self.config.reference_isotopologue != "default" and self.config.reference_isotopologue != "none":
192 | keys.remove(self.config.reference_isotopologue)
193 | if self.config.mass_override_isotopologue != "default":
194 | keys.remove(self.config.mass_override_isotopologue)
195 | #keys.sort()
196 | for name in keys:
197 | string += "\n" + str(self.KIES[name])
198 |
199 | if self.config.reference_isotopologue != "default" and self.config.reference_isotopologue != "none":
200 | string += "\n\nKIEs referenced to isotopologue {0}, whose absolute KIEs are:".format(self.config.reference_isotopologue)
201 | string += "\n" + str(self.KIES[self.config.reference_isotopologue])
202 | else:
203 | string += "\n\nAbsolute KIEs are given."
204 |
205 | return string
206 |
207 | class KIE(object):
208 | # the constructor expects a tuple of the form yielded by make_isotopologue
209 | def __init__(self, name, gs_tuple, ts_tuple, temperature, scaling, imag_threshold):
210 | # copy fields
211 | # the associated calculation object useful for pulling config fields etc.
212 | self.eie_flag = -1
213 | self.name = name
214 | self.imag_threshold = imag_threshold
215 | self.gs_tuple, self.ts_tuple = gs_tuple, ts_tuple
216 | self.temperature = temperature
217 | self.scaling = scaling
218 |
219 | if settings.DEBUG >= 2:
220 | print("Calculating KIE for isotopologue {0}.".format(name))
221 | self.value = self.calculate_kie()
222 |
223 | def calculate_kie(self):
224 | if settings.DEBUG >= 2:
225 | print(" Calculating Reduced Partition Function Ratio for Ground State.")
226 | rpfr_gs, gs_imag_ratios, gs_heavy_freqs, gs_light_freqs = calculate_rpfr(self.gs_tuple, self.imag_threshold, self.scaling, self.temperature)
227 | if settings.DEBUG >= 2:
228 | print(" rpfr_gs:", np.prod(rpfr_gs))
229 | if settings.DEBUG >= 2:
230 | print(" Calculating Reduced Partition Function Ratio for Transition State.")
231 |
232 | rpfr_ts, ts_imag_ratios, ts_heavy_freqs, ts_light_freqs = calculate_rpfr(self.ts_tuple, self.imag_threshold, self.scaling, self.temperature)
233 | if settings.DEBUG >= 2:
234 | print(" rpfr_ts:", np.prod(rpfr_ts))
235 |
236 | if ts_imag_ratios is not None:
237 | if self.eie_flag == -1:
238 | self.eie_flag = 0
239 | else:
240 | raise ValueError("quiver attempted to run a KIE calculation after an EIE calculation. Check the frequency threshold.")
241 |
242 | kies = ts_imag_ratios * rpfr_gs/rpfr_ts
243 | return kies
244 | else:
245 | if self.eie_flag == -1:
246 | self.eie_flag = 1
247 | else:
248 | raise ValueError("quiver attempted to run a KIE calculation after an EIE calculation. Check the frequency threshold.")
249 |
250 | eie = rpfr_gs/rpfr_ts
251 | return eie
252 |
253 | def apply_reference(self, reference_kie):
254 | self.value /= reference_kie.value
255 | return self.value
256 |
257 | def __str__(self):
258 | if self.value is not None:
259 | if self.eie_flag == 1:
260 | return "Isotopologue {1: >10s} {0: >33s} {2: ^12.4f} ".format("", self.name, self.value)
261 | else:
262 | return "Isotopologue {1: >10s} {0: >33s} {2: ^12.4f} {3: ^14.4f} {4: ^17.4f}".format("", self.name, self.value[0], self.value[1], self.value[2])
263 | else:
264 | "KIE Object for isotopomer {0}. No value has been calculated yet.".format(self.name)
265 |
266 | # utility function to calculate u terms
267 | # u = hv/kT where h = Planck's constant, v = frequency, kB = Boltzmann's constant, T = temperature
268 | # if using v in wavenumber, w
269 | # w = v/c, with c in cm/s
270 | # v = cw
271 | # u = hcw/kT, with c in cm/s
272 | def u(wavenumber, temperature):
273 | return h*c*wavenumber/(kB*temperature)
274 |
275 | # calculates the reduced isotopic function ratio for a species (Wolfsberg eqn 4.79)
276 | # assuming the symmetry ratio is 1/1
277 | # uses the Teller-Redlich product rule
278 | # returns 1 x n array of the partition function ratios, where n is the number of frequencies
279 | # frequencies below frequency_threshold will be ignored and will not be included in the array
280 | def partition_components(freqs_heavy, freqs_light, temperature):
281 | components = []
282 | i = 0
283 | for wavenumber_light, wavenumber_heavy in zip(freqs_light, freqs_heavy):
284 | i += 1
285 | product_factor = wavenumber_heavy/wavenumber_light
286 | u_light = u(wavenumber_light, temperature)
287 | u_heavy = u(wavenumber_heavy, temperature)
288 | excitation_factor = (1.0-np.exp(-u_light))/(1.0-np.exp(-u_heavy))
289 | ZPE_factor = np.exp(0.5*(u_light-u_heavy))
290 | components.append([product_factor,excitation_factor,ZPE_factor])
291 | if settings.DEBUG >= 3:
292 | overall_factor = product_factor * excitation_factor * ZPE_factor
293 | print("MODE %3d LIGHT: %9.3f cm-1 HEAVY: %9.3f cm-1 FRQ RATIO: %9.5f ZPE FACTOR: %9.5f CONTRB TO RIPF: %9.5f" % (i, wavenumber_light, wavenumber_heavy, product_factor, ZPE_factor, overall_factor))
294 | #if overall_factor < 0.99 or overall_factor > 1.01:
295 | # print " *"
296 | #else:
297 | # print
298 | return np.array(components)
299 |
300 | # tup is a tuple of a form (light_isotopologue, heavy_isotopologue)
301 | def calculate_rpfr(tup, imag_threshold, scaling, temperature):
302 | # calculate_frequencies gives tuples of the form (small_freqs, imaginary_freqs, freqs)
303 | light_small_freqs, light_imag_freqs, light_freqs, light_num_small = tup[0].calculate_frequencies(imag_threshold, scaling=scaling)
304 | heavy_small_freqs, heavy_imag_freqs, heavy_freqs, heavy_num_small = tup[1].calculate_frequencies(imag_threshold, scaling=scaling)
305 |
306 | if len(heavy_freqs) != len(light_freqs):
307 | raise ValueError("mismatch in the number of frequencies between isotopomers!")
308 | if len(light_imag_freqs) != len(heavy_imag_freqs):
309 | print("WARNING: mismatch in the number of imaginary frequencies between isotopomers, ignoring imaginary mode")
310 | light_imag_freqs = []
311 | heavy_imag_freqs = []
312 |
313 | if settings.DEBUG > 2:
314 | print("light imaginary frequencies: ", end=' ')
315 | if len(light_imag_freqs) == 0:
316 | print("none", end=' ')
317 | for i in light_imag_freqs:
318 | print("%.3f " % i, end=' ')
319 | print()
320 | print("light small frequencies: ", end=' ')
321 | for i in light_small_freqs:
322 | print("%.1f " % i, end=' ')
323 | print()
324 | print("heavy imaginary frequencies: ", end=' ')
325 | if len(heavy_imag_freqs) == 0:
326 | print("none", end=' ')
327 | for i in heavy_imag_freqs:
328 | print("%.3f " % i, end=' ')
329 | print()
330 | print("heavy small frequencies: ", end=' ')
331 | for i in heavy_small_freqs:
332 | print("%.1f " % i, end=' ')
333 | print()
334 |
335 | raw_imag_ratio = None
336 | imag_ratios = None
337 | try:
338 | raw_imag_ratio = light_imag_freqs[0]/heavy_imag_freqs[0]
339 | except IndexError:
340 | pass
341 |
342 | if raw_imag_ratio:
343 | wigner_imag_ratio = raw_imag_ratio * wigner(heavy_imag_freqs[0], light_imag_freqs[0], temperature)
344 | bell_imag_ratio = raw_imag_ratio * bell(heavy_imag_freqs[0], light_imag_freqs[0], temperature)
345 | imag_ratios = np.array([raw_imag_ratio, wigner_imag_ratio, bell_imag_ratio])
346 |
347 | partition_factors = partition_components(heavy_freqs, light_freqs, temperature)
348 |
349 | if settings.DEBUG >= 2:
350 | factors = np.prod(partition_factors, axis=0)
351 | print("{3: ^8}Product Factor: {0}\n{3: ^8}Excitation Factor: {1}\n{3: ^8}ZPE Factor: {2}".format(factors[0], factors[1], factors[2], ""))
352 |
353 | return (np.prod(partition_factors), imag_ratios, np.array(heavy_freqs), np.array(light_freqs))
354 |
355 | # calculates the Wigner tunnelling correction
356 | # multiplies the KIE by a factor of (1+u_H^2/24)/(1+u_D^2/24)
357 | # assumes the frequencies are sorted in ascending order
358 | def wigner(ts_imag_heavy, ts_imag_light, temperature):
359 | # calculate correction factor
360 | if ts_imag_heavy < 0.0 and ts_imag_light < 0.0:
361 | u_H = u(ts_imag_light, temperature)
362 | u_D = u(ts_imag_heavy, temperature)
363 | correction_factor = (1.0 + u_H**2 / 24.0) / (1.0 + u_D**2 / 24.0)
364 | else:
365 | raise ValueError("imaginary frequency passed to Wigner correction was real")
366 |
367 | return correction_factor
368 |
369 | # calculates the Bell inverted parabola tunneling correction
370 | # multiplies the KIE by a factor of (u_H/u_D)*(sin(u_D/2)/sin(u_H/2))
371 | # assumes the frequencies are sorted in ascending order
372 | def bell(ts_imag_heavy, ts_imag_light, temperature):
373 | # calculate correction factor
374 | if ts_imag_heavy < 0.0 and ts_imag_light < 0.0:
375 | u_H = u(ts_imag_light, temperature)
376 | u_D = u(ts_imag_heavy, temperature)
377 | correction_factor = (u_H/u_D)*(np.sin(u_D/2.0)/np.sin(u_H/2.0))
378 | else:
379 | raise ValueError("imaginary frequency passed to Bell correction was real")
380 | return correction_factor
381 |
--------------------------------------------------------------------------------
/src/orca.py:
--------------------------------------------------------------------------------
1 | import re
2 | import numpy as np
3 |
4 | from io import StringIO
5 |
6 | DEBUG = False
7 |
8 | elemToNum = {
9 | "H": 1, "He": 2,
10 | "Li": 3, "Be": 4, "B": 5, "C": 6, "N": 7, "O": 8, "F": 9, "Ne": 10,
11 | "Na": 11, "Mg": 12, "Al": 13, "Si": 14, "P": 15, "S": 16, "Cl": 17, "Ar": 18,
12 | "K": 19, "Ca": 20,
13 | "Sc": 21, "Ti": 22, "V": 23, "Cr": 24, "Mn": 25, "Fe": 26, "Co": 27, "Ni": 28, "Cu": 29, "Zn": 30,
14 | "Ga": 31, "Ge": 32, "As": 33, "Se": 34, "Br": 35, "Kr": 36,
15 | "Rb": 37, "Sr": 38,
16 | "Y": 39, "Zr": 40, "Nb": 41, "Mo": 42, "Tc": 43, "Ru": 44, "Rh": 45, "Pd": 46, "Ag": 47, "Cd": 48,
17 | "In": 49, "Sn": 50, "Sb": 51, "Te": 52, "I": 53, "Xe": 54,
18 | "Cs": 55, "Ba": 56,
19 | "La": 57, "Ce": 58, "Pr": 59, "Nd": 60, "Pm": 61, "Sm": 62, "Eu": 63, "Gd": 64, "Tb": 65, "Dy": 66, "Ho": 67, "Er": 68, "Tm": 69, "Yb": 70, "Lu": 71,
20 | "Hf": 72, "Ta": 73, "W": 74, "Re": 75, "Os": 76, "Ir": 77, "Pt": 78, "Au": 79, "Hg": 80,
21 | "Tl": 81, "Pb": 82, "Bi": 83, "Po": 84, "At": 85, "Rn": 86,
22 | "Fr": 87, "Ra": 88,
23 | "Ac": 89, "Th": 90, "Pa": 91, "U": 92, "Np": 93, "Pu": 94, "Am": 95, "Cm": 96, "Bk": 97, "Cf": 98, "Es": 99, "Fm": 100, "Md": 101, "No": 102, "Lr": 103,
24 | "Rf": 104, "Db": 105, "Sg": 106, "Bh": 107, "Hs": 108, "Mt": 109, "Ds": 110, "Rg": 111, "Cn": 112
25 | }
26 |
27 |
28 | def debug(s):
29 | if DEBUG:
30 | print("DEBUG: {}".format(s))
31 |
32 | def bohr2angstrom(length):
33 | return length*0.529177249
34 |
35 | def parse_orca_output(out_data):
36 | lines = out_data.split('\n')
37 |
38 | # Molecule position data
39 | try:
40 | atoms_line = next(
41 | (n for n, line in enumerate(lines)
42 | if line.startswith('$atoms')))
43 | except StopIteration:
44 | raise ValueError("Could not find '$atoms' in output data.")
45 |
46 | number_of_atoms = int(lines[atoms_line+1])
47 | debug("number_of_atoms={}".format(number_of_atoms))
48 |
49 | atomic_numbers = []
50 | positions = np.zeros(shape=(number_of_atoms, 3))
51 | for i, line in enumerate(
52 | lines[atoms_line+2:atoms_line+number_of_atoms+2]):
53 | _, atom, _, x, y, z = re.split(r'\s+', line)
54 |
55 | atomic_numbers.append(elemToNum[atom])
56 | positions[i][0] = bohr2angstrom(float(x))
57 | positions[i][1] = bohr2angstrom(float(y))
58 | positions[i][2] = bohr2angstrom(float(z))
59 |
60 | debug("atomic_numbers={}".format(atomic_numbers))
61 | debug("positions={}".format(positions))
62 |
63 | try:
64 | hessian_line = next(
65 | (n for n, line in enumerate(lines)
66 | if line.startswith('$hessian')))
67 | except StopIteration:
68 | raise ValueError("Could not find '$hessian' in output data.")
69 |
70 | hessian_size = int(lines[hessian_line+1])
71 | debug("Size of hessian: {}".format(hessian_size))
72 |
73 | hessian = None
74 |
75 | for i, batch in enumerate(range(0, hessian_size, 5)):
76 | batch_size = min(5, hessian_size-batch)
77 |
78 | s = StringIO(u"\n".join(
79 | lines[hessian_line+(i*(hessian_size+1)+2):
80 | hessian_line+((i+1)*(hessian_size+1))+2]))
81 |
82 | h = np.loadtxt(s, skiprows=1,
83 | converters=dict((i+1, float) for i in range(batch_size)),
84 | usecols=tuple(i+1 for i in range(batch_size)),
85 | ndmin=2)
86 |
87 | hessian = h if hessian is None else np.hstack((hessian, h))
88 |
89 | hessian = (hessian + hessian.T)/2
90 | debug("Hessian: {}".format(hessian))
91 |
92 | return atomic_numbers, positions, hessian
93 |
94 |
95 | if __name__ == '__main__':
96 | DEBUG = True
97 | import sys
98 | with open(sys.argv[1], 'r') as f:
99 | out_data = f.read()
100 | parse_orca_output(out_data)
101 |
--------------------------------------------------------------------------------
/src/quiver.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | import re
4 | import os
5 | import pickle
6 | import math
7 |
8 | import numpy as np
9 |
10 | import settings
11 | from utility import proj, normalize, test_orthogonality, schmidt
12 | from constants import DEFAULT_MASSES, PHYSICAL_CONSTANTS, LINEARITY_THRESHOLD, DROP_NUM_LINEAR
13 | from config import Config
14 |
15 | # represents a geometric arrangement of atoms with specific masses
16 | class Isotopologue(object):
17 | def __init__(self, id_, system, masses):
18 | self.name = id_
19 | self.system = system
20 | self.masses = masses
21 | self.frequencies = None
22 |
23 | self.number_of_atoms = system.number_of_atoms
24 | self.mw_hessian = self.calculate_mw_hessian()
25 |
26 | def __str__(self):
27 | returnString = "Isotopologue: %s\n" % self.name
28 | returnString += " masses: %s" % self.masses.__str__()
29 | return returnString
30 |
31 | def dump_debug(self, name, obj):
32 | pass
33 | #f = open(name+'_'+(self.system.filename)+'.pickle', 'w')
34 | #ret = pickle.dump(obj, f)
35 | #f.close()
36 |
37 | def calculate_mw_hessian(self):
38 | #### old code - this is very inefficient because we calculate sqrt about a zillion times
39 | #### better to call sqrt on the whole matrix and let numpy vectorize it
40 |
41 | #mw_hessian2 = np.zeros_like(hessian)
42 | #mass_weights2=[]
43 | #for i in range(0, 3*self.number_of_atoms):
44 | # for j in range(0, 3*self.number_of_atoms):
45 | # mass_weights2.append(1/(np.sqrt(masses3[i]*masses3[j])))
46 | # mw_hessian2[i,j] = hessian[i,j] / np.sqrt(masses3[i] * masses3[j] )
47 |
48 | #### new code, a bit more efficient
49 |
50 | # scatter masses into 2D array of combinations
51 | masses_ij = np.outer(np.array(self.masses), np.array(self.masses))
52 |
53 | # call sqrt once!
54 | # if we wanted we could take advantage of the symmetry of the matrix here...
55 | # but numpy is fast enough that it really doesn't matter
56 | inv_sqrt_masses = 1 / np.sqrt(masses_ij)
57 |
58 | # now we triple each dimension, to match the dimension of the hessian
59 | inv_sqrt_masses = np.repeat(inv_sqrt_masses, 3, 0)
60 | inv_sqrt_masses = np.repeat(inv_sqrt_masses, 3, 1)
61 |
62 | # and use matrix multiplication to generate mw_hessian
63 | mass_weights = np.ravel(inv_sqrt_masses)
64 | mw_hessian = self.system.hessian * inv_sqrt_masses
65 |
66 | #assert np.allclose(mw_hessian, mw_hessian2)
67 | #assert all([a == b for a, b in zip(mass_weights, mass_weights2)])
68 |
69 | if settings.DEBUG >= 3:
70 | self.dump_debug("mw", mass_weights)
71 | self.dump_debug("mw_hessian", mw_hessian)
72 |
73 | return mw_hessian
74 |
75 | def calculate_frequencies(self, imag_threshold, scaling=1.0, method="mass weighted hessian"):
76 | # short circuit if frequencies have already been calculated
77 | if self.frequencies is not None:
78 | return self.frequencies
79 |
80 | if method == "mass weighted hessian":
81 | imaginary_freqs = []
82 | small_freqs = []
83 | conv_factor = PHYSICAL_CONSTANTS['Eh']/(PHYSICAL_CONSTANTS['a0']**2 * PHYSICAL_CONSTANTS['amu'])
84 |
85 | v = np.linalg.eigvalsh(self.mw_hessian*conv_factor)
86 |
87 | constant = scaling / (2*np.pi*PHYSICAL_CONSTANTS['c'])
88 | freqs = np.sqrt(np.abs(v)) * np.sign(v) * constant
89 |
90 | #freqs2 = [ np.copysign(np.sqrt(np.abs(freq)),freq) * constant for freq in v ]
91 | #assert np.allclose(np.array(freqs), freqs2)
92 |
93 | freqs.sort()
94 |
95 | imaginary_freqs = []
96 | small_freqs = []
97 | regular_freqs = []
98 |
99 | # detect imaginary frequencies
100 | for f in freqs:
101 | if f < -imag_threshold:
102 | imaginary_freqs.append(f)
103 |
104 | if len(imaginary_freqs) > 1 and settings.DEBUG >= 2:
105 | print("WARNING: multiple imaginaries")
106 |
107 | # strip the imaginary frequencies
108 | freqs = freqs[len(imaginary_freqs):]
109 |
110 | if self.system.is_linear:
111 | small_freqs = freqs[:DROP_NUM_LINEAR]
112 | regular_freqs = freqs[DROP_NUM_LINEAR:]
113 | else:
114 | small_freqs = freqs[:1+DROP_NUM_LINEAR]
115 | regular_freqs = freqs[1+DROP_NUM_LINEAR:]
116 |
117 | # bugfix 2/6/20: third argument is regular_freqs, not freqs!
118 | self.frequencies = (small_freqs, imaginary_freqs, np.array(regular_freqs), len(small_freqs))
119 | if settings.DEBUG >= 3:
120 | self.dump_debug("freqs", self.frequencies)
121 | return self.frequencies
122 | else:
123 | raise ValueError("unknown frequency calculation type")
124 |
125 |
126 | class System(object):
127 | def __init__(self, outfile, style="g09"):
128 | self.positions_angstrom = False
129 | self.positions = False
130 | self.atomic_numbers = False
131 | self.number_of_atoms = False
132 | self.hessian = False
133 | self.is_linear = True
134 | self.filename = outfile
135 |
136 | valid_styles = ["g09", "pyquiver", "orca"]
137 | if style not in valid_styles:
138 | raise ValueError("specified style, {0}, not supported".format(style))
139 |
140 | if settings.DEBUG >= 1:
141 | print("Reading data from {0}... with style {1}".format(outfile, style))
142 |
143 | # assumes snip files worked correctly
144 | if style == "g09" and not outfile.endswith(".snip") and not "Normal termination" in tail(outfile):
145 | raise ValueError("Gaussian job %s terminated in an error" % outfile)
146 |
147 | self.filename = outfile
148 | with open(outfile, 'r') as f:
149 | out_data = f.read()
150 | if style == "pyquiver":
151 | lines = out_data.split("\n")
152 | try:
153 | number_of_atoms = int(lines[0])
154 | except ValueError:
155 | raise ValueError("first line must contain integer number of atoms.")
156 | self.number_of_atoms = number_of_atoms
157 |
158 | atomic_numbers = [0 for i in range(number_of_atoms)]
159 | positions = np.zeros(shape=(number_of_atoms,3))
160 |
161 | for l in lines[1:number_of_atoms+1]:
162 | fields = l.split(',')
163 | try:
164 | center_number, atomic_number, x, y, z = fields
165 | except ValueError:
166 | raise ValueError("the following line in the geometry did not have the appropriate number of fields: {0}".format(l))
167 | center_number = int(center_number)
168 | atomic_number = int(atomic_number)
169 |
170 | atomic_numbers[center_number] = atomic_number
171 | positions[center_number][0] = x
172 | positions[center_number][1] = y
173 | positions[center_number][2] = z
174 |
175 | fcm_fields = lines[number_of_atoms+1].split(',')
176 | hessian = self._parse_serial_lower_hessian(fcm_fields)
177 |
178 | if style == "g09":
179 | # check that the verbose output option has been set
180 | verbose_flag_present = re.search(r" *#[pP] ", out_data)
181 | if verbose_flag_present == None and not outfile.lower().endswith(".snip"):
182 | print()
183 | print("Error: Gaussian output file %s" % outfile)
184 | print("was not run with the verbose flag, so it does not contain enough information for")
185 | print("PyQuiver to run. Please re-run this calculation with a route card that starts with #p")
186 | print()
187 | sys.exit(1)
188 |
189 | # read in the number of atoms
190 | m = re.search("NAtoms\= +([0-9]+)", out_data)
191 | if m:
192 | number_of_atoms = int(m.group(1))
193 | else:
194 | raise AttributeError("Number of atoms not detected.")
195 | m = None
196 | self.number_of_atoms = number_of_atoms
197 |
198 | # read in the last geometry (assumed cartesian coordinates)
199 | atomic_numbers = [0 for i in range(number_of_atoms)]
200 | positions = np.zeros(shape=(number_of_atoms,3))
201 |
202 | # use standard orientation if possible
203 | for m in re.finditer("Standard orientation(.+?)Rotational constants \(GHZ\)", out_data, re.DOTALL):
204 | pass
205 |
206 | # for input files with nosymm keyword, use input orientation
207 | if m is None:
208 | for m in re.finditer("Input orientation(.+?)Distance matrix", out_data, re.DOTALL):
209 | pass
210 | if not m is None:
211 | print("Couldn't find standard orientation so used input orientation instead.")
212 |
213 | if m is None:
214 | for m in re.finditer("Input orientation(.+?)Rotational constants \(GHZ\)", out_data, re.DOTALL):
215 | pass
216 | if not m is None:
217 | print("Couldn't find standard orientation so used input orientation instead.")
218 |
219 | # still couldn't find any geometries
220 | if m is None:
221 | raise AttributeError("Geometry table not detected.")
222 |
223 | def valid_geom_line_p(split_line):
224 | if len(split_line) == 6:
225 | try:
226 | int(split_line[0])
227 | int(split_line[1])
228 | int(split_line[2])
229 | return True
230 | except ValueError:
231 | return False
232 | return False
233 |
234 | for l in m.group(1).split('\n'):
235 | raw_geom_line = l.split()
236 | raw_geom_line = [_f for _f in l.split(' ') if _f]
237 |
238 | if valid_geom_line_p(raw_geom_line):
239 | center_number = int(raw_geom_line[0]) - 1
240 | atomic_numbers[center_number] = int(raw_geom_line[1])
241 | for e in range(0,3):
242 | positions[center_number][e] = raw_geom_line[3+e]
243 |
244 | # units = hartrees/bohr^2
245 | hessian = self._parse_g09_hessian(out_data)
246 |
247 | elif style == "orca":
248 | from orca import parse_orca_output
249 | atomic_numbers, positions, hessian = parse_orca_output(out_data)
250 | self.number_of_atoms = len(atomic_numbers)
251 |
252 |
253 | # copy fields
254 | self.hessian = hessian
255 |
256 | self.positions_angstrom = positions
257 | # convert position in angstroms to needed bohr
258 | self.positions = positions/PHYSICAL_CONSTANTS['atb']
259 |
260 | # detect if molecule is linear
261 | # method: take every difference of two centers (that share center 0)
262 | # calculate the dot product with other differences
263 |
264 | if self.number_of_atoms > 2:
265 | detected_indep = 0
266 | for i in range(1,len(positions)-1):
267 | for j in range(i+1, len(positions)):
268 | # compares how parallel the unit vectors are
269 | diff0i = positions[0] - positions[i]
270 | diff0j = positions[0] - positions[j]
271 | u = diff0i/np.linalg.norm(diff0i)
272 | v = diff0j/np.linalg.norm(diff0j)
273 | # if the vectors are (probably) linearly indep, we break
274 | if 1 - np.abs(np.inner(u,v)) > LINEARITY_THRESHOLD:
275 | self.is_linear = False
276 | detected_indep = 1
277 | break
278 |
279 | if detected_indep:
280 | break
281 | linear_string = "linear" if self.is_linear else "not linear"
282 | if settings.DEBUG >= 2:
283 | print("Molecule is %s." % linear_string)
284 | self.atomic_numbers = atomic_numbers
285 |
286 | def dump_debug(self, obj):
287 | f = open('freqs_'+self.name+'.json', 'w')
288 | out = json.dumps(self.frequencies)
289 | f.write(out)
290 | f.close()
291 |
292 | def _lower_triangle_serial_triangle(self,row,col):
293 | if col > row:
294 | return self._lower_triangle_serial_triangle(col,row)
295 | triangle = lambda n: n*(n+1)/2
296 | return int(triangle(row) + col)
297 |
298 | # search for the archive at the end of the file
299 | # then extract the force constant matrix
300 | def _parse_g09_hessian(self, data):
301 | #print("\n\nparsing\n\n")
302 | # regex for finding text between 1\1\GINC and \@
303 | # DOTALL means that . will match newlines
304 | # there are two capture groups here, which is why we have to use archive[0] later
305 | raw_archive = re.findall(r"1\\1\\GINC(.+?)\\(\s*)@", data, re.DOTALL)
306 | found_frequencies = False
307 | for archive in raw_archive:
308 | archive = re.sub('[\s+]', '', archive[0])
309 | #print(archive[:1000])
310 | #print("...")
311 | #print(archive[-1000:])
312 | #print("---")
313 | archive = re.search("NImag\=(.+?)$", archive, re.DOTALL)
314 | #print(archive)
315 | #print("*")
316 | #print()
317 | if archive:
318 | found_frequencies = True
319 | break
320 | if not found_frequencies:
321 | raise ValueError(f"No frequency job detected in {self.filename}.")
322 |
323 | raw_fcm = archive.group(0).split('\\')[2].split(',')
324 | #print(raw_fcm)
325 | self.raw_fcm = raw_fcm
326 | fcm = self._parse_serial_lower_hessian(raw_fcm)
327 | #print("\n\nsuccess\n\n")
328 | return fcm
329 |
330 | def _parse_serial_lower_hessian(self, fields):
331 | #fcm = np.zeros(shape=(3*self.number_of_atoms, 3*self.number_of_atoms))
332 | #for i in range(3*self.number_of_atoms):
333 | # for j in range(3*self.number_of_atoms):
334 | # fcm[i,j] = fields[self._lower_triangle_serial_triangle(i,j)]
335 |
336 | # compute index over entire grid at once
337 | range1d = np.array(range(3*self.number_of_atoms), dtype=int)
338 | xgrid, ygrid = np.meshgrid(range1d, range1d)
339 | agrid = np.minimum(xgrid, ygrid)
340 | bgrid = np.maximum(xgrid, ygrid)
341 | idxs2 = (bgrid*(bgrid+1)/2 + agrid).astype(int)
342 |
343 | fcm2 = np.array(fields, dtype=float)[idxs2]
344 | #assert np.allclose(fcm, fcm2)
345 |
346 | return fcm2
347 |
348 | def _make_serial_hessian(self):
349 | serial = ""
350 | for i in range(self.number_of_atoms*3):
351 | for j in range(0,i+1):
352 | serial += str(self.hessian[i,j]) + ","
353 | return serial
354 |
355 | def _make_serial_geometry(self):
356 | serial = ""
357 | for i in range(self.number_of_atoms):
358 | serial += "{0},{1},{2},{3},{4}\n".format(i, self.atomic_numbers[i], self.positions_angstrom[i,0], self.positions_angstrom[i,1], self.positions_angstrom[i,2])
359 | return serial
360 |
361 |
362 | def dump_pyquiver_input_file(self, extension=".qin"):
363 | path = os.path.splitext(self.filename)[0] + extension
364 | serial = str(self.number_of_atoms) + "\n"
365 | serial += self._make_serial_geometry()
366 | serial += self._make_serial_hessian()
367 |
368 | with open(path, 'w') as f:
369 | f.write(serial)
370 |
371 | return serial
372 |
373 | if __name__ == "__main__":
374 | parser = argparse.ArgumentParser(description="PyQuiver calculates KIEs and EIEs based on a ground and transition state file.")
375 | parser.add_argument('-v', '--verbose', dest="debug", help='when the verbose flag is set debug information is printed', action='count')
376 | parser.add_argument('-s', '--style', dest="style", default='g09', help='style of input files (g09, orca, or pyquiver)')
377 | parser.add_argument('config', help='configuration file path')
378 | parser.add_argument('gs', help='ground state file path')
379 | parser.add_argument('ts', help='transition state file path')
380 |
381 | args = parser.parse_args()
382 | if args.debug:
383 | settings.DEBUG = args.debug + 1
384 |
385 | from kie import KIE_Calculation
386 | calc = KIE_Calculation(args.config, args.gs, args.ts, style=args.style)
387 | print(calc)
388 |
389 |
390 | def slugify(value):
391 | return "".join(x for x in value if x.isalnum())
392 |
393 | def tail(filename):
394 | # with open(filename) as f:
395 | # content = f.readlines()
396 | # return content[-1].strip()
397 |
398 | # https://stackoverflow.com/questions/46258499/how-to-read-the-last-line-of-a-file-in-python
399 | with open(filename, 'rb') as f:
400 | try: # catch OSError in case of a one line file
401 | f.seek(-2, os.SEEK_END)
402 | while f.read(1) != b'\n':
403 | f.seek(-2, os.SEEK_CUR)
404 | except OSError:
405 | f.seek(0)
406 | return f.readline().decode()
407 |
--------------------------------------------------------------------------------
/src/settings.py:
--------------------------------------------------------------------------------
1 | # DEBUG global variable
2 | # 3 = dump files into cwd
3 | # 2 = extra verbose - corresponds to -v flag on quiver or -vv flag on auto
4 | # 1 = reasonable level of printing
5 | # 0 = minimal printing
6 | DEBUG = 0
7 |
--------------------------------------------------------------------------------
/src/utility.py:
--------------------------------------------------------------------------------
1 | ### This file contains utility functions used in quiver calculations.
2 |
3 | # Linear Algebra utility function
4 |
5 | def proj(u,v):
6 | # project u onto v
7 | return np.inner(v,u)/np.inner(u,u) * u
8 |
9 | def normalize(v):
10 | norm=np.linalg.norm(v)
11 | if norm < 1.0E-5:
12 | raise ValueError
13 | return v/norm
14 |
15 | def test_orthogonality(vectors):
16 | mat = np.zeros(shape=(len(vectors),len(vectors)))
17 | for i,u in enumerate(vectors):
18 | for j,v in enumerate(vectors):
19 | inner = np.inner(u,v)
20 | if inner != 0.0 or inner != 1.0:
21 | mat[i][j] = inner
22 | print("Orthogonality:")
23 | for l in mat:
24 | print(l)
25 |
26 | def schmidt(seed_vectors, rest_vectors, dimension):
27 | vectors = list(seed_vectors)
28 | test_vectors = list(rest_vectors)
29 | while len(vectors) < dimension:
30 | try:
31 | test_vector = test_vectors.pop()
32 | for v in vectors:
33 | test_vector -= proj(v, test_vector)
34 | try:
35 | vectors.append(normalize(test_vector))
36 | except ValueError:
37 | pass
38 |
39 | except IndexError:
40 | raise ValueError("Could not fill the appropriate dimensional space")
41 | if len(vectors) < dimension:
42 | raise ValueError("Could not fill the appropriate dimensional space")
43 | else:
44 | return vectors
45 |
--------------------------------------------------------------------------------
/src/weights.dat:
--------------------------------------------------------------------------------
1 | # This file stores the atomic weights and masses that will be used in the mass-weighted Hessian calculation.
2 | #
3 | # format is:
4 | # element_name,atomic_number,atomic_symbol,atomic_weight, [atomic_number_of_replacement, symbol_of_replacement]
5 | #
6 | # if no replacements are specified, the atomic weight will be used to calculate the frequencies and
7 | # no KIEs can be calculated at that position
8 | #
9 | # comment lines begin with # and will be ignored
10 | # anything after a # is also ignored
11 | #
12 | # choices here are taken to match those of quiver
13 | # otherwise, data taken from http://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl
14 | #
15 | # as a safety feature, you must specify the data in ascending order of atomic number
16 | #hydrogen,1,H,1.00783,1H,1.00783,2D,2.0141,3T,3.016049
17 | hydrogen,1,H,1.0078250,1H,1.00783,2D,2.0141,3T,3.016049
18 | lithium,3,Li,6.94
19 | boron,5,B,10.811
20 | carbon,6,C,12.0,12C,12.0,13C,13.00335,14C,14.0031
21 | nitrogen,7,N,14.0067,14N,14.0031,15N,15.0001
22 | #oxygen,8,O,15.9994,16O,15.9949,17O,16.9991,18O,17.9992
23 | oxygen,8,O,15.9949146,16O,15.9949,17O,16.9991,18O,17.9992
24 | fluorine,9,F,18.9984,18F,18.0009,19F,18.9984
25 | sodium,11,Na,22.9898
26 | magnesium,12,Mg,24.305
27 | aluminum,13,Al,26.9815
28 | silicon,14,Si,28.0855
29 | phosphorus,15,P,30.9738
30 | sulfur,16,S,32.453
31 | chlorine,17,Cl,35.453
32 | potassium,19,K,39.0983
33 | titanium,22,Ti,47.867
34 | vanadium,23,V,50.9439595
35 | iron,26,Fe,55.845
36 | cobalt,27,Co,58.9332
37 | nickel,28,Ni,58.6934
38 | copper,29,Cu,63.546
39 | zinc,30,Zn,65.39
40 | selenium,34,Se,78.96
41 | bromine,35,Br,79.9
42 | rhodium,45,Rh,102.9055
43 | palladium,46,Pd,106.42
44 | silver,47,Ag,107.8682
45 | tin,50,Sn,118.71
46 | iodine,53,I,126.9045
47 | cesium,55,Cs,132.9055
48 | platinum,78,Pt,195.078
49 | gold,79,Au,196.9665
50 |
--------------------------------------------------------------------------------
/tutorial/TUTORIAL.md:
--------------------------------------------------------------------------------
1 | ## Tutorial
2 |
3 | In this tutorial, we reproduce the B3LYP/6-31G* KIE predictions for the following Claisen rearrangement reported by Singleton ([reference 5](../DETAILS.md#references)):
4 |
5 |
6 |
7 |
8 |
9 | (This is Table 4 in the paper.)
10 |
11 | ### File Locations
12 |
13 | All the files associated with this tutorial are available in the `tutorial/` directory. There are separate folders for running with Gaussian, ORCA, or PyQuiver standard files.
14 |
15 | Here, we'll focus on the use of PyQuiver with Gaussian. This tutorial requires the *PyQuiver* configuration file `claisen_demo.config` and the g09 output files `claisen_gs.out` and `claisen_ts.out`, representing the ground and transition state frequency calculations, respectively.
16 |
17 | If running with ORCA, use the `.hess` files instead of the output files. If running with Quiver Standard files, use the `.qin` files.
18 |
19 | Note that the tutorial results will differ slightly with the provided ORCA files due to subtle differences in geometry. Thse differences are not experimentally meaningful and can be ignored.
20 |
21 | ### Getting Started
22 |
23 | In general, all KIEs are defined as rate constant(light)/rate constant(heavy). For example, the absolute KIE at C1 is defined as the rate of the rearrangement with carbon-12 at C1 divided by the rate with carbon-13 at C1. This definition is given by this line of the `claisen_demo.config` file:
24 |
25 | ```
26 | isotopomer C1 1 1 13C
27 | ```
28 |
29 | `C1` is an arbitrary label for the KIE of interest (it can be any string without a space character). In general, we may want to calculate multiple KIEs using one configuration file. For example, the next few lines define the KIEs at C2 and the oxygen:
30 |
31 | ```
32 | isotopomer C2 2 2 13C
33 | isotopomer O3 3 3 17O
34 | ```
35 |
36 | In each case, the isotopomer definition is followed by three parameters: the atom number in the ground state, the atom number in the transition state, and the isotope to substitute with. For example, for C1, atom 1 in the ground state and atom 1 in the transition state will be substituted with carbon-13. In general, *PyQuiver* will try to prevent you from entering isotopomers that do not make sense.
37 |
38 | The definition of `13C` is drawn from `src/weights.dat`:
39 |
40 | ```
41 | carbon,6,C,12.0,12C,12.0,13C,13.00335,14C,14.0031
42 | ```
43 |
44 | In English, this says that carbon has an atomic number of 6 and has the symbol `C`. In all cases (`C1`, `C2`, `O`, etc.), whenever carbon appears in the "light" isotopomer, it is defined to have a mass of `12.0`. This is called the "default mass." If the "heavy" replacement is specified as `13C`, it is given a mass of `13.00335`. Although a number of common replacements are already defined (e.g., `2D` (deuterium) or `18O` (oxygen-18)), it is easy to add more definitions.
45 |
46 | Note that KIEs can be defined for multiple isotopic replacements by repeating the label of the isotopomer. For example, these entries replace two hydrogens with two deuteriums:
47 |
48 | ```
49 | isotopomer H/D 7 7 2D
50 | isotopomer H/D 8 8 2D
51 | ```
52 |
53 | Now we are ready to calculate the KIEs! Enter in the following:
54 |
55 | ```
56 | cd src/
57 | python quiver.py ../tutorial/gaussian/claisen_demo.config ../tutorial/gaussian/claisen_gs.out ../tutorial/gaussian/claisen_ts.out
58 | ```
59 |
60 | When run from the command line, *PyQuiver* expects the names (in order) of the configuration file, the ground state file, and the transition state file. The expected output is:
61 |
62 | ```
63 | Read atomic weight data for 30 elements.
64 |
65 | Reading configuration from claisen_demo.config
66 | Reading data from claisen_gs.out... with style g09
67 | Reading data from claisen_ts.out... with style g09
68 | Config file: claisen_demo.config
69 | Temperature: 393.0 K
70 | Scaling: 0.961
71 | Reference Isotopologue: C5
72 | Imag threshold (cm-1): 50
73 | Isotopologue C5, replacement 1: replace gs atom 5 and ts atom 5 with 13C
74 | Isotopologue C1, replacement 1: replace gs atom 1 and ts atom 1 with 13C
75 | Isotopologue C2, replacement 1: replace gs atom 2 and ts atom 2 with 13C
76 | Isotopologue C4, replacement 1: replace gs atom 4 and ts atom 4 with 13C
77 | Isotopologue C6, replacement 1: replace gs atom 6 and ts atom 6 with 13C
78 | Isotopologue H/D, replacement 1: replace gs atom 7 and ts atom 7 with 2D
79 | Isotopologue H/D, replacement 2: replace gs atom 8 and ts atom 8 with 2D
80 | Isotopologue O3, replacement 1: replace gs atom 3 and ts atom 3 with 17O
81 |
82 | === PyQuiver Analysis ===
83 | Isotopologue uncorrected Wigner inverted parabola
84 | KIE KIE KIE
85 | Isotopologue C1 1.0091 1.0107 1.0110
86 | Isotopologue C2 1.0000 1.0000 1.0000
87 | Isotopologue O3 1.0153 1.0168 1.0171
88 | Isotopologue C4 1.0249 1.0276 1.0281
89 | Isotopologue C6 1.0111 1.0128 1.0131
90 | Isotopologue H/D 0.9515 0.9529 0.9532
91 |
92 | KIEs referenced to isotopologue C5, whose absolute KIEs are:
93 | Isotopologue C5 1.0019 1.0019 1.0019
94 | ```
95 |
96 | Note that these KIEs are *relative* to the KIE at `C5`. This is controlled by this line of the config file:
97 |
98 | ```
99 | reference_isotopomer C5
100 | ```
101 |
102 | This means that all absolute KIEs will be divided by this one to give relative KIEs. Use `none` to calculate absolute KIEs only.
103 |
104 | These numbers agree closely with the predictions reported by Singleton. There are small (0.001) differences that arise from roundoff errors, differing values of physical constants, slight changes in the way masses are handled, and the way the original QUIVER program handles small rotational/translational frequencies with a threshold system. These small differences should not affect any chemical conclusions.
105 |
106 | ### Summary
107 |
108 | The above captures the basic workflow of a *PyQuiver* calculation:
109 |
110 | * locate ground and transition states (if using Gaussian, turn on the verbose `#p` flag)
111 | * run a frequency calculations
112 | * specify the desired isotopic substitutions in a configuration file
113 | * run `python quiver.py` on the configuration, ground state, and transition state files
114 |
115 | If EIEs are desired, simply replace the transition state with the equilibrium state of interest.
116 |
--------------------------------------------------------------------------------
/tutorial/gaussian/claisen_demo.config:
--------------------------------------------------------------------------------
1 | # This file will control the behavior of PyQuiver.
2 | # Blank lines and lines starting with # will be ignored.
3 |
4 | # scaling factor for frequencies
5 | # frequencies will be multiplied by this value
6 | scaling 0.9614
7 |
8 | ### deprecated: this keyword will now be ignored ###
9 | #frequency_threshold 50
10 |
11 | # imaginaries less than this value in i*cm-1 will be ignored for the transition mode
12 | imag_threshold 50
13 |
14 | # temperature in K
15 | temperature 393
16 |
17 | # specifies the masses used for the light isotopomer
18 | # specify the name of an isotopomer or
19 | # use "default" to use the default masses in weights.dat
20 | mass_override_isotopologue default
21 |
22 | # all KIEs will be divided by the KIE at this position
23 | # specify the name of an isotopomer or
24 | # use "default" or "none" to skip this step
25 | reference_isotopomer C5
26 | # Uncomment to calculate absolute KIEs only.
27 | #reference_isotopomer none
28 |
29 | # define the isotopomers here
30 | #
31 | # isotopomer name, atom number in ground state, atom number in transition state, valid replacement atomic weight (must be specified in weights.dat)
32 | #
33 | # for example, 'isotopomer abc 2 4 C13' replaces atom 2 in the gs and atom 4 in the ts with carbon-13
34 | # and calls the resulting KIE "abc"
35 | #
36 | # add additional isotopomer lines with the same name to give multiple replacements within a single isotopomer
37 |
38 | # the name reference was selected above as the reference isotopomer
39 |
40 | isotopomer C1 1 1 13C
41 | isotopomer C2 2 2 13C
42 | isotopomer O3 3 3 17O
43 | isotopomer C4 4 4 13C
44 |
45 | # this is identical to the reference isotopomer and therefore ought to produce a value of 1.00 (which it does)
46 | isotopomer C5 5 5 13C
47 | isotopomer C6 6 6 13C
48 |
49 | # example of doing a multiple replacement
50 | isotopomer H/D 7 7 2D
51 | isotopomer H/D 8 8 2D
52 |
--------------------------------------------------------------------------------
/tutorial/orca/claisen_demo.config:
--------------------------------------------------------------------------------
1 | # This file will control the behavior of PyQuiver.
2 | # Blank lines and lines starting with # will be ignored.
3 |
4 | # scaling factor for frequencies
5 | # frequencies will be multiplied by this value
6 | scaling 0.9614
7 |
8 | ### deprecated: this keyword will now be ignored ###
9 | #frequency_threshold 50
10 |
11 | # imaginaries less than this value in i*cm-1 will be ignored for the transition mode
12 | imag_threshold 50
13 |
14 | # temperature in K
15 | temperature 393
16 |
17 | # specifies the masses used for the light isotopomer
18 | # specify the name of an isotopomer or
19 | # use "default" to use the default masses in weights.dat
20 | mass_override_isotopologue default
21 |
22 | # all KIEs will be divided by the KIE at this position
23 | # specify the name of an isotopomer or
24 | # use "default" or "none" to skip this step
25 | reference_isotopomer C5
26 | # Uncomment to calculate absolute KIEs only.
27 | #reference_isotopomer none
28 |
29 | # define the isotopomers here
30 | #
31 | # isotopomer name, atom number in ground state, atom number in transition state, valid replacement atomic weight (must be specified in weights.dat)
32 | #
33 | # for example, 'isotopomer abc 2 4 C13' replaces atom 2 in the gs and atom 4 in the ts with carbon-13
34 | # and calls the resulting KIE "abc"
35 | #
36 | # add additional isotopomer lines with the same name to give multiple replacements within a single isotopomer
37 |
38 | # the name reference was selected above as the reference isotopomer
39 |
40 | isotopomer C1 1 1 13C
41 | isotopomer C2 2 2 13C
42 | isotopomer O3 3 3 17O
43 | isotopomer C4 4 4 13C
44 |
45 | # this is identical to the reference isotopomer and therefore ought to produce a value of 1.00 (which it does)
46 | isotopomer C5 5 5 13C
47 | isotopomer C6 6 6 13C
48 |
49 | # example of doing a multiple replacement
50 | isotopomer H/D 7 7 2D
51 | isotopomer H/D 8 8 2D
52 |
--------------------------------------------------------------------------------
/tutorial/orca/claisen_gs.xyz:
--------------------------------------------------------------------------------
1 | 14
2 | claisen_gs
3 | C 2.533366 0.354528 -0.000003
4 | C 1.645119 -0.643361 -0.000007
5 | O 0.289757 -0.572633 -0.000001
6 | C -0.300955 0.716222 -0.000005
7 | C -1.797864 0.595382 -0.000005
8 | C -2.478428 -0.549716 -0.000009
9 | H 3.589823 0.107677 -0.000008
10 | H 2.263112 1.404966 0.000008
11 | H 1.954434 -1.685673 -0.000016
12 | H 0.038878 1.281578 -0.884124
13 | H 0.038876 1.281582 0.884111
14 | H -2.323822 1.550451 0.000000
15 | H -3.565344 -0.560946 -0.000007
16 | H -1.969201 -1.507986 -0.000015
17 |
--------------------------------------------------------------------------------
/tutorial/orca/claisen_gs_freq.inp:
--------------------------------------------------------------------------------
1 | !B3LYP 6-31G* RIJCOSX def2/J Freq
2 | !pal4
3 | *xyzfile 0 1 claisen_gs.xyz
4 |
--------------------------------------------------------------------------------
/tutorial/orca/claisen_ts.xyz:
--------------------------------------------------------------------------------
1 | 14
2 | claisen_ts
3 | C -1.474032 0.786774 -0.283331
4 | C -1.296078 -0.476777 0.256320
5 | O -0.512902 -1.359242 -0.257853
6 | C 1.291759 -0.901845 0.187721
7 | C 1.379160 0.410519 -0.300035
8 | C 0.673744 1.429519 0.315326
9 | H -2.153042 1.487010 0.198833
10 | H -1.239230 0.973314 -1.325004
11 | H -1.694692 -0.663780 1.269444
12 | H 1.774509 -1.712494 -0.347055
13 | H 1.175597 -1.072994 1.253923
14 | H 1.706592 0.552626 -1.328300
15 | H 0.639070 2.423886 -0.122641
16 | H 0.448795 1.383584 1.376553
17 |
--------------------------------------------------------------------------------
/tutorial/orca/claisen_ts_freq.inp:
--------------------------------------------------------------------------------
1 | !B3LYP 6-31G* RIJCOSX def2/J Freq
2 | !pal4
3 | *xyzfile 0 1 claisen_ts.xyz
4 |
--------------------------------------------------------------------------------
/tutorial/pyquiver/claisen_demo.config:
--------------------------------------------------------------------------------
1 | # This file will control the behavior of PyQuiver.
2 | # Blank lines and lines starting with # will be ignored.
3 |
4 | # scaling factor for frequencies
5 | # frequencies will be multiplied by this value
6 | scaling 0.9614
7 |
8 | ### deprecated: this keyword will now be ignored ###
9 | #frequency_threshold 50
10 |
11 | # imaginaries less than this value in i*cm-1 will be ignored for the transition mode
12 | imag_threshold 50
13 |
14 | # temperature in K
15 | temperature 393
16 |
17 | # specifies the masses used for the light isotopomer
18 | # specify the name of an isotopomer or
19 | # use "default" to use the default masses in weights.dat
20 | mass_override_isotopologue default
21 |
22 | # all KIEs will be divided by the KIE at this position
23 | # specify the name of an isotopomer or
24 | # use "default" or "none" to skip this step
25 | reference_isotopomer C5
26 | # Uncomment to calculate absolute KIEs only.
27 | #reference_isotopomer none
28 |
29 | # define the isotopomers here
30 | #
31 | # isotopomer name, atom number in ground state, atom number in transition state, valid replacement atomic weight (must be specified in weights.dat)
32 | #
33 | # for example, 'isotopomer abc 2 4 C13' replaces atom 2 in the gs and atom 4 in the ts with carbon-13
34 | # and calls the resulting KIE "abc"
35 | #
36 | # add additional isotopomer lines with the same name to give multiple replacements within a single isotopomer
37 |
38 | # the name reference was selected above as the reference isotopomer
39 |
40 | isotopomer C1 1 1 13C
41 | isotopomer C2 2 2 13C
42 | isotopomer O3 3 3 17O
43 | isotopomer C4 4 4 13C
44 |
45 | # this is identical to the reference isotopomer and therefore ought to produce a value of 1.00 (which it does)
46 | isotopomer C5 5 5 13C
47 | isotopomer C6 6 6 13C
48 |
49 | # example of doing a multiple replacement
50 | isotopomer H/D 7 7 2D
51 | isotopomer H/D 8 8 2D
52 |
--------------------------------------------------------------------------------
/tutorial/pyquiver/claisen_gs.qin:
--------------------------------------------------------------------------------
1 | 14
2 | 0,6,2.529357,0.36133,-2e-05
3 | 1,6,1.650985,-0.646172,-2e-06
4 | 2,8,0.294333,-0.586301,2.9e-05
5 | 3,6,-0.296838,0.704706,2.1e-05
6 | 4,6,-1.792958,0.591589,-9e-06
7 | 5,6,-2.486162,-0.545043,-1.7e-05
8 | 6,1,3.588621,0.131739,-4.5e-05
9 | 7,1,2.248176,1.40772,-1.3e-05
10 | 8,1,1.967584,-1.685756,-9e-06
11 | 9,1,0.043549,1.268971,-0.883806
12 | 10,1,0.043514,1.268967,0.883863
13 | 11,1,-2.309465,1.551442,-2e-05
14 | 12,1,-3.572272,-0.540878,-3.7e-05
15 | 13,1,-1.990668,-1.510251,-4e-06
16 | 0.74230619,0.09529476,0.77708958,-1.491e-05,-1.33e-06,0.08257369,-0.28456899,-0.20271375,5e-06,0.70304478,-0.20577085,-0.38135502,4.32e-06,0.11298793,0.84228138,5.12e-06,4.1e-06,-0.04827938,-1.206e-05,-2.97e-06,0.13540834,-0.05484909,-0.00871881,1.52e-06,-0.3155381,-0.01124513,6.64e-06,0.52342495,-0.02617547,0.01424471,5.9e-07,0.02363705,-0.11163132,-1.3e-07,-0.08433554,0.37527311,1.21e-06,3.9e-07,0.01172492,5.8e-06,8.3e-07,-0.04564858,-8.38e-06,-1.89e-06,0.07882105,0.00266724,0.00186237,-6e-08,-0.03710831,0.04392886,7e-08,-0.09730855,0.01566473,7.7e-07,0.53316703,-0.01304485,0.00321988,3.5e-07,0.02691053,-0.00987549,-4e-07,0.04525067,-0.20216681,3.2e-07,0.03590686,0.50063556,-5e-08,-1.4e-07,-0.00158362,1.5e-07,-9.7e-07,-0.00301636,9e-08,1.76e-06,-0.0682566,-1.73e-06,1.42e-06,0.55973916,0.00039868,0.00058112,0.0,-0.00073999,0.00041141,-1.6e-07,-0.02289439,0.00963081,-3e-07,-0.21897493,-0.00833414,-2.88e-06,0.62781544,-0.00023844,-0.00027274,-3e-08,-0.00168271,-0.00740187,3.2e-07,0.04925904,-0.00388215,5.1e-07,-0.02156852,-0.09131716,-2.1e-07,0.11252764,0.84124942,7e-08,-4e-08,0.00046045,2e-07,0.0,5.151e-05,-4.5e-07,2e-07,0.00211877,-2.88e-06,-4e-08,-0.07618293,8.55e-06,2e-07,0.14246815,-0.00052318,2.505e-05,0.0,0.00022051,-0.00113669,-1e-08,0.0009982,-0.00202618,5e-08,-0.02075263,-0.03122247,-3.2e-07,-0.23614687,-0.20511284,-2.75e-06,0.71537499,-0.00010622,-0.00015564,0.0,0.00104342,-0.000161,-2e-08,-0.00124758,-0.00086342,0.0,-0.01042053,0.00346079,-1.7e-07,-0.19997456,-0.45401901,-2.17e-06,0.09711983,0.81360047,-1e-08,0.0,6.907e-05,0.0,-2e-08,-0.00011752,1e-08,0.0,0.00036836,-5.2e-07,-6.7e-07,0.00593798,-2.69e-06,-2.15e-06,-0.04684332,1.092e-05,-1.01e-06,0.10434748,-0.33586344,0.06123752,7.32e-06,-0.0177423,0.00877674,3.6e-07,5.354e-05,-0.00285066,2.5e-07,0.00085008,-0.00110173,0.0,6.057e-05,8.04e-06,0.0,-0.00010546,-3.246e-05,0.0,0.35503734,0.0670774,-0.07013313,-1.62e-06,-0.02547168,0.00844064,6e-07,-0.00414368,-0.00315285,7e-08,0.00127663,-0.00206353,6e-08,-0.00021381,0.00055069,-3e-08,0.00016753,-4.436e-05,0.0,-0.06565159,0.06706971,7.45e-06,-1.44e-06,-0.02340353,1.9e-07,-2.8e-07,-0.00380091,2.9e-07,1.8e-07,0.01188271,-8e-08,-3e-08,0.00095079,0.0,-4e-08,0.00024163,0.0,0.0,-6.68e-06,-7.88e-06,1.59e-06,0.02012529,-0.07812401,0.07685992,1.31e-06,0.01140799,-0.0215606,-2.1e-07,0.00082339,0.00246423,-1.4e-07,-0.001965,-0.00083855,-3e-08,-0.00018258,-0.00024748,0.0,8.054e-05,2.636e-05,0.0,-0.00305901,0.02700544,1e-07,0.07552879,0.07163049,-0.3275014,-1.92e-06,0.01012707,-0.01988579,-2.2e-07,0.00095827,0.00167463,-3e-08,0.00111958,-0.0007768,1e-07,-0.00025647,0.00098243,-4e-08,0.00036347,-7.571e-05,0.0,-0.00018694,-0.00200388,2e-08,-0.08116076,0.34835311,1.29e-06,-1.94e-06,-0.02703889,-8e-08,5e-07,0.00450606,-2.2e-07,-1e-07,-0.00565882,0.0,1e-08,-0.00097856,2e-08,4e-08,-0.0006078,0.0,0.0,-1.448e-05,8e-08,-6.5e-07,0.00020147,-1.22e-06,2.1e-06,0.01942911,0.00971839,-0.02025405,-1.1e-07,-0.07069332,0.06696124,7.4e-07,-0.02576705,0.03939997,3.4e-07,-0.00404762,-0.00305432,3.5e-07,-8.733e-05,0.00189164,2e-08,-2.39e-05,5.131e-05,0.0,0.00072463,2.805e-05,-1.4e-07,-0.00224613,-0.00323579,2.7e-07,0.09325234,0.00771224,-0.01799486,-1.8e-07,0.07297591,-0.32214518,-1.59e-06,0.00592869,0.00028651,-7e-08,-0.00113712,0.0035218,-3e-08,-0.0001076,-0.00026323,-2e-08,8.908e-05,5.222e-05,0.0,-0.00036096,0.00082157,0.0,-0.00285191,-0.00131517,8e-08,-0.08217533,0.33727977,-1.5e-07,2.9e-07,0.00616416,7.4e-07,-1.44e-06,-0.04139622,6.2e-07,-8.7e-07,0.00078558,1.2e-07,1.8e-07,0.0040008,0.0,-8e-08,0.00027034,0.0,0.0,-4.279e-05,-1.5e-07,1e-08,-0.00588242,2.6e-07,7e-08,0.0103025,-1.42e-06,1.8e-06,0.02599702,-0.00044948,-0.00206844,3.91e-05,0.00537277,0.00235403,-0.00161223,0.00079321,0.01364636,-0.01354133,-0.07495745,-0.03841715,0.06832437,-0.013104,-0.01926472,0.02629488,-0.00298613,0.00228375,1.225e-05,4.408e-05,-2.93e-05,0.00078551,-0.00116736,0.00031705,-0.0016661,-7.419e-05,-8.404e-05,0.000518,0.08175683,0.00148803,0.00143336,-0.00054752,-0.00812643,9.427e-05,0.00053116,0.003593,-0.03636867,0.02677514,-0.03344531,-0.10072535,0.08926957,-0.00546302,-0.000415,0.00280248,0.00045927,0.00118084,-4.407e-05,3.72e-05,0.00019982,-0.00013511,0.00015556,0.00025178,0.00042369,0.00031726,-0.0001069,-0.00055617,0.03748573,0.12448224,-0.0002016,0.00093363,-0.00025596,0.00201701,-0.00013508,0.00090199,-0.0053682,0.00422267,0.00674284,0.06238111,0.0929774,-0.21410871,0.0002197,-0.00143797,0.00398842,-0.00053256,-0.00089619,-6.789e-05,4.832e-05,-0.00050505,-0.00012731,-0.00017568,-0.00071185,-0.00014206,-0.00022894,4.15e-06,-0.00010531,-0.06791437,-0.10653895,0.22752767,-0.00044916,-0.00206838,-3.918e-05,0.00537273,0.00235396,0.00161186,0.00079284,0.01364655,0.01354182,-0.07495259,-0.03841339,-0.06831965,-0.01310306,-0.01926476,-0.0262957,-0.00298611,0.00228375,-1.236e-05,4.405e-05,-2.934e-05,-0.00078544,-0.00116689,0.00031703,0.00166596,-7.426e-05,-8.401e-05,-0.00051805,0.00442995,0.0037757,0.00902454,0.08175081,0.00148816,0.0014333,0.00054734,-0.0081263,9.419e-05,-0.0005309,0.00359407,-0.03636844,-0.02677524,-0.03344212,-0.10072435,-0.08927143,-0.00546298,-0.00041495,-0.00280279,0.00045927,0.00118083,4.411e-05,3.721e-05,0.0001998,0.00013511,0.00015562,0.00025172,-0.00042372,0.00031735,-0.00010691,0.0005562,0.00377528,0.00973468,0.01210429,0.03748143,0.12448124,0.00020158,-0.00093355,-0.00025593,-0.00201694,0.00013506,0.00090194,0.00536822,-0.00422226,0.00674366,-0.06237655,-0.09297915,-0.21411564,-0.00022038,0.00143714,0.00398727,0.00053243,0.00089629,-6.787e-05,-4.83e-05,0.00050501,-0.00012732,0.00017562,0.00071175,-0.0001421,0.00022892,-4.16e-06,-0.00010533,-0.00902331,-0.01210392,-0.02428917,0.06790939,0.10654077,0.22753497,-0.00038039,-4.482e-05,2e-08,0.00125914,0.00177727,-1.3e-07,-0.00851825,-0.00114813,-4e-08,-0.00839499,0.02821446,-2.3e-07,-0.12352292,0.11661093,-1.86e-06,0.01192872,-0.01752148,1.7e-07,-5.144e-05,-8.316e-05,1e-08,6.599e-05,-6.229e-05,-1e-08,-0.0006182,7.792e-05,2e-08,0.00067448,-0.00053471,0.0008102,0.00067446,-0.00053474,-0.00081018,0.12709882,0.00010638,2.236e-05,0.0,-0.00048416,0.00040017,2e-08,-0.00128067,0.00107713,-5e-08,-0.00238366,0.00238878,-5e-08,0.11493097,-0.26528732,2.51e-06,0.0149517,-0.02277016,3.5e-07,1.562e-05,-7.1e-06,0.0,-2.232e-05,-3.92e-05,0.0,-0.0001133,1.522e-05,0.0,0.00075202,0.00018983,2.752e-05,0.00075203,0.0001898,-2.75e-05,-0.12373657,0.28521407,0.0,1e-08,-7.364e-05,-2e-08,6e-08,0.00059346,-1.3e-07,-1.1e-07,0.00103748,-1.6e-07,6e-07,0.00133027,-1.83e-06,2.41e-06,-0.03794671,8e-08,-1.5e-07,0.00544732,0.0,0.0,-6.202e-05,0.0,0.0,8.032e-05,-2e-08,0.0,-4.846e-05,-0.00081569,-0.00014428,0.0001267,0.00081573,0.00014424,0.00012669,2.09e-06,-2.76e-06,0.02533882,-0.00025803,-9.357e-05,0.0,0.00068142,-0.00012812,-2e-08,-0.00025311,-0.00026174,1e-08,0.00145902,-0.00162686,-2e-07,-0.01311968,0.00218911,-3.2e-07,-0.3437295,0.00465586,-5.71e-06,-3.337e-05,5.36e-06,0.0,1.529e-05,5.157e-05,0.0,-3.716e-05,3.515e-05,0.0,-0.00042981,-0.00011033,-3.565e-05,-0.0004299,-0.00011033,3.564e-05,0.00122517,-0.00022035,1.7e-07,0.36372586,7.296e-05,-4e-05,0.0,-0.0001804,0.00028551,0.0,0.00118959,0.00076134,2e-08,-7.176e-05,-0.00475081,7e-08,-0.03132155,0.00236823,-6e-07,0.00921279,-0.05820429,3.1e-07,8.37e-06,5.525e-05,0.0,1.853e-05,8.064e-05,0.0,-8.354e-05,8.95e-06,0.0,-0.00069947,-2.8e-05,1.483e-05,-0.00069945,-2.8e-05,-1.486e-05,9.282e-05,0.00138512,-4e-08,-0.00242482,0.05429418,-2e-08,0.0,-8.205e-05,0.0,-1e-08,0.00016533,0.0,0.0,-2.56e-06,-2.4e-07,1e-08,0.01391137,-1.4e-07,6e-08,0.00251666,-5.76e-06,1.8e-07,-0.03309177,0.0,0.0,-6.259e-05,0.0,0.0,8.407e-05,0.0,0.0,-2.036e-05,-0.00262734,-0.00020282,-0.00032278,0.00262737,0.00020282,-0.00032267,1.7e-07,-3e-08,-0.00817451,6.26e-06,-1.8e-07,0.02358444,0.00037526,0.00010109,-1e-08,-0.00096832,0.00028994,4e-08,-0.00175761,-0.00129198,-8e-08,0.00031868,-0.00022906,1.3e-07,0.01360107,-0.01510692,2.4e-07,-0.1213492,0.12183855,-2.05e-06,4.073e-05,6.213e-05,0.0,-1.101e-05,1.772e-05,0.0,-2.62e-05,-1.802e-05,0.0,9.711e-05,0.00036806,-4.388e-05,9.715e-05,0.00036808,4.388e-05,-0.00144058,-0.00326769,-2.3e-07,-0.0088162,0.02488592,-3e-07,0.11983912,0.00046543,9.59e-06,-1e-08,-0.0008965,0.00085953,3e-08,0.00119809,0.00111624,0.0,0.00270999,-0.0008265,0.0,0.01305218,-0.02187733,3.2e-07,0.11665018,-0.28318156,3.15e-06,6.365e-05,6.736e-05,0.0,-4.04e-06,3.65e-06,0.0,-5.047e-05,-5.379e-05,0.0,-5.111e-05,7.709e-05,-5.939e-05,-5.113e-05,7.709e-05,5.939e-05,-0.0031075,-0.00277872,0.0,-0.00196094,0.00381188,-5e-08,-0.12801783,0.30269548,1e-08,0.0,-1.931e-05,0.0,2e-08,-0.00026966,-3e-08,-3e-08,-0.0006588,1.4e-07,0.0,-0.00762795,1e-07,-1.9e-07,0.00547755,-2.06e-06,3.22e-06,-0.0359179,0.0,0.0,7.089e-05,0.0,0.0,-2.082e-05,0.0,0.0,8.049e-05,0.00122627,0.00047081,0.00013155,-0.00122628,-0.0004708,0.0001315,-2.3e-07,-2e-08,0.01222428,-1.9e-07,4.5e-07,0.00181741,2.26e-06,-3.44e-06,0.02458077,
--------------------------------------------------------------------------------
/tutorial/pyquiver/claisen_ts.qin:
--------------------------------------------------------------------------------
1 | 14
2 | 0,6,-1.466309,0.784167,-0.289984
3 | 1,6,-1.291057,-0.472056,0.261839
4 | 2,8,-0.511026,-1.358456,-0.252221
5 | 3,6,1.28557,-0.900284,0.182034
6 | 4,6,1.374702,0.412704,-0.301796
7 | 5,6,0.668678,1.425295,0.322236
8 | 6,1,-2.145897,1.492397,0.178794
9 | 7,1,-1.2254,0.953689,-1.332994
10 | 8,1,-1.676663,-0.646152,1.281812
11 | 9,1,1.766395,-1.711905,-0.35332
12 | 10,1,1.17354,-1.069495,1.248816
13 | 11,1,1.692496,0.557034,-1.332675
14 | 12,1,0.629026,2.424945,-0.101698
15 | 13,1,0.445204,1.368175,1.383056
16 | 0.25705678,-0.23007491,0.59966183,-0.16192179,-0.11022057,0.60984932,-0.07656178,0.02905976,0.00818469,0.38536877,0.08489313,-0.28912002,0.12578789,-0.1718946,0.6209069,0.00461265,0.14824441,-0.16908357,-0.16472155,-0.098638,0.57828378,0.02129914,0.07754609,-0.00422419,-0.17015337,0.09617573,0.09482815,0.16536912,0.02027087,-0.07166135,-0.01297006,0.13915825,-0.24134984,-0.10996954,-0.16849674,0.34267992,-0.00963912,-0.01499756,0.02378521,0.09050501,-0.0933176,-0.158732,-0.10268931,0.14672479,0.16345721,-0.0221225,-0.04109546,0.00730978,-0.06439803,0.08184619,0.01326556,0.08524428,-0.02282316,-0.00916157,0.14670341,-0.019531,-0.03045864,0.00238464,-0.01838502,0.04823174,0.0141267,0.05750403,-0.04121265,-0.01711401,-0.16842249,0.65496507,0.00229953,0.00232178,-0.00079117,0.00850703,-0.00026777,0.0016706,-0.00808117,-0.00825758,-0.02369709,-0.13633424,-0.0363327,0.62891745,-0.02524692,-0.0156395,0.00336173,-0.02043449,0.01182777,0.0028556,-0.0005058,-0.01429652,-0.00395659,-0.06552562,-0.01453662,0.02796684,0.2762643,0.03739198,0.0418173,-0.00384168,0.02748232,-0.06676882,-0.00686828,-0.09076673,0.01202529,0.00843405,0.05269007,-0.29691773,0.09441583,-0.10216991,0.65309583,0.00036582,0.00468424,0.00112247,0.00128574,-0.00203214,0.0024345,-0.00334434,-0.00538533,0.00082345,0.02372186,0.10483561,-0.14394418,-0.20449141,-0.0348432,0.61884646,0.04689904,0.05212237,-0.00210577,0.01564304,-0.07740371,-0.00538065,-0.06035684,0.02495034,0.01196147,0.04281365,0.04678661,-0.00662745,-0.11538547,0.07797382,0.09314637,0.16151212,-0.00444531,-0.01990304,0.00072068,-0.00603052,0.00892431,-0.00189121,0.01817104,-0.00647995,-0.00114526,0.00114205,-0.05078609,-0.01228219,0.12652134,-0.25703366,-0.10569097,-0.14758652,0.67816421,0.00223839,-0.00281183,-0.01641099,-0.00282684,0.00593585,0.00094566,0.01077087,0.00022989,0.00040712,-0.01405324,-0.00364663,0.01354625,0.08954691,-0.09221556,-0.17696918,-0.16350257,-0.01222856,0.62413085,-0.15100128,0.13007792,0.08663379,-0.00116816,-0.00440801,0.00083449,0.00528337,0.00486085,0.0022378,0.00016892,-0.00115465,0.00083071,-0.00181961,0.00021058,-0.00116851,-0.00202633,0.00298733,0.00269021,0.15355173,0.12099792,-0.16997833,-0.07223236,0.01928998,-0.01059982,-0.02007791,0.00326028,-0.00479126,0.00551072,-0.0010468,-0.00047277,6.105e-05,-0.0003752,0.00016689,-0.00018777,-0.00143305,0.00167075,0.00106179,-0.13471698,0.17852932,0.08629335,-0.07467755,-0.10981185,-0.01086322,0.00584247,0.00652383,0.00164687,0.00488845,0.002458,0.00060053,9.561e-05,0.00025462,-0.00050935,-0.00039245,-0.00049127,-0.00180365,0.00162926,0.00130688,-0.09141951,0.08254975,0.10637724,-0.04747853,0.00628881,0.0696131,0.00474543,-0.0075854,-0.0004927,-0.00789211,0.00082985,-0.00098084,0.00357308,0.00356128,6.688e-05,0.00130598,-0.00541154,-0.00230306,-0.01050806,0.00127975,-0.00754824,-0.00188462,-0.00481932,0.0165796,0.05389317,0.00322667,-0.05563386,0.04214745,-0.00661184,-0.00325946,0.031485,-0.00592968,0.00343364,-0.00387208,0.0024284,0.00219809,0.0002179,0.0022017,-0.00285086,0.00030161,-0.00436972,0.00074915,-0.00303478,0.00275057,0.00523044,-0.01900306,0.00383267,0.05423344,0.07389474,0.0509024,-0.32758926,0.00576226,-0.00424363,-0.00971995,-0.0037907,-0.00052339,0.0017222,0.00143939,0.00177601,0.00015288,0.00133838,-0.00197354,0.00086066,-0.00261522,0.00027987,-0.00216576,0.0007238,0.00301855,-0.00669474,-0.07753628,-0.05160817,0.3415908,0.00658121,0.00119538,-0.00240111,-0.0724735,-0.01301009,0.06421691,-0.0127614,0.01010101,0.02925247,0.0037432,0.00196,0.0014788,0.00159966,-0.00052063,0.00071516,0.00089302,-7.224e-05,0.00080043,-0.00183085,-0.0020625,-0.00100095,0.00518907,0.00318967,0.00144499,0.06965618,-0.00807384,-0.00182509,0.02945502,-0.01145381,-0.06744401,0.046502,0.01739224,0.00397043,-0.0293159,-0.00145912,-0.00020157,-0.00066607,0.00089481,0.00133209,0.00090772,0.00165776,0.00019739,-0.00019257,-0.00070032,9.69e-06,-0.00115537,0.00205992,-0.00389638,0.00299364,-0.00037638,0.06780926,0.00568738,0.00089518,-0.01135277,0.06644581,0.05886039,-0.25094851,0.02066737,-0.0153437,-0.0128089,-0.00227065,-0.00055835,-0.00063618,0.00034204,0.000701,0.00110183,0.00061692,-1.899e-05,-0.0005511,-0.00155369,-4.86e-05,-0.00012129,0.00232401,0.00354912,0.00148128,-0.09345172,-0.04836724,0.27302845,0.00024898,0.00055293,0.00034231,-0.00239271,-0.00183138,-0.0026484,-0.01157912,0.00843832,0.00594383,-0.09327079,0.10445925,0.06989208,0.00743513,-0.00576022,-0.00226478,0.00326622,-0.00018931,0.00250599,0.0001149,3.767e-05,3.949e-05,-0.00018824,-0.00010463,-9.136e-05,-4.408e-05,-5.118e-05,0.00012618,0.09765155,-0.00029655,0.00033813,2.659e-05,-0.00051503,-0.00018627,-0.0001818,-0.00346204,0.00286979,0.00174091,0.09965311,-0.21847246,-0.10760503,0.01505771,-0.01541954,-0.01694176,-0.00055631,-0.00478267,0.00406401,5.29e-06,1.124e-05,1.432e-05,-1.497e-05,-4.472e-05,-5.732e-05,-4.398e-05,-2.305e-05,3.934e-05,-0.10624683,0.22933397,0.0002024,0.00050212,0.00019267,-0.00096546,-0.00090771,-0.00116292,-0.00518075,0.0037062,0.00254186,0.07360785,-0.10897079,-0.13200429,-0.00590767,0.00923864,0.00854369,0.00085357,0.00207575,0.00016092,7.524e-05,6.216e-05,1.34e-05,-0.00013808,-5.85e-05,-5.272e-05,-0.0001546,7.743e-05,0.00024672,-0.07423093,0.1161847,0.13039443,0.00122577,0.00393338,0.00014749,0.00469087,-0.00779704,-0.00500254,-0.02108556,0.00106486,-0.0125555,-0.03360648,0.01079156,0.03630763,0.0036914,-0.00900075,0.0052679,-0.00675157,0.00138412,-0.00066029,0.00015616,0.00020052,-1.98e-05,-0.00046775,-0.00030463,-0.00013718,-0.00022987,0.00029563,0.00081717,0.00210573,-0.0031461,0.01360827,0.04405622,0.0003979,0.00100634,-4.251e-05,0.0019506,-0.00120303,0.00034037,-0.00364326,0.00059686,-0.0024793,0.00250877,-0.0621684,0.04013015,-0.00381517,-0.00238015,0.03078061,-0.00288453,0.00181223,0.00074079,2.916e-05,1.199e-05,-1.94e-05,-0.00014872,-4.672e-05,-8.856e-05,-0.00025765,6.038e-05,4.9e-06,0.00166778,0.00617777,-0.02215912,0.00239574,0.06083531,0.00042782,0.00080501,0.0001261,0.00048668,-0.00097771,5.844e-05,-0.00389694,-0.00091574,-3.898e-05,0.03968183,0.04518383,-0.33477178,0.00262309,-0.00120788,-0.00737869,-0.00093371,-0.00097516,0.00102058,6.917e-05,2.13e-06,1.597e-05,-4.39e-05,-9.627e-05,-3.424e-05,-0.00059414,1.136e-05,3.898e-05,0.00140196,0.00382982,-0.01030897,-0.03957618,-0.04807654,0.34943577,0.00077369,0.00056209,0.00063772,0.00049402,-2.193e-05,0.00059764,0.00031646,0.00034394,2.122e-05,0.00205467,-5.404e-05,-0.00033556,-0.06064652,-0.00592155,0.07727645,-0.0028649,-0.00165904,0.01588696,5.758e-05,3.53e-06,-0.00016277,-0.00022206,-7.998e-05,-0.00037547,-0.00022155,-0.00012861,-0.00019944,-0.00299664,-0.00104951,-0.00130054,0.00521456,0.00160847,0.00081944,0.05701135,2.658e-05,0.0004126,0.00020567,0.00039168,-0.00039736,0.00012799,0.00026596,0.00066194,-0.00044387,-0.00772792,-0.0029473,0.03198312,-0.0060762,-0.061172,0.03457968,0.00662207,0.00493471,-0.02586671,-0.00013703,-5.218e-05,-4.007e-05,0.00012187,0.00010212,4.8e-07,-3.889e-05,-8.633e-05,-4.114e-05,-0.00049499,0.00104521,-0.00064368,-0.00015311,-0.00465974,0.00162053,0.00415275,0.06389217,0.00013262,0.00033695,-0.00014934,-0.00010869,-0.00035252,0.00024243,-0.00026779,0.00011591,0.00052788,0.0026576,0.00043021,-0.00926896,0.08601391,0.03684867,-0.30325001,0.00264641,0.00273113,-0.01220891,-3.971e-05,-3.596e-05,8.451e-05,0.00022483,-3.397e-05,0.00013384,-7.354e-05,-8.235e-05,-9.34e-06,-0.00141499,-0.00027911,0.00054955,0.00196597,0.00215725,0.00129165,-0.09360758,-0.04002967,0.32038098,-0.00461662,-0.00771261,0.00188714,-0.00012599,0.00293809,-0.00050322,0.00226644,-0.00166537,-0.00052402,0.00129945,0.00282751,0.00486925,0.00419668,0.01877601,-0.00857295,-0.03413825,0.00801225,0.00467264,0.00012821,0.00028546,0.00031149,0.00080131,5.636e-05,4.136e-05,-0.00022648,0.00017357,-2.344e-05,-0.00034535,0.00064806,-0.00038405,-9.386e-05,-0.00022219,0.00033123,-0.00364267,-0.00172427,-0.00176005,0.03292826,-0.00152566,-0.00167141,0.00092125,4.817e-05,0.00032445,-0.00089986,0.00046,0.00026597,0.00021491,0.00412663,-0.00188576,-0.00239814,-0.00221187,-0.01391488,0.011324,0.01758414,-0.29819113,0.10661694,-0.00016784,9.595e-05,7.068e-05,0.00019134,-4.9e-05,-0.00030713,-0.00013169,6.375e-05,-2.185e-05,-0.00054295,-0.0010298,0.00051602,-2.582e-05,0.00015588,0.00012884,-0.00122929,0.00066317,-0.00057371,-0.01712954,0.31329577,-0.00275864,-0.00441683,0.00094647,-0.00088614,0.00215674,-0.00085342,0.00211019,-0.00108179,-0.00035678,0.00188836,-0.00217874,-0.00018263,-0.00086018,-0.01321627,0.00865825,0.0056429,0.10740635,-0.10191718,0.00038441,0.00023753,5.293e-05,0.00027348,-8.114e-05,0.00018121,-7.8e-06,6.662e-05,0.0002831,1.868e-05,-0.00084807,0.00049493,0.00011293,-0.00043825,0.00040666,-0.0019111,-0.00014186,0.00045137,-0.00309753,-0.11464968,0.09936042,-0.00705696,-0.00681625,-0.00746487,-0.00323413,0.00627125,-0.00246194,0.00455539,-0.00273648,-0.00041486,-0.00667724,-0.00580641,-0.0008403,-0.00492871,0.00502655,0.02036576,-0.03899569,0.00048506,0.05947876,0.00026998,0.00037849,0.00030793,-0.00086668,-0.00028556,-9.871e-05,0.00012538,-0.00023067,0.00047208,-5.57e-06,-3.284e-05,-8.524e-05,0.00109438,0.00041311,-0.00079636,0.00467201,0.00477149,0.00363103,0.00156888,0.00055439,-0.00090958,0.04947896,-0.00325778,-0.00298447,-0.00234201,-0.00248992,0.00194122,-0.00229988,0.00152308,-0.0010088,6.02e-05,-0.00182025,0.00012845,-0.00132036,0.00261767,0.00802024,-0.0223323,0.00653674,-0.05927622,0.02134737,0.00036313,0.00016809,0.00019735,-0.00018554,-0.00016587,-0.00016922,6.798e-05,3.345e-05,0.00034995,6.554e-05,0.00018241,0.00037678,0.00036162,-0.00019871,0.00066777,0.00347317,-0.00239701,-0.00123281,-0.00526332,0.00187704,0.02718539,-0.00199212,0.05368018,-0.00183515,-0.00156775,-0.0008333,-0.00080532,0.00215372,0.00034113,0.00145176,-0.00121813,-8.919e-05,-0.00235304,-3.136e-05,0.00075449,0.00167669,0.00492065,-0.01035798,0.06810138,0.01938931,-0.33129514,-0.00029821,7.893e-05,3.176e-05,-3.879e-05,8.689e-05,0.00013381,-0.00022489,-0.00023429,0.00024772,0.00037993,1.34e-05,0.00039073,-0.00027587,-0.0008504,0.00013852,0.00265303,-0.00131047,0.00122436,0.00275216,-0.00094227,-0.00752532,-0.07118368,-0.02048823,0.34683841,
--------------------------------------------------------------------------------