diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 000000000..2c90fcf48 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: fa8206334e5f75e03fda66a659984138 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index c989692d6..000000000 --- a/.codecov.yml +++ /dev/null @@ -1,18 +0,0 @@ -coverage: - ignore: - - qcengine/programs/util/.* - - qcengine/programs/cfour/.* - - qcengine/programs/gamess/.* - - setup.py - status: - patch: false - project: - default: - threshold: 80% -comment: - layout: "header" - require_changes: false - branches: null - behavior: once - flags: null - paths: null diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 65beb3ad2..000000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -qcengine/_version.py export-subst diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 3553258e8..000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,42 +0,0 @@ -# How to contribute - -We welcome contributions from external contributors, and this document -describes how to merge code changes into this project_name. - -## Getting Started - -* Make sure you have a [GitHub account](https://github.com/signup/free). -* [Fork](https://help.github.com/articles/fork-a-repo/) this repository on GitHub. -* On your local machine, - [clone](https://help.github.com/articles/cloning-a-repository/) your fork of - the repository. - -## Making Changes - -* Add some really awesome code to your local fork. It's usually a [good - idea](http://blog.jasonmeridth.com/posts/do-not-issue-pull-requests-from-your-master-branch/) - to make changes on a - [branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/) - with the branch name relating to the feature you are going to add. -* When you are ready for others to examine and comment on your new feature, - navigate to your fork of project_name on GitHub and open a [pull - request](https://help.github.com/articles/using-pull-requests/) (PR). Note that - after you launch a PR from one of your fork's branches, all - subsequent commits to that branch will be added to the open pull request - automatically. Each commit added to the PR will be validated for - mergability, compilation and test suite compliance; the results of these tests - will be visible on the PR page. -* If you're providing a new feature, you must add test cases and documentation. -* When the code is ready to go, make sure you run the test suite using pytest. -* When you're ready to be considered for merging, check the "Ready to go" - box on the PR page to let the project_name devs know that the changes are complete. - The code will not be merged until this box is checked, the continuous - integration returns checkmarks, - and multiple core developers give "Approved" reviews. - -# Additional Resources - -* [General GitHub documentation](https://help.github.com/) -* [PR best practices](http://codeinthehole.com/writing/pull-requests-and-other-good-practices-for-teams-using-github/) -* [A guide to contributing to software packages](http://www.contribution-guide.org) -* [Thinkful PR example](http://www.thinkful.com/learn/github-pull-request-tutorial/#Time-to-Submit-Your-First-PR) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 66d50f0de..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -label: Bug ---- - -**Describe the bug** - - -**To Reproduce** - - -**Expected behavior** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 9d2944f30..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -label: Feature ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the solution you'd like** - - -**Describe alternatives you've considered** - - -**Additional context** - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 84f94c5ac..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,12 +0,0 @@ - - -## Description - - -## Changelog description - - -## Status - -- [ ] Code base linted -- [ ] Ready to go diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 05b9c7e13..000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,236 +0,0 @@ -name: CI - -on: - push: - branches: - - master - pull_request: - branches: - - master - schedule: - - cron: "9 16 * * 1" - -jobs: - build: - defaults: - run: - shell: bash -l {0} - strategy: - fail-fast: false - matrix: - cfg: - - conda-env: psi - python-version: 3.7 - label: Psi4-1.5 - runs-on: ubuntu-latest - pytest: "" - - - conda-env: psi-nightly - python-version: "3.10" - label: Psi4-1.6 - runs-on: ubuntu-latest - pytest: "" - - - conda-env: psi-cf - python-version: "3.12" - label: Psi4-1.8 - runs-on: windows-latest - pytest: "-k 'not (hes2 or qchem)'" - - - conda-env: torchani - python-version: 3.8 - label: ANI - runs-on: ubuntu-latest - pytest: "" - - - conda-env: openmm - python-version: 3.8 - label: OpenMM - runs-on: ubuntu-latest - pytest: "" - - - conda-env: xtb - python-version: "3.10" - label: xTB - runs-on: ubuntu-latest - pytest: "" - - - conda-env: qcore - python-version: 3.7 - label: QCore - runs-on: ubuntu-latest - pytest: "" - - - conda-env: nwchem - python-version: 3.8 - label: NWChem70 - runs-on: ubuntu-20.04 - pytest: "" - # formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021 - - - conda-env: nwchem-cf - python-version: 3.12 - label: NWChem - runs-on: ubuntu-latest - pytest: "" - - - conda-env: mrchem - python-version: 3.8 - label: MRChem - runs-on: ubuntu-latest - pytest: "" - - - conda-env: adcc - python-version: 3.8 - label: ADCC - runs-on: ubuntu-latest - pytest: "" - - - conda-env: opt-disp - python-version: 3.8 - label: optimization-dispersion - runs-on: ubuntu-latest - pytest: "" - - - conda-env: opt-disp-cf - python-version: 3.11 - label: optimization-dispersion - runs-on: windows-latest - pytest: "-k 'not (hes2 or qchem)'" - - - conda-env: mace - python-version: "3.10" - label: MACE - runs-on: ubuntu-latest - pytest: "" - - - conda-env: aimnet2 - python-version: 3.11 - label: AIMNET2 - runs-on: ubuntu-latest - pytest: "" - - name: "🐍 ${{ matrix.cfg.python-version }} • ${{ matrix.cfg.label }} • ${{ matrix.cfg.runs-on }}" - runs-on: ${{ matrix.cfg.runs-on }} - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Create Environment - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: test - environment-file: devtools/conda-envs/${{ matrix.cfg.conda-env }}.yaml - python-version: ${{ matrix.cfg.python-version }} - auto-activate-base: false - show-channel-urls: true - miniforge-variant: Mambaforge - use-mamba: true - add-pip-as-python-dependency: true - # note: conda-forge chnl req'd for Mambaforge, but we'll spec in file, not here `channels: conda-forge,...` - # note: any activate/deactivate use the conda cmd. other cmds use mamba cmd. - - - name: Special Config - NWChem - if: "(matrix.cfg.label == 'NWChem70')" - run: | - sudo apt-get -y install nwchem - - - name: Special Config - QCore - if: "(matrix.cfg.label == 'QCore')" - run: | - qcore --accept-license - - - name: Special Config - QCElemental Dep - if: false - run: | - conda remove qcelemental --force - python -m pip install 'git+https://github.com/MolSSI/QCElemental.git@loriab-patch-2' --no-deps - - # note: conda remove --force, not mamba remove --force b/c https://github.com/mamba-org/mamba/issues/412 - # alt. is micromamba but not yet ready for setup-miniconda https://github.com/conda-incubator/setup-miniconda/issues/75 - - name: Special Config - QCEngine Dep - if: "(startsWith(matrix.cfg.label, 'Psi4')) || (matrix.cfg.label == 'ADCC') || (matrix.cfg.label == 'optimization-dispersion')" - run: | - conda remove qcengine --force - - # QCEngine CI and Psi4 are circularly dependent, so a hack is in order - - name: Special Config - Faux Pydantic Upgrade - if: "((matrix.cfg.label == 'Psi4-1.6') || (matrix.cfg.label == 'optimization-dispersion')) && (runner.os != 'Windows')" - run: | - sed -i s/from\ pydantic\ /from\ pydantic.v1\ /g ${CONDA_PREFIX}/lib/python${{ matrix.cfg.python-version }}/site-packages/psi4/driver/*py - - - name: Environment Information - run: | - mamba info - mamba list - - - name: Install QCEngine - run: | - python -m pip install . --no-deps - - - name: QCEngineRecords - if: "(matrix.cfg.label != 'Psi4-1.6')" - run: | - qcengine info - export QCER_VER=`python -c "import qcengine.testing; print(qcengine.testing.QCENGINE_RECORDS_COMMIT)"` - pip install git+https://github.com/MolSSI/QCEngineRecords.git@${QCER_VER}#egg=qcenginerecords - python -c "import qcengine; print(qcengine.config.global_repr())" - - - name: PyTest - run: | - pytest -rws -v ${{ matrix.cfg.pytest }} --cov=qcengine --color=yes --cov-report=xml qcengine/ - - - name: CodeCov - uses: codecov/codecov-action@v3 - - release_sphinx: - needs: [build] - defaults: - run: - shell: bash -l {0} - strategy: - fail-fast: false - matrix: - cfg: - - conda-env: docs-cf - python-version: 3.8 - label: Sphinx - runs-on: ubuntu-latest - - name: "🐍 ${{ matrix.cfg.python-version }} • ${{ matrix.cfg.label }}" - runs-on: ${{ matrix.cfg.runs-on }} - - steps: - - uses: actions/checkout@v3 - - - name: Create Environment - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: test - environment-file: devtools/conda-envs/${{ matrix.cfg.conda-env }}.yaml - python-version: ${{ matrix.cfg.python-version }} - auto-activate-base: false - miniforge-variant: Mambaforge - use-mamba: true - add-pip-as-python-dependency: true - channels: conda-forge - - - name: Environment Information - run: | - mamba info - mamba list --show-channel-urls - - - name: Build Documentation - run: | - python -m pip install . --no-deps - cd docs - make html - - - name: GitHub Pages Deploy - uses: JamesIves/github-pages-deploy-action@4.1.1 - if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' ) - with: - branch: gh-pages - folder: docs/build/html diff --git a/.github/workflows/Lint.yml b/.github/workflows/Lint.yml deleted file mode 100644 index a6579f3cd..000000000 --- a/.github/workflows/Lint.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Lint - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10"] - - steps: - - uses: actions/checkout@v4 - - - name: Python Setup - uses: actions/setup-python@v4 - with: - python-version: "${{ matrix.python-version }}" - - - name: Create Environment - shell: bash - run: | - python -m pip install --upgrade pip - python -m pip install -e '.[lint]' - - - name: Lint - shell: bash - run: black qcengine --check - diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 968264715..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ "master", "gh-pages" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: "8 16 * * 2" - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ python ] - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - queries: +security-and-quality - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{ matrix.language }}" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c6814bcbb..000000000 --- a/.gitignore +++ /dev/null @@ -1,79 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Vim scratch files -.swp - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot -*.swp - -# Django stuff: -*.log - -# PyCharm junk -.idea - -# Sphinx documentation -docs/_build/ -docs/source/api/ - -# PyBuilder -target/ - -# Random files in base -timer.dat -tmp.py - -# geometric junk -energy.txt -*.ipynb_checkpoints - -# Psi4 quantities -timer.dat -*.out -iter.dat -psi*clean - -# vscode folders -.vscode diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a575187b3..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -See [changelog in docs](docs/source/changelog.rst). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 28b705da1..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,77 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, -body size, disability, ethnicity, gender identity and expression, level of -experience, nationality, personal appearance, race, religion, or sexual -identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -Moreover, project maintainers will strive to offer feedback and advice to -ensure quality and consistency of contributions to the code. Contributions -from outside the group of project maintainers are strongly welcomed but the -final decision as to whether commits are merged into the codebase rests with -the team of project maintainers. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an -appointed representative at an online or offline event. Representation of a -project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at molssi@vt.edu. The project team will -review and investigate all complaints, and will respond in a way that it deems -appropriate to the circumstances. The project team is obligated to maintain -confidentiality with regard to the reporter of an incident. Further details of -specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 1.4, available at -[http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 82dfc8b47..000000000 --- a/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2017-2021, The Molecular Sciences Software Institute -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index ffc72f766..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -recursive-include qcengine *.py - -include setup.py -include README.md -include LICENSE -include MANIFEST.in - -include versioneer.py -include qcengine/_version.py diff --git a/Makefile b/Makefile deleted file mode 100644 index ea25ff1c8..000000000 --- a/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -.DEFAULT_GOAL := all -isort = isort --float-to-top qcengine -black = black qcengine -autoflake = autoflake -ir --remove-all-unused-imports --ignore-init-module-imports --remove-unused-variables --exclude="**/tests/**" qcengine - -.PHONY: install -install: - pip install -e . - -.PHONY: format -format: -# $(autoflake) -# $(isort) - $(black) - -.PHONY: lint -lint: - $(isort) --check-only - $(black) --check - -.PHONY: check-dist -check-dist: - python setup.py check -ms - python setup.py sdist - twine check dist/* - -.PHONY: mypy -mypy: - mypy qcengine - -.PHONY: test -test: - pytest -v --cov=qcengine/ - -.PHONY: clean -clean: - rm -rf `find . -name __pycache__` - rm -f `find . -type f -name '*.py[co]' ` - rm -f `find . -type f -name '*~' ` - rm -f `find . -type f -name '.*~' ` - rm -rf .cache - rm -rf .pytest_cache - rm -rf .mypy_cache - rm -rf htmlcov - rm -rf *.egg-info - rm -f .coverage - rm -f .coverage.* - rm -rf build - rm -rf dist - rm -f qcengine/*.c qcengine/*.so - python setup.py clean diff --git a/README.md b/README.md deleted file mode 100644 index 3aba69cb0..000000000 --- a/README.md +++ /dev/null @@ -1,59 +0,0 @@ -QCEngine -======== -[![Build Status](https://github.com/MolSSI/QCEngine/workflows/CI/badge.svg?branch=master)](https://github.com/MolSSI/QCEngine/actions?query=workflow%3ACI) -[![codecov](https://img.shields.io/codecov/c/github/MolSSI/QCEngine.svg?logo=Codecov&logoColor=white)](https://codecov.io/gh/MolSSI/QCEngine) -[![Documentation Status](https://img.shields.io/github/actions/workflow/status/MolSSI/QCEngine/CI.yml?label=docs&logo=readthedocs&logoColor=white)](https://molssi.github.io/QCEngine/) -[![Conda (channel only)](https://img.shields.io/conda/vn/conda-forge/qcengine?color=blue&logo=anaconda&logoColor=white)](https://anaconda.org/conda-forge/qcengine) -[![Chat on Slack](https://img.shields.io/badge/chat-on_slack-808493.svg?longCache=true&style=flat&logo=slack)](https://join.slack.com/t/qcarchive/shared_invite/enQtNDIzNTQ2OTExODk0LTE3MWI0YzBjNzVhNzczNDM0ZTA5MmQ1ODcxYTc0YTA1ZDQ2MTk1NDhlMjhjMmQ0YWYwOGMzYzJkZTM2NDlmOGM) -![python](https://img.shields.io/badge/python-3.7+-blue.svg) - - -Quantum chemistry program executor and IO standardizer ([QCSchema](https://github.com/MolSSI/QCSchema)) for quantum chemistry. - -# Example - -A simple example of QCEngine's capabilities is as follows: - -```python ->>> import qcengine as qcng ->>> import qcelemental as qcel - ->>> mol = qcel.models.Molecule.from_data(""" -O 0.0 0.000 -0.129 -H 0.0 -1.494 1.027 -H 0.0 1.494 1.027 -""") - ->>> inp = qcel.models.AtomicInput( - molecule=mol, - driver="energy", - model={"method": "SCF", "basis": "sto-3g"}, - keywords={"scf_type": "df"} - ) -``` - -These input specifications can be executed with the ``compute`` function along with a program specifier: - -```python ->>> ret = qcng.compute(inp, "psi4") -``` - -The results contain a complete record of the computation: - - -```python ->>> ret.return_result --74.45994963230625 - ->>> ret.properties.scf_dipole_moment -[0.0, 0.0, 0.6635967188869244] - ->>> ret.provenance.cpu -Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz -``` - -See the [documentation](https://molssi.github.io/QCEngine/) for more information. - -# License - -BSD-3C. See the [License File](LICENSE) for more information. diff --git a/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png b/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png new file mode 100644 index 000000000..eca980223 Binary files /dev/null and b/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png differ diff --git a/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png.map b/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png.map new file mode 100644 index 000000000..6c949f2b3 --- /dev/null +++ b/_images/inheritance-73f22ae93c9fefc236fdafee2d96c1966f59ad9b.png.map @@ -0,0 +1,5 @@ + + + + + diff --git a/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png b/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png new file mode 100644 index 000000000..2dd8967c1 Binary files /dev/null and b/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png differ diff --git a/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png.map b/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png.map new file mode 100644 index 000000000..26df58dda --- /dev/null +++ b/_images/inheritance-c72d11f91e00b97018aea4f61e7125b725684508.png.map @@ -0,0 +1,4 @@ + + + + diff --git a/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png b/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png new file mode 100644 index 000000000..5adf7f06b Binary files /dev/null and b/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png differ diff --git a/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png.map b/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png.map new file mode 100644 index 000000000..29c9be796 --- /dev/null +++ b/_images/inheritance-f0f36dbf79b2a1e42b9c6cc34fa47d5a4e33766c.png.map @@ -0,0 +1,3 @@ + + + diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 000000000..cc98f555d --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,149 @@ + + + + + + Overview: module code — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcelemental/models/results.html b/_modules/qcelemental/models/results.html new file mode 100644 index 000000000..5b337c80f --- /dev/null +++ b/_modules/qcelemental/models/results.html @@ -0,0 +1,968 @@ + + + + + + qcelemental.models.results — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcelemental.models.results

+from enum import Enum
+from functools import partial
+from typing import TYPE_CHECKING, Any, Dict, Optional, Set, Union
+
+import numpy as np
+
+try:
+    from pydantic.v1 import Field, constr, validator
+except ImportError:  # Will also trap ModuleNotFoundError
+    from pydantic import Field, constr, validator
+
+from ..util import provenance_stamp
+from .basemodels import ProtoModel, qcschema_draft
+from .basis import BasisSet
+from .common_models import ComputeError, DriverEnum, Model, Provenance, qcschema_input_default, qcschema_output_default
+from .molecule import Molecule
+from .types import Array
+
+if TYPE_CHECKING:
+    try:
+        from pydantic.v1.typing import ReprArgs
+    except ImportError:  # Will also trap ModuleNotFoundError
+        from pydantic.typing import ReprArgs
+
+
+class AtomicResultProperties(ProtoModel):
+    r"""
+    Named properties of quantum chemistry computations following the MolSSI QCSchema.
+
+    All arrays are stored flat but must be reshapable into the dimensions in attribute ``shape``, with abbreviations as follows:
+
+    * nao: number of atomic orbitals = :attr:`~qcelemental.models.AtomicResultProperties.calcinfo_nbasis`
+    * nmo: number of molecular orbitals = :attr:`~qcelemental.models.AtomicResultProperties.calcinfo_nmo`
+    """
+
+    # Calcinfo
+    calcinfo_nbasis: Optional[int] = Field(None, description="The number of basis functions for the computation.")
+    calcinfo_nmo: Optional[int] = Field(None, description="The number of molecular orbitals for the computation.")
+    calcinfo_nalpha: Optional[int] = Field(None, description="The number of alpha electrons in the computation.")
+    calcinfo_nbeta: Optional[int] = Field(None, description="The number of beta electrons in the computation.")
+    calcinfo_natom: Optional[int] = Field(None, description="The number of atoms in the computation.")
+
+    # Canonical
+    nuclear_repulsion_energy: Optional[float] = Field(None, description="The nuclear repulsion energy.")
+    return_energy: Optional[float] = Field(
+        None,
+        description=f"The energy of the requested method, identical to :attr:`~qcelemental.models.AtomicResult.return_result` for :attr:`~qcelemental.models.AtomicInput.driver`\\ =\\ :attr:`~qcelemental.models.DriverEnum.energy` computations.",
+    )
+    return_gradient: Optional[Array[float]] = Field(
+        None,
+        description=f"The gradient of the requested method, identical to :attr:`~qcelemental.models.AtomicResult.return_result` for :attr:`~qcelemental.models.AtomicInput.driver`\\ =\\ :attr:`~qcelemental.models.DriverEnum.gradient` computations.",
+        units="E_h/a0",
+    )
+    return_hessian: Optional[Array[float]] = Field(
+        None,
+        description=f"The Hessian of the requested method, identical to :attr:`~qcelemental.models.AtomicResult.return_result` for :attr:`~qcelemental.models.AtomicInput.driver`\\ =\\ :attr:`~qcelemental.models.DriverEnum.hessian` computations.",
+        units="E_h/a0^2",
+    )
+
+    # SCF Keywords
+    scf_one_electron_energy: Optional[float] = Field(
+        None,
+        description="The one-electron (core Hamiltonian) energy contribution to the total SCF energy.",
+        units="E_h",
+    )
+    scf_two_electron_energy: Optional[float] = Field(
+        None,
+        description="The two-electron energy contribution to the total SCF energy.",
+        units="E_h",
+    )
+    scf_vv10_energy: Optional[float] = Field(
+        None,
+        description="The VV10 functional energy contribution to the total SCF energy.",
+        units="E_h",
+    )
+    scf_xc_energy: Optional[float] = Field(
+        None,
+        description="The functional (XC) energy contribution to the total SCF energy.",
+        units="E_h",
+    )
+    scf_dispersion_correction_energy: Optional[float] = Field(
+        None,
+        description="The dispersion correction appended to an underlying functional when a DFT-D method is requested.",
+        units="E_h",
+    )
+    scf_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The SCF X, Y, and Z dipole components",
+        units="e a0",
+    )
+    scf_quadrupole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The quadrupole components (redundant; 6 unique).",
+        shape=[3, 3],
+        units="e a0^2",
+    )
+    scf_total_energy: Optional[float] = Field(
+        None,
+        description="The total electronic energy of the SCF stage of the calculation.",
+        units="E_h",
+    )
+    scf_total_gradient: Optional[Array[float]] = Field(
+        None,
+        description="The total electronic gradient of the SCF stage of the calculation.",
+        units="E_h/a0",
+    )
+    scf_total_hessian: Optional[Array[float]] = Field(
+        None,
+        description="The total electronic Hessian of the SCF stage of the calculation.",
+        units="E_h/a0^2",
+    )
+    scf_iterations: Optional[int] = Field(None, description="The number of SCF iterations taken before convergence.")
+
+    # MP2 Keywords
+    mp2_same_spin_correlation_energy: Optional[float] = Field(
+        None,
+        description="The portion of MP2 doubles correlation energy from same-spin (i.e. triplet) correlations, without any user scaling.",
+        units="E_h",
+    )
+    mp2_opposite_spin_correlation_energy: Optional[float] = Field(
+        None,
+        description="The portion of MP2 doubles correlation energy from opposite-spin (i.e. singlet) correlations, without any user scaling.",
+        units="E_h",
+    )
+    mp2_singles_energy: Optional[float] = Field(
+        None,
+        description="The singles portion of the MP2 correlation energy. Zero except in ROHF.",
+        units="E_h",
+    )
+    mp2_doubles_energy: Optional[float] = Field(
+        None,
+        description="The doubles portion of the MP2 correlation energy including same-spin and opposite-spin correlations.",
+        units="E_h",
+    )
+    mp2_correlation_energy: Optional[float] = Field(
+        None,
+        description="The MP2 correlation energy.",
+        units="E_h",
+    )
+    mp2_total_energy: Optional[float] = Field(
+        None,
+        description="The total MP2 energy (MP2 correlation energy + HF energy).",
+        units="E_h",
+    )
+    mp2_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The MP2 X, Y, and Z dipole components.",
+        shape=[3],
+        units="e a0",
+    )
+
+    # CCSD Keywords
+    ccsd_same_spin_correlation_energy: Optional[float] = Field(
+        None,
+        description="The portion of CCSD doubles correlation energy from same-spin (i.e. triplet) correlations, without any user scaling.",
+        units="E_h",
+    )
+    ccsd_opposite_spin_correlation_energy: Optional[float] = Field(
+        None,
+        description="The portion of CCSD doubles correlation energy from opposite-spin (i.e. singlet) correlations, without any user scaling.",
+        units="E_h",
+    )
+    ccsd_singles_energy: Optional[float] = Field(
+        None,
+        description="The singles portion of the CCSD correlation energy. Zero except in ROHF.",
+        units="E_h",
+    )
+    ccsd_doubles_energy: Optional[float] = Field(
+        None,
+        description="The doubles portion of the CCSD correlation energy including same-spin and opposite-spin correlations.",
+        units="E_h",
+    )
+    ccsd_correlation_energy: Optional[float] = Field(
+        None,
+        description="The CCSD correlation energy.",
+        units="E_h",
+    )
+    ccsd_total_energy: Optional[float] = Field(
+        None,
+        description="The total CCSD energy (CCSD correlation energy + HF energy).",
+        units="E_h",
+    )
+    ccsd_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The CCSD X, Y, and Z dipole components.",
+        shape=[3],
+        units="e a0",
+    )
+    ccsd_iterations: Optional[int] = Field(None, description="The number of CCSD iterations taken before convergence.")
+
+    # CCSD(T) keywords
+    ccsd_prt_pr_correlation_energy: Optional[float] = Field(
+        None,
+        description="The CCSD(T) correlation energy.",
+        units="E_h",
+    )
+    ccsd_prt_pr_total_energy: Optional[float] = Field(
+        None,
+        description="The total CCSD(T) energy (CCSD(T) correlation energy + HF energy).",
+        units="E_h",
+    )
+    ccsd_prt_pr_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The CCSD(T) X, Y, and Z dipole components.",
+        shape=[3],
+        units="e a0",
+    )
+
+    # CCSDT keywords
+    ccsdt_correlation_energy: Optional[float] = Field(
+        None,
+        description="The CCSDT correlation energy.",
+        units="E_h",
+    )
+    ccsdt_total_energy: Optional[float] = Field(
+        None,
+        description="The total CCSDT energy (CCSDT correlation energy + HF energy).",
+        units="E_h",
+    )
+    ccsdt_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The CCSDT X, Y, and Z dipole components.",
+        shape=[3],
+        units="e a0",
+    )
+    ccsdt_iterations: Optional[int] = Field(
+        None, description="The number of CCSDT iterations taken before convergence."
+    )
+
+    # CCSDTQ keywords
+    ccsdtq_correlation_energy: Optional[float] = Field(
+        None,
+        description="The CCSDTQ correlation energy.",
+        units="E_h",
+    )
+    ccsdtq_total_energy: Optional[float] = Field(
+        None,
+        description="The total CCSDTQ energy (CCSDTQ correlation energy + HF energy).",
+        units="E_h",
+    )
+    ccsdtq_dipole_moment: Optional[Array[float]] = Field(
+        None,
+        description="The CCSDTQ X, Y, and Z dipole components.",
+        shape=[3],
+        units="e a0",
+    )
+    ccsdtq_iterations: Optional[int] = Field(
+        None, description="The number of CCSDTQ iterations taken before convergence."
+    )
+
+    class Config(ProtoModel.Config):
+        force_skip_defaults = True
+
+    def __repr_args__(self) -> "ReprArgs":
+        return [(k, v) for k, v in self.dict().items()]
+
+    @validator(
+        "scf_dipole_moment",
+        "mp2_dipole_moment",
+        "ccsd_dipole_moment",
+        "ccsd_prt_pr_dipole_moment",
+        "scf_quadrupole_moment",
+    )
+    def _validate_poles(cls, v, values, field):
+        if v is None:
+            return v
+
+        if field.name.endswith("_dipole_moment"):
+            order = 1
+        elif field.name.endswith("_quadrupole_moment"):
+            order = 2
+
+        shape = tuple([3] * order)
+        return np.asarray(v).reshape(shape)
+
+    @validator(
+        "return_gradient",
+        "return_hessian",
+        "scf_total_gradient",
+        "scf_total_hessian",
+    )
+    def _validate_derivs(cls, v, values, field):
+        if v is None:
+            return v
+
+        nat = values.get("calcinfo_natom", None)
+        if nat is None:
+            raise ValueError(f"Please also set ``calcinfo_natom``!")
+
+        if field.name.endswith("_gradient"):
+            shape = (nat, 3)
+        elif field.name.endswith("_hessian"):
+            shape = (3 * nat, 3 * nat)
+
+        try:
+            v = np.asarray(v).reshape(shape)
+        except (ValueError, AttributeError):
+            raise ValueError(f"Derivative must be castable to shape {shape}!")
+        return v
+
+    def dict(self, *args, **kwargs):
+        # pure-json dict repr for QCFractal compliance, see https://github.com/MolSSI/QCFractal/issues/579
+        # Sep 2021: commenting below for now to allow recomposing AtomicResult.properties for qcdb.
+        #   This will break QCFractal tests for now, but future qcf will be ok with it.
+        # kwargs["encoding"] = "json"
+        return super().dict(*args, **kwargs)
+
+
+class WavefunctionProperties(ProtoModel):
+    r"""Wavefunction properties resulting from a computation. Matrix quantities are stored in column-major order. Presence and contents configurable by protocol."""
+
+    # Class properties
+    _return_results_names: Set[str] = {
+        "orbitals_a",
+        "orbitals_b",
+        "density_a",
+        "density_b",
+        "fock_a",
+        "fock_b",
+        "eigenvalues_a",
+        "eigenvalues_b",
+        "occupations_a",
+        "occupations_b",
+    }
+
+    # The full basis set description of the quantities
+    basis: BasisSet = Field(..., description=str(BasisSet.__doc__))
+    restricted: bool = Field(
+        ...,
+        description=str(
+            "If the computation was restricted or not (alpha == beta). If True, all beta quantities are skipped."
+        ),
+    )
+
+    # Core Hamiltonian
+    h_core_a: Optional[Array[float]] = Field(
+        None, description="Alpha-spin core (one-electron) Hamiltonian in the AO basis.", shape=["nao", "nao"]
+    )
+    h_core_b: Optional[Array[float]] = Field(
+        None, description="Beta-spin core (one-electron) Hamiltonian in the AO basis.", shape=["nao", "nao"]
+    )
+    h_effective_a: Optional[Array[float]] = Field(
+        None, description="Alpha-spin effective core (one-electron) Hamiltonian in the AO basis.", shape=["nao", "nao"]
+    )
+    h_effective_b: Optional[Array[float]] = Field(
+        None, description="Beta-spin effective core (one-electron) Hamiltonian in the AO basis", shape=["nao", "nao"]
+    )
+
+    # SCF Results
+    scf_orbitals_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin orbitals in the AO basis.", shape=["nao", "nmo"]
+    )
+    scf_orbitals_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin orbitals in the AO basis.", shape=["nao", "nmo"]
+    )
+    scf_density_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin density matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_density_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin density matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_fock_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin Fock matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_fock_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin Fock matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_eigenvalues_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin orbital eigenvalues.", shape=["nmo"]
+    )
+    scf_eigenvalues_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin orbital eigenvalues.", shape=["nmo"]
+    )
+    scf_occupations_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin orbital occupations.", shape=["nmo"]
+    )
+    scf_occupations_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin orbital occupations.", shape=["nmo"]
+    )
+
+    # BELOW from qcsk
+    scf_coulomb_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin Coulomb matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_coulomb_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin Coulomb matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_exchange_a: Optional[Array[float]] = Field(
+        None, description="SCF alpha-spin exchange matrix in the AO basis.", shape=["nao", "nao"]
+    )
+    scf_exchange_b: Optional[Array[float]] = Field(
+        None, description="SCF beta-spin exchange matrix in the AO basis.", shape=["nao", "nao"]
+    )
+
+    # Localized-orbital SCF wavefunction quantities
+    localized_orbitals_a: Optional[Array[float]] = Field(
+        None,
+        description="Localized alpha-spin orbitals in the AO basis. All nmo orbitals are included, even if only a subset were localized.",
+        shape=["nao", "nmo"],
+    )
+    localized_orbitals_b: Optional[Array[float]] = Field(
+        None,
+        description="Localized beta-spin orbitals in the AO basis. All nmo orbitals are included, even if only a subset were localized.",
+        shape=["nao", "nmo"],
+    )
+    localized_fock_a: Optional[Array[float]] = Field(
+        None,
+        description="Alpha-spin Fock matrix in the localized molecular orbital basis. All nmo orbitals are included, even if only a subset were localized.",
+        shape=["nmo", "nmo"],
+    )
+    localized_fock_b: Optional[Array[float]] = Field(
+        None,
+        description="Beta-spin Fock matrix in the localized molecular orbital basis. All nmo orbitals are included, even if only a subset were localized.",
+        shape=["nmo", "nmo"],
+    )
+    # ABOVE from qcsk
+
+    # Return results, must be defined last
+    orbitals_a: Optional[str] = Field(None, description="Index to the alpha-spin orbitals of the primary return.")
+    orbitals_b: Optional[str] = Field(None, description="Index to the beta-spin orbitals of the primary return.")
+    density_a: Optional[str] = Field(None, description="Index to the alpha-spin density of the primary return.")
+    density_b: Optional[str] = Field(None, description="Index to the beta-spin density of the primary return.")
+    fock_a: Optional[str] = Field(None, description="Index to the alpha-spin Fock matrix of the primary return.")
+    fock_b: Optional[str] = Field(None, description="Index to the beta-spin Fock matrix of the primary return.")
+    eigenvalues_a: Optional[str] = Field(
+        None, description="Index to the alpha-spin orbital eigenvalues of the primary return."
+    )
+    eigenvalues_b: Optional[str] = Field(
+        None, description="Index to the beta-spin orbital eigenvalues of the primary return."
+    )
+    occupations_a: Optional[str] = Field(
+        None, description="Index to the alpha-spin orbital occupations of the primary return."
+    )
+    occupations_b: Optional[str] = Field(
+        None, description="Index to the beta-spin orbital occupations of the primary return."
+    )
+
+    class Config(ProtoModel.Config):
+        force_skip_defaults = True
+
+    @validator("scf_eigenvalues_a", "scf_eigenvalues_b", "scf_occupations_a", "scf_occupations_b")
+    def _assert1d(cls, v, values):
+        try:
+            v = v.reshape(-1)
+        except (ValueError, AttributeError):
+            raise ValueError("Vector must be castable to shape (-1, )!")
+        return v
+
+    @validator("scf_orbitals_a", "scf_orbitals_b")
+    def _assert2d_nao_x(cls, v, values):
+        bas = values.get("basis", None)
+
+        # Do not raise multiple errors
+        if bas is None:
+            return v
+
+        try:
+            v = v.reshape(bas.nbf, -1)
+        except (ValueError, AttributeError):
+            raise ValueError("Matrix must be castable to shape (nbf, -1)!")
+        return v
+
+    @validator(
+        "h_core_a",
+        "h_core_b",
+        "h_effective_a",
+        "h_effective_b",
+        # SCF
+        "scf_density_a",
+        "scf_density_b",
+        "scf_fock_a",
+        "scf_fock_b",
+    )
+    def _assert2d(cls, v, values):
+        bas = values.get("basis", None)
+
+        # Do not raise multiple errors
+        if bas is None:
+            return v
+
+        try:
+            v = v.reshape(bas.nbf, bas.nbf)
+        except (ValueError, AttributeError):
+            raise ValueError("Matrix must be castable to shape (nbf, nbf)!")
+        return v
+
+    @validator(
+        "orbitals_a",
+        "orbitals_b",
+        "density_a",
+        "density_b",
+        "fock_a",
+        "fock_b",
+        "eigenvalues_a",
+        "eigenvalues_b",
+        "occupations_a",
+        "occupations_b",
+    )
+    def _assert_exists(cls, v, values):
+        if values.get(v, None) is None:
+            raise ValueError(f"Return quantity {v} does not exist in the values.")
+        return v
+
+
+class WavefunctionProtocolEnum(str, Enum):
+    r"""Wavefunction to keep from a computation."""
+
+    all = "all"
+    orbitals_and_eigenvalues = "orbitals_and_eigenvalues"
+    occupations_and_eigenvalues = "occupations_and_eigenvalues"
+    return_results = "return_results"
+    none = "none"
+
+
+class ErrorCorrectionProtocol(ProtoModel):
+    r"""Configuration for how QCEngine handles error correction
+
+    WARNING: These protocols are currently experimental and only supported by NWChem tasks
+    """
+
+    default_policy: bool = Field(
+        True, description="Whether to allow error corrections to be used " "if not directly specified in `policies`"
+    )
+    # TODO (wardlt): Consider support for common policies (e.g., 'only increase iterations') as strings (see #182)
+    policies: Optional[Dict[str, bool]] = Field(
+        None,
+        description="Settings that define whether specific error corrections are allowed. "
+        "Keys are the name of a known error and values are whether it is allowed to be used.",
+    )
+
+    def allows(self, policy: str):
+        if self.policies is None:
+            return self.default_policy
+        return self.policies.get(policy, self.default_policy)
+
+
+class NativeFilesProtocolEnum(str, Enum):
+    r"""CMS program files to keep from a computation."""
+
+    all = "all"
+    input = "input"
+    none = "none"
+
+
+class AtomicResultProtocols(ProtoModel):
+    r"""Protocols regarding the manipulation of computational result data."""
+
+    wavefunction: WavefunctionProtocolEnum = Field(
+        WavefunctionProtocolEnum.none, description=str(WavefunctionProtocolEnum.__doc__)
+    )
+    stdout: bool = Field(True, description="Primary output file to keep from the computation")
+    error_correction: ErrorCorrectionProtocol = Field(
+        default_factory=ErrorCorrectionProtocol, description="Policies for error correction"
+    )
+    native_files: NativeFilesProtocolEnum = Field(
+        NativeFilesProtocolEnum.none,
+        description="Policies for keeping processed files from the computation",
+    )
+
+    class Config:
+        force_skip_defaults = True
+
+
+### Primary models
+
+
+
[docs]class AtomicInput(ProtoModel): + r"""The MolSSI Quantum Chemistry Schema""" + + id: Optional[str] = Field(None, description="The optional ID for the computation.") + schema_name: constr(strip_whitespace=True, regex="^(qc_?schema_input)$") = Field( # type: ignore + qcschema_input_default, + description=( + f"The QCSchema specification this model conforms to. Explicitly fixed as {qcschema_input_default}." + ), + ) + schema_version: int = Field( + 1, + description="The version number of :attr:`~qcelemental.models.AtomicInput.schema_name` to which this model conforms.", + ) + + molecule: Molecule = Field(..., description="The molecule to use in the computation.") + driver: DriverEnum = Field(..., description=str(DriverEnum.__doc__)) + model: Model = Field(..., description=str(Model.__doc__)) + keywords: Dict[str, Any] = Field({}, description="The program-specific keywords to be used.") + protocols: AtomicResultProtocols = Field(AtomicResultProtocols(), description=str(AtomicResultProtocols.__doc__)) + + extras: Dict[str, Any] = Field( + {}, + description="Additional information to bundle with the computation. Use for schema development and scratch space.", + ) + + provenance: Provenance = Field( + default_factory=partial(provenance_stamp, __name__), description=str(Provenance.__doc__) + ) + + class Config(ProtoModel.Config): + def schema_extra(schema, model): + schema["$schema"] = qcschema_draft + + def __repr_args__(self) -> "ReprArgs": + return [ + ("driver", self.driver.value), + ("model", self.model.dict()), + ("molecule_hash", self.molecule.get_hash()[:7]), + ]
+ + +
[docs]class AtomicResult(AtomicInput): + r"""Results from a CMS program execution.""" + + schema_name: constr(strip_whitespace=True, regex="^(qc_?schema_output)$") = Field( # type: ignore + qcschema_output_default, + description=( + f"The QCSchema specification this model conforms to. Explicitly fixed as {qcschema_output_default}." + ), + ) + properties: AtomicResultProperties = Field(..., description=str(AtomicResultProperties.__doc__)) + wavefunction: Optional[WavefunctionProperties] = Field(None, description=str(WavefunctionProperties.__doc__)) + + return_result: Union[float, Array[float], Dict[str, Any]] = Field( + ..., + description="The primary return specified by the :attr:`~qcelemental.models.AtomicInput.driver` field. Scalar if energy; array if gradient or hessian; dictionary with property keys if properties.", + ) # type: ignore + + stdout: Optional[str] = Field( + None, + description="The primary logging output of the program, whether natively standard output or a file. Presence vs. absence (or null-ness?) configurable by protocol.", + ) + stderr: Optional[str] = Field(None, description="The standard error of the program execution.") + native_files: Dict[str, Any] = Field({}, description="DSL files.") + + success: bool = Field(..., description="The success of program execution. If False, other fields may be blank.") + error: Optional[ComputeError] = Field(None, description=str(ComputeError.__doc__)) + provenance: Provenance = Field(..., description=str(Provenance.__doc__)) + + @validator("schema_name", pre=True) + def _input_to_output(cls, v): + r"""If qcschema_input is passed in, cast it to output, otherwise no""" + if v.lower().strip() in [qcschema_input_default, qcschema_output_default]: + return qcschema_output_default + raise ValueError( + "Only {0} or {1} is allowed for schema_name, " + "which will be converted to {0}".format(qcschema_output_default, qcschema_input_default) + ) + + @validator("return_result") + def _validate_return_result(cls, v, values): + if values["driver"] == "gradient": + v = np.asarray(v).reshape(-1, 3) + elif values["driver"] == "hessian": + v = np.asarray(v) + nsq = int(v.size**0.5) + v.shape = (nsq, nsq) + + return v + + @validator("wavefunction", pre=True) + def _wavefunction_protocol(cls, value, values): + # We are pre, gotta do extra checks + if value is None: + return value + elif isinstance(value, dict): + wfn = value.copy() + elif isinstance(value, WavefunctionProperties): + wfn = value.dict() + else: + raise ValueError("wavefunction must be None, a dict, or a WavefunctionProperties object.") + + # Do not propagate validation errors + if "protocols" not in values: + raise ValueError("Protocols was not properly formed.") + + # Handle restricted + restricted = wfn.get("restricted", None) + if restricted is None: + raise ValueError("`restricted` is required.") + + if restricted: + for k in list(wfn.keys()): + if k.endswith("_b"): + wfn.pop(k) + + # Handle protocols + wfnp = values["protocols"].wavefunction + return_keep = None + if wfnp == "all": + pass + elif wfnp == "none": + wfn = None + elif wfnp == "return_results": + return_keep = [ + "orbitals_a", + "orbitals_b", + "density_a", + "density_b", + "fock_a", + "fock_b", + "eigenvalues_a", + "eigenvalues_b", + "occupations_a", + "occupations_b", + ] + elif wfnp == "orbitals_and_eigenvalues": + return_keep = ["orbitals_a", "orbitals_b", "eigenvalues_a", "eigenvalues_b"] + elif wfnp == "occupations_and_eigenvalues": + return_keep = ["occupations_a", "occupations_b", "eigenvalues_a", "eigenvalues_b"] + else: + raise ValueError(f"Protocol `wavefunction:{wfnp}` is not understood.") + + if return_keep is not None: + ret_wfn = {"restricted": restricted} + if "basis" in wfn: + ret_wfn["basis"] = wfn["basis"] + + for rk in return_keep: + key = wfn.get(rk, None) + if key is None: + continue + + ret_wfn[rk] = key + ret_wfn[key] = wfn[key] + + return ret_wfn + else: + return wfn + + @validator("stdout") + def _stdout_protocol(cls, value, values): + # Do not propagate validation errors + if "protocols" not in values: + raise ValueError("Protocols was not properly formed.") + + outp = values["protocols"].stdout + if outp is True: + return value + elif outp is False: + return None + else: + raise ValueError(f"Protocol `stdout:{outp}` is not understood") + + @validator("native_files") + def _native_file_protocol(cls, value, values): + ancp = values["protocols"].native_files + if ancp == "all": + return value + elif ancp == "none": + return {} + elif ancp == "input": + return_keep = ["input"] + if value is None: + files = {} + else: + files = value.copy() + else: + raise ValueError(f"Protocol `native_files:{ancp}` is not understood") + + ret = {} + for rk in return_keep: + ret[rk] = files.get(rk, None) + return ret
+ + +class ResultProperties(AtomicResultProperties): + """QC Result Properties Schema. + + .. deprecated:: 0.12 + Use :py:func:`qcelemental.models.AtomicResultProperties` instead. + + """ + + def __init__(self, *args, **kwargs): + from warnings import warn + + warn( + "ResultProperties has been renamed to AtomicResultProperties and will be removed as soon as v0.13.0", + DeprecationWarning, + ) + super().__init__(*args, **kwargs) + + +class ResultProtocols(AtomicResultProtocols): + """QC Result Protocols Schema. + + .. deprecated:: 0.12 + Use :py:func:`qcelemental.models.AtomicResultProtocols` instead. + + """ + + def __init__(self, *args, **kwargs): + from warnings import warn + + warn( + "ResultProtocols has been renamed to AtomicResultProtocols and will be removed as soon as v0.13.0", + DeprecationWarning, + ) + super().__init__(*args, **kwargs) + + +class ResultInput(AtomicInput): + """QC Input Schema. + + .. deprecated:: 0.12 + Use :py:func:`qcelemental.models.AtomicInput` instead. + + """ + + def __init__(self, *args, **kwargs): + from warnings import warn + + warn("ResultInput has been renamed to AtomicInput and will be removed as soon as v0.13.0", DeprecationWarning) + super().__init__(*args, **kwargs) + + +class Result(AtomicResult): + """QC Result Schema. + + .. deprecated:: 0.12 + Use :py:func:`qcelemental.models.AtomicResult` instead. + + """ + + def __init__(self, *args, **kwargs): + from warnings import warn + + warn("Result has been renamed to AtomicResult and will be removed as soon as v0.13.0", DeprecationWarning) + super().__init__(*args, **kwargs) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/compute.html b/_modules/qcengine/compute.html new file mode 100644 index 000000000..1c58598c4 --- /dev/null +++ b/_modules/qcengine/compute.html @@ -0,0 +1,322 @@ + + + + + + qcengine.compute — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.compute

+"""
+Integrates the computes together
+"""
+import warnings
+from typing import TYPE_CHECKING, Any, Dict, Optional, Union
+
+from qcelemental.models import AtomicInput, AtomicResult, FailedOperation, OptimizationResult
+
+from .config import get_config
+from .exceptions import InputError, RandomError
+from .procedures import get_procedure
+from .programs import get_program
+from .util import compute_wrapper, environ_context, handle_output_metadata, model_wrapper
+
+if TYPE_CHECKING:
+    try:
+        from pydantic.v1.main import BaseModel
+    except ImportError:
+        from pydantic.main import BaseModel
+    from qcelemental.models import AtomicResult
+
+
+__all__ = ["compute", "compute_procedure"]
+
+
+def _process_failure_and_return(model, return_dict, raise_error):
+    if isinstance(model, FailedOperation):
+        if raise_error:
+            raise InputError(model.error.error_message)
+        elif return_dict:
+            return model.dict()
+        else:
+            return model
+    else:
+        return False
+
+
+
[docs]def compute( + input_data: Union[Dict[str, Any], "AtomicInput"], + program: str, + raise_error: bool = False, + task_config: Optional[Dict[str, Any]] = None, + local_options: Optional[Dict[str, Any]] = None, + return_dict: bool = False, +) -> Union["AtomicResult", "FailedOperation", Dict[str, Any]]: + """Executes a single CMS program given a QCSchema input. + + The full specification can be found at: + http://molssi-qc-schema.readthedocs.io/en/latest/index.html# + + Parameters + ---------- + input_data + A QCSchema input specification in dictionary or model from QCElemental.models + program + The CMS program with which to execute the input. + raise_error + Determines if compute should raise an error or not. + retries : int, optional + The number of random tries to retry for. + task_config + A dictionary of local configuration options corresponding to a TaskConfig object. + local_options + Deprecated parameter, renamed to ``task_config`` + return_dict + Returns a dict instead of qcelemental.models.AtomicResult + + Returns + ------- + result + AtomicResult, FailedOperation, or Dict representation of either object type + A QCSchema representation of the requested output, type depends on return_dict key. + """ + + output_data = input_data.copy() # lgtm [py/multiple-definition] + with compute_wrapper(capture_output=False, raise_error=raise_error) as metadata: + + # Grab the executor and build the input model + executor = get_program(program) + + # Build the model and validate + input_data = model_wrapper(input_data, AtomicInput) + + # Build out task_config + if task_config is None: + task_config = {} + + if local_options: + warnings.warn( + "Using the `local_options` keyword argument is deprecated in favor of using `task_config`, " + "in version 0.30.0 it will stop working.", + category=FutureWarning, + stacklevel=2, + ) + task_config = {**local_options, **task_config} + + input_engine_options = input_data.extras.pop("_qcengine_local_config", {}) + + task_config = {**task_config, **input_engine_options} + config = get_config(task_config=task_config) + + # Set environment parameters and execute + with environ_context(config=config): + + # Handle optional retries + for x in range(config.retries + 1): + try: + output_data = executor.compute(input_data, config) + break + except RandomError as e: + + if x == config.retries: + raise e + else: + metadata["retries"] += 1 + except: + raise + + return handle_output_metadata(output_data, metadata, raise_error=raise_error, return_dict=return_dict)
+ + +
[docs]def compute_procedure( + input_data: Union[Dict[str, Any], "BaseModel"], + procedure: str, + raise_error: bool = False, + task_config: Optional[Dict[str, str]] = None, + local_options: Optional[Dict[str, str]] = None, + return_dict: bool = False, +) -> Union["OptimizationResult", "FailedOperation", Dict[str, Any]]: + """Runs a procedure (a collection of the quantum chemistry executions) + + Parameters + ---------- + input_data : dict or qcelemental.models.OptimizationInput + A JSON input specific to the procedure executed in dictionary or model from QCElemental.models + procedure : {"geometric", "berny"} + The name of the procedure to run + raise_error : bool, option + Determines if compute should raise an error or not. + task_config + A dictionary of local configuration options corresponding to a TaskConfig object. + local_options + Deprecated parameter, renamed to ``task_config`` + return_dict : bool, optional, default True + Returns a dict instead of qcelemental.models.AtomicInput + + Returns + ------ + dict, OptimizationResult, FailedOperation + A QC Schema representation of the requested output, type depends on return_dict key. + """ + # Build out task_config + if task_config is None: + task_config = {} + + if local_options: + warnings.warn( + "Using the `local_options` keyword argument is depreciated in favor of using `task_config`, " + "in version 0.30.0 it will stop working.", + category=FutureWarning, + stacklevel=2, + ) + task_config = {**local_options, **task_config} + + output_data = input_data.copy() # lgtm [py/multiple-definition] + with compute_wrapper(capture_output=False, raise_error=raise_error) as metadata: + + # Grab the executor and build the input model + executor = get_procedure(procedure) + + config = get_config(task_config=task_config) + input_data = executor.build_input_model(input_data) + + # Create a base output data in case of errors + output_data = input_data.copy() # lgtm [py/multiple-definition] + + # Set environment parameters and execute + with environ_context(config=config): + output_data = executor.compute(input_data, config) + + return handle_output_metadata(output_data, metadata, raise_error=raise_error, return_dict=return_dict)
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/config.html b/_modules/qcengine/config.html new file mode 100644 index 000000000..bdeea2198 --- /dev/null +++ b/_modules/qcengine/config.html @@ -0,0 +1,503 @@ + + + + + + qcengine.config — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.config

+"""
+Creates globals for the qcengine module
+"""
+
+import fnmatch
+import getpass
+import logging
+import os
+import socket
+from typing import Any, Dict, Optional, Union
+
+try:
+    import pydantic.v1 as pydantic
+except ImportError:
+    import pydantic
+
+from .extras import get_information
+
+__all__ = ["get_config", "get_provenance_augments", "global_repr", "NodeDescriptor"]
+
+# Start a globals dictionary with small starting values
+_global_values = None
+NODE_DESCRIPTORS = {}
+LOGGER = logging.getLogger("QCEngine")
+LOGGER.setLevel(logging.CRITICAL)
+
+
+# Generic globals
+def get_global(key: Optional[str] = None) -> Union[str, Dict[str, Any]]:
+    import cpuinfo
+    import psutil
+
+    # TODO (wardlt): Implement a means of getting CPU information from compute nodes on clusters for MPI tasks
+    #  The QC code runs on a different node than the node running this Python function, which may have different info
+
+    global _global_values
+    if _global_values is None:
+        _global_values = {}
+        _global_values["hostname"] = socket.gethostname()
+        _global_values["memory"] = round(psutil.virtual_memory().available / (1024**3), 3)
+        _global_values["username"] = getpass.getuser()
+
+        # Work through VMs and logical cores.
+        if hasattr(psutil.Process(), "cpu_affinity"):
+            cpu_cnt = len(psutil.Process().cpu_affinity())
+            full_physical_cnt = psutil.cpu_count(logical=False)
+            full_logical_cnt = psutil.cpu_count(logical=True)
+            if (cpu_cnt == full_logical_cnt) and (full_physical_cnt is not None):
+                # cpu_affinity isn't capturing deliberate setting but just VMs, so use physical
+                cpu_cnt = full_physical_cnt
+        else:
+            cpu_cnt = psutil.cpu_count(logical=False)
+            if cpu_cnt is None:
+                cpu_cnt = psutil.cpu_count(logical=True)
+
+        _global_values["ncores"] = cpu_cnt
+        _global_values["nnodes"] = 1
+
+        _global_values["cpuinfo"] = cpuinfo.get_cpu_info()
+        try:
+            _global_values["cpu_brand"] = _global_values["cpuinfo"]["brand_raw"]
+        except KeyError:
+            # Remove this if py-cpuinfo is pinned to >=6.0.0
+            _global_values["cpu_brand"] = _global_values["cpuinfo"].get("brand", "(unknown)")
+
+    if key is None:
+        return _global_values.copy()
+    else:
+        return _global_values[key]
+
+
+
[docs]class NodeDescriptor(pydantic.BaseModel): + """ + Description of an individual node + """ + + # Host data + hostname_pattern: str + name: str + scratch_directory: Optional[str] = None # What location to use as scratch + + memory: Optional[float] = None + memory_safety_factor: int = 10 # Percentage of memory as a safety factor + + # Specifications + ncores: Optional[int] = pydantic.Field( + None, + description="""Number of cores accessible to each task on this node + + The default value, ``None``, will allow QCEngine to autodetect the number of cores.""", + ) + jobs_per_node: int = 1 + retries: int = 0 + + # Cluster options + is_batch_node: bool = pydantic.Field( + False, + help="""Whether the node running QCEngine is a batch node + + Some clusters are configured such that tasks are launched from a special "batch" or "MOM" onto the compute nodes. + The compute nodes on such clusters often have a different CPU architecture than the batch nodes and + often are unable to launch MPI tasks, which has two implications: + 1) QCEngine must make *all* calls to an executable via ``mpirun`` because the executables might not + be able to run on the batch node. + 2) QCEngine must run on the batch node to be able to launch tasks on the more than one compute nodes + + ``is_batch_node`` is used when creating the task configuration as a means of determining whether + ``mpiexec_command`` must always be used even for serial jobs (e.g., getting the version number) + """, + ) + mpiexec_command: Optional[str] = pydantic.Field( + None, + description="""Invocation for launching node-parallel tasks with MPI + + The invocation need not specify the number of nodes, tasks, or cores per node. + Information about the task configuration will be added to the command by use of + Python's string formatting. The configuration will be supplied as the following variables: + + {nnodes} - Number of nodes + {ranks_per_node} - Number of MPI ranks per node + {cores_per_rank} - Number of cores to use for each MPI rank + {total_ranks} - Total number of MPI ranks + + As examples, the ``aprun`` command on Cray systems should be similar to + ``aprun -n {total_ranks} -N {ranks_per_node}`` and ``mpirun`` from OpenMPI should + be similar to ``mpirun -np {total_ranks} -N {ranks_per_node}``. + + Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can + use the {cores_per_rank} option to control the hybrid parallelism. + As an example, the Cray ``aprun`` command using this figure could be: + ``aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1``. + The appropriate number of ranks per node will be determined based on the number of + cores per node and the number of cores per rank. + """, + ) + + def __init__(self, **data: Dict[str, Any]): + data = parse_environment(data) + super().__init__(**data) + + if self.mpiexec_command is not None: + # Ensure that the mpiexec_command contains necessary information + if not ("{total_ranks}" in self.mpiexec_command or "{nnodes}" in self.mpiexec_command): + raise ValueError("mpiexec_command must contain either {total_ranks} or {nnodes}") + if "{ranks_per_node}" not in self.mpiexec_command: + raise ValueError("mpiexec_command must explicitly state the number of ranks per node") + + class Config: + extra = "forbid"
+ + +class TaskConfig(pydantic.BaseSettings): + """Description of the configuration used to launch a task.""" + + # Specifications + ncores: int = pydantic.Field(None, description="Number cores per task on each node") + nnodes: int = pydantic.Field(None, description="Number of nodes per task") + memory: float = pydantic.Field( + None, description="Amount of memory in GiB (2^30 bytes; not GB = 10^9 bytes) per node." + ) + scratch_directory: Optional[str] # What location to use as scratch + retries: int # Number of retries on random failures + mpiexec_command: Optional[str] # Command used to launch MPI tasks, see NodeDescriptor + use_mpiexec: bool = False # Whether it is necessary to use MPI to run an executable + cores_per_rank: int = pydantic.Field(1, description="Number of cores per MPI rank") + scratch_messy: bool = pydantic.Field( + False, description="Leave scratch directory and contents on disk after completion." + ) + + class Config(pydantic.BaseSettings.Config): + extra = "forbid" + env_prefix = "QCENGINE_" + + +def _load_defaults() -> None: + """ + Pulls the defaults from the QCA folder + """ + + # Find the config + load_path = None + test_paths = [os.getcwd(), os.path.join(os.path.expanduser("~"), ".qcarchive")] + + if "DQM_CONFIG_PATH" in os.environ: + test_paths.insert(0, os.environ["DQM_CONFIG_PATH"]) + + for path in test_paths: + path = os.path.join(path, "qcengine.yaml") + if os.path.exists(path): + load_path = path + break + + if load_path is None: + LOGGER.info("Could not find 'qcengine.yaml'. Searched the following paths: {}".format(", ".join(test_paths))) + LOGGER.info("Using default options...") + + else: + import yaml + + LOGGER.info("Found 'qcengine.yaml' at path: {}".format(load_path)) + with open(load_path, "r") as stream: + user_config = yaml.load(stream, Loader=yaml.SafeLoader) + + for k, v in user_config.items(): + NODE_DESCRIPTORS[k] = NodeDescriptor(name=k, **v) + + +
[docs]def global_repr() -> str: + """ + A representation of the current global configuration. + """ + + ret = "" + ret += "Host information:\n" + ret += "-" * 80 + "\n" + + prov = get_provenance_augments() + for k in ["username", "hostname", "cpu"]: + ret += "{:<30} {:<30}\n".format(k, prov[k]) + + ret += "\nNode information:\n" + ret += "-" * 80 + "\n" + for k, v in get_node_descriptor(): + ret += " {:<28} {}\n".format(k, v) + + if k in ["scratch_directory", "memory_per_job"]: + ret += "\n" + + ret += "\nJob information:\n" + ret += "-" * 80 + "\n" + for k, v in get_config(): + ret += " {:<28} {}\n".format(k, v) + + ret += "-" * 80 + "\n" + + return ret
+ + +def get_node_descriptor(hostname: Optional[str] = None) -> NodeDescriptor: + """ + Find the correct NodeDescriptor based off current hostname + """ + if isinstance(hostname, NodeDescriptor): + return hostname + + if hostname is None: + hostname = get_global("hostname") + + # Find a match + for name, node in NODE_DESCRIPTORS.items(): + + if fnmatch.fnmatch(hostname, node.hostname_pattern): + config = node + break + else: + config = NodeDescriptor( + name="default", hostname_pattern="*", memory=get_global("memory"), ncores=get_global("ncores") + ) + + return config + + +def parse_environment(data: Dict[str, Any]) -> Dict[str, Any]: + """Collects local environment variable values into ``data`` for any keys with RHS starting with ``$``.""" + ret = {} + for k, var in data.items(): + if isinstance(var, str): + var = os.path.expanduser(os.path.expandvars(var)) + if var.startswith("$"): + var = None + + ret[k] = var + + return ret + + +def read_qcengine_task_environment() -> Dict[str, Any]: + """ + Reads the qcengine task-related environment variables and returns a dictionary of the values. + """ + + ret = {} + for k, v in os.environ.items(): + if k.startswith("QCENGINE_"): + ret[k[9:].lower()] = v + + return ret + + +
[docs]def get_config(*, hostname: Optional[str] = None, task_config: Dict[str, Any] = None) -> TaskConfig: + """ + Returns the configuration key for qcengine. + """ + + if task_config is None: + task_config = {} + + task_config_env = read_qcengine_task_environment() + task_config = {**task_config_env, **parse_environment(task_config)} + + config = {} + + # Node data + node = get_node_descriptor(hostname) + ncores = node.ncores or get_global("ncores") + config["scratch_directory"] = task_config.pop("scratch_directory", node.scratch_directory) + config["retries"] = task_config.pop("retries", node.retries) + + # Jobs per node + jobs_per_node = int(task_config.pop("jobs_per_node", None) or node.jobs_per_node) + + # Handle memory + memory = task_config.pop("memory", None) + if memory is None: + memory = node.memory or get_global("memory") + memory_coeff = 1 - node.memory_safety_factor / 100 + memory = round(memory * memory_coeff / jobs_per_node, 3) + + config["memory"] = memory + + # Get the number of cores available to each task + ncores = int(task_config.pop("ncores", int(ncores / jobs_per_node))) + if ncores < 1: + raise KeyError("Number of jobs per node exceeds the number of available cores.") + + config["ncores"] = ncores + config["nnodes"] = int(task_config.pop("nnodes", 1)) + + # Add in the MPI launch command template + config["mpiexec_command"] = node.mpiexec_command + config["use_mpiexec"] = node.is_batch_node or config["nnodes"] > 1 + config["cores_per_rank"] = task_config.get("cores_per_rank", 1) + + # Override any settings + if task_config is not None: + config.update(task_config) + + # Make sure mpirun command is defined if needed + if config["use_mpiexec"] and config["mpiexec_command"] is None: + raise ValueError( + "You need to define the mpiexec command for this node. " + "See: https://qcengine.readthedocs.io/en/stable/environment.html" + ) + + return TaskConfig(**config)
+ + +
[docs]def get_provenance_augments() -> Dict[str, str]: + return { + "cpu": get_global("cpu_brand"), + "hostname": get_global("hostname"), + "username": get_global("username"), + "qcengine_version": get_information("version"), + }
+ + +def get_logger() -> "Logger": + return LOGGER + + +# Pull in the local variables +_load_defaults() +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/mdi_server.html b/_modules/qcengine/mdi_server.html new file mode 100644 index 000000000..73529219b --- /dev/null +++ b/_modules/qcengine/mdi_server.html @@ -0,0 +1,628 @@ + + + + + + qcengine.mdi_server — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.mdi_server

+"""Support for using QCEngine as an MDI engine.
+For details regarding MDI, see https://molssi.github.io/MDI_Library/html/index.html.
+
+"""
+from typing import Any, Dict, List, Optional
+
+import numpy as np
+import qcelemental as qcel
+from qcelemental.util import which_import
+
+from .compute import compute
+
+try:
+    from mdi import (
+        MDI_CHAR,
+        MDI_COMMAND_LENGTH,
+        MDI_DOUBLE,
+        MDI_INT,
+        MDI_MAJOR_VERSION,
+        MDI_Accept_Communicator,
+        MDI_Init,
+        MDI_MPI_get_world_comm,
+        MDI_Recv,
+        MDI_Recv_Command,
+        MDI_Register_Command,
+        MDI_Register_Node,
+        MDI_Send,
+    )
+
+    use_mdi = True
+except ImportError:
+    use_mdi = False
+
+
+
[docs]class MDIServer: + def __init__( + self, + mdi_options: str, + program: str, + molecule, + model, + keywords, + raise_error: bool = False, + local_options: Optional[Dict[str, Any]] = None, + ): + """Initialize an MDIServer object for communication with MDI + + Parameters + ---------- + mdi_options: str + Options used during MDI initialization. + program : str + The program to execute the input with. + molecule + The initial state of the molecule. + model + The simulation model to use. + keywords + Program-specific keywords. + raise_error : bool, optional + Determines if compute should raise an error or not. + local_options : Optional[Dict[str, Any]], optional + A dictionary of local configuration options + """ + + if not use_mdi: + raise Exception("Trying to run as an MDI engine, but the MDI Library was not found") + + if MDI_MAJOR_VERSION < 1: + raise Exception("QCEngine requires version 1.0.0 or higher of the MDI Library") + + # Confirm that the MDI library has been located + which_import("mdi", raise_error=True, raise_msg="Please install via 'conda install pymdi -c conda-forge'") + + # Initialize MDI + MDI_Init(mdi_options) + + # Input variables + self.molecule = molecule + self.model = model + self.keywords = keywords + self.program = program + self.raise_error = raise_error + self.local_options = local_options + + # The MDI interface does not currently support multiple fragments + if len(self.molecule.fragments) != 1: + raise Exception("The MDI interface does not support multiple fragments") + + # Molecule charge and multiplicity + self.total_charge = self.molecule.molecular_charge + self.multiplicity = self.molecule.molecular_multiplicity + + # Flag to track whether the latest molecule specification has been validated + self.molecule_validated = True + + # Output of most recent compute call + self.compute_return = None + + # Set whether the current energy is valid + self.energy_is_current = False + + # MPI variables + self.mpi_world = None + self.world_rank = 0 + + # Flag to stop listening for MDI commands + self.stop_listening = False + + # Dictionary of all supported MDI commands + self.commands = { + "<@": self.send_node, + "<NATOMS": self.send_natoms, + ">NATOMS": self.recv_natoms, + "<COORDS": self.send_coords, + "<ENERGY": self.send_energy, + "<FORCES": self.send_forces, + ">COORDS": self.recv_coords, + "SCF": self.run_energy, + "<ELEMENTS": self.send_elements, + ">ELEMENTS": self.recv_elements, + "<MASSES": self.send_masses, + ">MASSES": self.recv_masses, + "<TOTCHARGE": self.send_total_charge, + ">TOTCHARGE": self.recv_total_charge, + "<ELEC_MULT": self.send_multiplicity, + ">ELEC_MULT": self.recv_multiplicity, + "EXIT": self.stop, + } + + # Register the @DEFAULT node + MDI_Register_Node("@DEFAULT") + + # Register all supported commands + for c in self.commands.keys(): + MDI_Register_Command("@DEFAULT", c) + + # Set the current node + self.current_node = "@DEFAULT" + + # Accept a communicator to the driver code + self.comm = MDI_Accept_Communicator() + +
[docs] def update_molecule(self, key: str, value): + """Update the molecule + + Parameters + ---------- + key : str + Key of the molecular element to update + value + Update value + """ + if key == "molecular_charge" or key == "molecular_multiplicity": + # In order to validate correctly, the charges and multiplicities must be set simultaneously + try: + self.molecule = qcel.models.Molecule( + **{ + **self.molecule.dict(), + **{ + "molecular_charge": self.total_charge, + "fragment_charges": [self.total_charge], + "molecular_multiplicity": self.multiplicity, + "fragment_multiplicities": [self.multiplicity], + }, + } + ) + self.molecule_validated = True + except qcel.exceptions.ValidationError: + # The molecule didn't validate correctly, but a future >TOTCHARGE or >ELEC_MULT command might fix it + self.molecule_validated = False + else: + try: + self.molecule = qcel.models.Molecule(**{**self.molecule.dict(), **{key: value}}) + self.molecule_validated = True + except qcel.exceptions.ValidationError: + if self.molecule_validated: + # This update caused the validation error + raise Exception("MDI command caused a validation error")
+ + # Respond to the <@ command +
[docs] def send_node(self) -> str: + """Send the name of the current node through MDI + + Returns + ------- + node : str + Name of the current node + """ + node = self.current_node + MDI_Send(node, MDI_COMMAND_LENGTH, MDI_CHAR, self.comm) + return node
+ + # Respond to the <NATOMS command +
[docs] def send_natoms(self) -> int: + """Send the number of atoms through MDI + + Returns + ------- + natom : int + Number of atoms + """ + natom = len(self.molecule.geometry) + MDI_Send(natom, 1, MDI_INT, self.comm) + return natom
+ + # Respond to the >NATOMS command +
[docs] def recv_natoms(self, natoms: Optional[int] = None) -> None: + """Receive the number of atoms in the system through MDI and create a new molecule with them + + Parameters + ---------- + natoms : int, optional + New number of atoms. If None, receive through MDI. + """ + natom = len(self.molecule.geometry) + if natoms is None: + natoms = MDI_Recv(1, MDI_INT, self.comm) + + mol_string = "" + for iatom in range(natoms): + mol_string += "He " + str(1.0 * iatom) + " 0.0 0.0\n" + self.molecule = qcel.models.Molecule.from_data(mol_string) + + self.energy_is_current = False
+ + # Respond to the <COORDS command +
[docs] def send_coords(self) -> np.ndarray: + """Send the nuclear coordinates through MDI + + Returns + ------- + coords : np.ndarray + Nuclear coordinates + """ + natom = len(self.molecule.geometry) + + coords = np.reshape(self.molecule.geometry, (3 * natom)) + MDI_Send(coords, 3 * natom, MDI_DOUBLE, self.comm) + + return coords
+ + # Respond to the >COORDS command +
[docs] def recv_coords(self, coords: Optional[np.ndarray] = None) -> None: + """Receive a set of nuclear coordinates through MDI and assign them to the atoms in the current molecule + + Parameters + ---------- + coords : np.ndarray, optional + New nuclear coordinates. If None, receive through MDI. + """ + natom = len(self.molecule.geometry) + if coords is None: + coords = np.zeros(3 * natom) + MDI_Recv(3 * natom, MDI_DOUBLE, self.comm, buf=coords) + new_geometry = np.reshape(coords, (-1, 3)) + self.molecule = qcel.models.Molecule(**{**self.molecule.dict(), **{"geometry": new_geometry}}) + self.energy_is_current = False
+ + # Respond to the <ENERGY command +
[docs] def send_energy(self) -> float: + """Send the total energy through MDI + + Returns + ------- + energy : float + Energy of the system + """ + # Ensure that the molecule currently passes validation + if not self.molecule_validated: + raise Exception("MDI attempting to compute energy on an unvalidated molecule") + self.run_energy() + + # Confirm that the calculation completed successfully + if not hasattr(self.compute_return, "properties"): + raise Exception("MDI Calculation failed: \n\n" + str(self.compute_return.error.error_message)) + + properties = self.compute_return.properties.dict() + energy = properties["return_energy"] + MDI_Send(energy, 1, MDI_DOUBLE, self.comm) + return energy
+ + # Respond to the <FORCES command +
[docs] def send_forces(self) -> np.ndarray: + """Send the nuclear forces through MDI + + Returns + ------- + forces : np.ndarray + Forces on the nuclei + """ + # Ensure that the molecule currently passes validation + if not self.molecule_validated: + raise Exception("MDI attempting to compute gradients on an unvalidated molecule") + self.run_energy() + properties = self.compute_return.properties.dict() + + forces = np.reshape(-1.0 * properties["return_gradient"], (-1,)) + + if len(forces) != 3 * len(self.molecule.geometry): + raise Exception( + "MDI: The length of the forces is not what was expected. Expected: " + + str(3 * len(self.molecule.geometry)) + + " Actual: " + + str(len(forces)) + ) + + MDI_Send(forces, len(forces), MDI_DOUBLE, self.comm) + return forces
+ + # Respond to the SCF command +
[docs] def run_energy(self) -> None: + if not self.energy_is_current: + """Ensure that the orientation of the molecule remains fixed""" + self.update_molecule("fix_com", True) + self.update_molecule("fix_orientation", True) + + """Run an energy calculation""" + input = qcel.models.AtomicInput( + molecule=self.molecule, driver="gradient", model=self.model, keywords=self.keywords + ) + self.compute_return = compute( + input_data=input, program=self.program, raise_error=self.raise_error, local_options=self.local_options + ) + + # If there is an error message, print it out + if hasattr(self.compute_return, "error"): + if self.compute_return.error is not None: + print("---------------- QCEngine Compute Error ----------------\n\n") + print(str(self.compute_return.error.error_message)) + print("\n\n-------------- End QCEngine Compute Error --------------", flush=True) + + self.energy_is_current = True
+ + # Respond to the <ELEMENTS command +
[docs] def send_elements(self): + """Send the atomic number of each nucleus through MDI + + Returns + ------- + elements : :obj:`list` of :obj:`int` + Element of each atom + """ + natom = len(self.molecule.geometry) + elements = [qcel.periodictable.to_atomic_number(self.molecule.symbols[iatom]) for iatom in range(natom)] + MDI_Send(elements, natom, MDI_INT, self.comm) + return elements
+ + # Respond to the >ELEMENTS command +
[docs] def recv_elements(self, elements: Optional[List[int]] = None): + """Receive a set of atomic numbers through MDI and assign them to the atoms in the current molecule + + Parameters + ---------- + elements : :obj:`list` of :obj:`int`, optional + New element numbers. If None, receive through MDI. + """ + natom = len(self.molecule.geometry) + if elements is None: + elements = MDI_Recv(natom, MDI_INT, self.comm) + + for iatom in range(natom): + self.molecule.symbols[iatom] = qcel.periodictable.to_symbol(elements[iatom]) + + return elements
+ + # Respond to the <MASSES command +
[docs] def send_masses(self) -> np.ndarray: + """Send the nuclear masses through MDI + + Returns + ------- + masses : np.ndarray + Atomic masses + """ + natom = len(self.molecule.geometry) + masses = self.molecule.masses + MDI_Send(masses, natom, MDI_DOUBLE, self.comm) + return masses
+ + # Respond to the >MASSES command +
[docs] def recv_masses(self, masses: Optional[List[float]] = None) -> None: + """Receive a set of nuclear masses through MDI and assign them to the atoms in the current molecule + + Parameters + ---------- + masses : :obj:`list` of :obj:`float`, optional + New nuclear masses. If None, receive through MDI. + """ + natom = len(self.molecule.geometry) + if masses is None: + masses = MDI_Recv(natom, MDI_DOUBLE, self.comm) + self.update_molecule("masses", masses) + self.energy_is_current = False
+ + # Respond to the <TOTCHARGE command +
[docs] def send_total_charge(self) -> float: + """Send the total system charge through MDI + + Returns + ------- + charge : float + Total charge of the system + """ + charge = self.molecule.molecular_charge + MDI_Send(charge, 1, MDI_DOUBLE, self.comm) + return charge
+ + # Respond to the >TOTCHARGE command +
[docs] def recv_total_charge(self, charge: Optional[float] = None) -> None: + """Receive the total system charge through MDI + + Parameters + ---------- + charge : float, optional + New charge of the system. If None, receive through MDI. + """ + if charge is None: + charge = MDI_Recv(1, MDI_DOUBLE, self.comm) + self.total_charge = charge + self.energy_is_current = False + + # Allow a validation error here, because a future >ELEC_MULT command might resolve it + try: + self.update_molecule("molecular_charge", self.total_charge) + except qcel.exceptions.ValidationError: + pass
+ + # Respond to the <ELEC_MULT command +
[docs] def send_multiplicity(self) -> int: + """Send the electronic multiplicity through MDI + + Returns + ------- + multiplicity : int + Multiplicity of the system + """ + multiplicity = self.molecule.molecular_multiplicity + MDI_Send(multiplicity, 1, MDI_INT, self.comm) + return multiplicity
+ + # Respond to the >ELEC_MULT command +
[docs] def recv_multiplicity(self, multiplicity: Optional[int] = None) -> None: + """Receive the electronic multiplicity through MDI + + Parameters + ---------- + multiplicity : int, optional + New multiplicity of the system. If None, receive through MDI. + """ + if multiplicity is None: + multiplicity = MDI_Recv(1, MDI_INT, self.comm) + self.multiplicity = multiplicity + self.energy_is_current = False + + # Allow a validation error here, because a future >TOTCHARGE command might resolve it + try: + self.update_molecule("molecular_multiplicity", self.multiplicity) + except qcel.exceptions.ValidationError: + pass
+ + # Respond to the EXIT command +
[docs] def stop(self) -> None: + """Stop listening for MDI commands""" + self.stop_listening = True
+ + # Enter server mode, listening for commands from the driver +
[docs] def start(self) -> None: + """Receive commands through MDI and respond to them as defined by the MDI Standard""" + + while not self.stop_listening: + if self.world_rank == 0: + command = MDI_Recv_Command(self.comm) + else: + command = None + if self.world_rank == 0: + print("MDI command received: " + str(command)) + + # Search for this command in self.commands + found_command = False + for supported_command in self.commands: + if not found_command and command == supported_command: + # Run the function corresponding to this command + self.commands[supported_command]() + found_command = True + if not found_command: + raise Exception("Unrecognized command: " + str(command))
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/procedures/base.html b/_modules/qcengine/procedures/base.html new file mode 100644 index 000000000..a83b1a2da --- /dev/null +++ b/_modules/qcengine/procedures/base.html @@ -0,0 +1,214 @@ + + + + + + qcengine.procedures.base — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.procedures.base

+"""
+Imports the various procedure backends
+"""
+
+from typing import Set
+
+from ..exceptions import InputError, ResourceError
+from .berny import BernyProcedure
+from .geometric import GeometricProcedure
+from .nwchem_opt import NWChemDriverProcedure
+from .optking import OptKingProcedure
+from .torsiondrive import TorsionDriveProcedure
+from .model import ProcedureHarness
+
+__all__ = ["register_procedure", "get_procedure", "list_all_procedures", "list_available_procedures"]
+
+procedures = {}
+
+
+def register_procedure(entry_point: ProcedureHarness) -> None:
+    """
+    Register a new ProcedureHarness with QCEngine
+    """
+
+    name = entry_point.name
+    if name.lower() in procedures.keys():
+        raise ValueError("{} is already a registered procedure.".format(name))
+
+    procedures[name.lower()] = entry_point
+
+
+
[docs]def get_procedure(name: str) -> ProcedureHarness: + """ + Returns a procedures executor class + """ + + name = name.lower() + + if name not in procedures: + raise InputError(f"Procedure {name} is not registered to QCEngine.") + + ret = procedures[name] + if not ret.found(): + raise ResourceError(f"Procedure {name} is registered with QCEngine, but cannot be found.") + + return ret
+ + +
[docs]def list_all_procedures() -> Set[str]: + """ + List all procedures registered by QCEngine. + """ + return set(procedures.keys())
+ + +
[docs]def list_available_procedures() -> Set[str]: + """ + List all procedures that can be exectued (found) by QCEngine. + """ + + ret = set() + for k, p in procedures.items(): + if p.found(): + ret.add(k) + + return ret
+ + +register_procedure(GeometricProcedure()) +register_procedure(OptKingProcedure()) +register_procedure(BernyProcedure()) +register_procedure(NWChemDriverProcedure()) +register_procedure(TorsionDriveProcedure()) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/programs/base.html b/_modules/qcengine/programs/base.html new file mode 100644 index 000000000..8a84be7ef --- /dev/null +++ b/_modules/qcengine/programs/base.html @@ -0,0 +1,284 @@ + + + + + + qcengine.programs.base — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.programs.base

+"""
+Imports the various compute backends
+"""
+
+from typing import Set
+
+from ..exceptions import InputError, ResourceError
+from .model import ProgramHarness
+from .adcc import AdccHarness
+from .cfour import CFOURHarness
+from .dftd3 import DFTD3Harness
+from .dftd_ng import DFTD4Harness, SDFTD3Harness
+from .gamess import GAMESSHarness
+from .gcp import GCPHarness, MCTCGCPHarness
+from .molpro import MolproHarness
+from .mopac import MopacHarness
+from .mp2d import MP2DHarness
+from .mrchem import MRChemHarness
+from .nwchem import NWChemHarness
+from .openmm import OpenMMHarness
+from .psi4 import Psi4Harness
+from .qchem import QChemHarness
+from .qcore import EntosHarness, QcoreHarness
+from .rdkit import RDKitHarness
+from .terachem import TeraChemHarness
+from .terachem_frontend import TeraChemFrontEndHarness
+from .terachem_pbs import TeraChemPBSHarness
+from .torchani import TorchANIHarness
+from .turbomole import TurbomoleHarness
+from .xtb import XTBHarness
+from .mace import MACEHarness
+from .aimnet2 import AIMNET2Harness
+
+__all__ = ["register_program", "get_program", "list_all_programs", "list_available_programs"]
+
+programs = {}
+
+
+
[docs]def register_program(entry_point: ProgramHarness) -> None: + """ + Register a new ProgramHarness with QCEngine. + """ + + name = entry_point.name + if name.lower() in programs.keys(): + raise ValueError("{} is already a registered program.".format(name)) + + programs[name.lower()] = entry_point
+ + +
[docs]def unregister_program(name: str) -> None: + """ + Unregisters a given program. + """ + + ret = programs.pop(name.lower(), None) + if ret is None: + raise KeyError(f"Program {name} is not registered with QCEngine")
+ + +
[docs]def get_program(name: str, check: bool = True) -> ProgramHarness: + """ + Returns a program's executor class + + Parameters + ---------- + check + ``True`` Do raise error if program not found. ``False`` is handy for + the specialized case of calling non-execution methods (like parsing for testing) + on the returned ``Harness``. + + """ + name = name.lower() + + if name not in programs: + raise InputError(f"Program {name} is not registered to QCEngine.") + + ret = programs[name] + if check: + try: + ret.found(raise_error=True) + except ModuleNotFoundError as err: + raise ResourceError(f"Program {name} is registered with QCEngine, but cannot be found.") from err + + return ret
+ + +
[docs]def list_all_programs() -> Set[str]: + """ + List all programs registered by QCEngine. + """ + return set(programs.keys())
+ + +
[docs]def list_available_programs() -> Set[str]: + """ + List all programs that can be exectued (found) by QCEngine. + """ + + ret = set() + for k, p in programs.items(): + if p.found(): + ret.add(k) + + return ret
+ + +# Quantum +register_program(AdccHarness()) +register_program(CFOURHarness()) +register_program(EntosHarness()) # Duplicate of Qcore harness to transition the namespace, to be deprecated +register_program(GAMESSHarness()) +register_program(MRChemHarness()) +register_program(MolproHarness()) +register_program(NWChemHarness()) +register_program(Psi4Harness()) +register_program(QChemHarness()) +register_program(QcoreHarness()) +register_program(TeraChemHarness()) +register_program(TurbomoleHarness()) +register_program(TeraChemFrontEndHarness()) +register_program(TeraChemPBSHarness()) + +# Semi-empirical +register_program(MopacHarness()) +register_program(XTBHarness()) + +# AI +register_program(TorchANIHarness()) +register_program(MACEHarness()) +register_program(AIMNET2Harness()) + +# Molecular Mechanics +register_program(RDKitHarness()) +register_program(OpenMMHarness()) + +# Analytical Corrections +register_program(DFTD3Harness()) +register_program(DFTD4Harness()) +register_program(SDFTD3Harness()) +register_program(GCPHarness()) +register_program(MCTCGCPHarness()) +register_program(MP2DHarness()) +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/programs/model.html b/_modules/qcengine/programs/model.html new file mode 100644 index 000000000..095f9569e --- /dev/null +++ b/_modules/qcengine/programs/model.html @@ -0,0 +1,305 @@ + + + + + + qcengine.programs.model — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.programs.model

+import abc
+import logging
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+try:
+    from pydantic.v1 import BaseModel
+except ImportError:
+    from pydantic import BaseModel
+from qcelemental.models import AtomicInput, AtomicResult, FailedOperation
+
+from qcengine.exceptions import KnownErrorException
+from qcengine.config import TaskConfig
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]class ProgramHarness(BaseModel, abc.ABC): + + _defaults: Dict[str, Any] = {} + name: str + scratch: bool + thread_safe: bool + thread_parallel: bool + node_parallel: bool + managed_memory: bool + extras: Optional[Dict[str, Any]] + + class Config: + allow_mutation: False + extra: "forbid" + + def __init__(self, **kwargs): + super().__init__(**{**self._defaults, **kwargs}) + +
[docs] @abc.abstractmethod + def compute(self, input_data: AtomicInput, config: TaskConfig) -> Union[AtomicResult, FailedOperation]: + """Top-level compute method to be implemented for every ProgramHarness + + Note: + This method behave in any of the following ways: + 1. Return AtomicResult upon successful completion of a calculation + 2. Return FailedOperation object if an operation was unsuccessful or raised an exception. This is most + likely to occur if the underlying QC package has a QCSchema API that catches exceptions and + returns them as FailedOperation objects to end users. + 3. Raise an exception if a computation failed. The raised exception will be handled by the + qcng.compute() method and either raised or packaged as a FailedOperation object. + """ + pass
+ +
[docs] @staticmethod + @abc.abstractmethod + def found(raise_error: bool = False) -> bool: + """ + Checks if the program can be found. + + Parameters + ---------- + raise_error : bool, optional + If True, raises an error if the program cannot be found. + + Returns + ------- + bool + Returns True if the program was found, False otherwise. + """
+ + ## Utility + +
[docs] def get_version(self) -> str: + """Finds program, extracts version, returns normalized version string. + + Returns + ------- + str + Return a valid, safe python version string. + """
+ + ## Computers + +
[docs] def build_input( + self, input_model: AtomicInput, config: TaskConfig, template: Optional[str] = None + ) -> Dict[str, Any]: + raise ValueError("build_input is not implemented for {}.", self.__class__)
+ +
[docs] def execute( + self, + inputs: Dict[str, Any], + extra_outfiles: Optional[List[str]] = None, + extra_commands: Optional[List[str]] = None, + scratch_name: Optional[str] = None, + timeout: Optional[int] = None, + ) -> Tuple[bool, Dict[str, Any]]: + raise ValueError("execute is not implemented for {}.", self.__class__)
+ +
[docs] def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": + raise ValueError("parse_output is not implemented for {}.", self.__class__)
+ + +class ErrorCorrectionProgramHarness(ProgramHarness, abc.ABC): + """Base class for Harnesses that include logic to correct common errors + + Classes which implement this Harness must override the :meth:`_compute` method + rather than :meth:`compute`. The ``compute`` method from this class will make + calls to ``_compute`` with different actions as it attempts to correct errors in the + input files. + + The error corrections are defined by first implementing a :class:`KnownErrorException` + that contains logic to determine if a certain error occurs and returns an appropriate + update to the keywords of an AtomicInput. + + Then, modify the ``_compute`` method of your Harness to run the ``detect_error`` method + of each ``KnownErrorException`` for the Harness after it finishes performing the computation. + The ``detect_error`` method will raise an exception that is caught by the + ``ErrorCorrectionProgramHarness`` and used to determine if/how to re-run the computation. + """ + + def _compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: + raise NotImplementedError() + + def compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: + # Get the error correction configuration + error_policy = input_data.protocols.error_correction + + # Create a local copy of the input data + local_input_data = input_data + + # Run the method and, if it fails, assess if the failure is restartable + observed_errors = {} # Errors that have been observed previously + while True: + try: + result = self._compute(local_input_data, config) + break + except KnownErrorException as e: + logger.info(f"Caught a {type(e)} error.") + + # Determine whether this specific type of error is allowed + correction_allowed = error_policy.allows(e.error_name) + if not correction_allowed: + logger.info(f'Error correction for "{e.error_name}" is not allowed') + raise e + logger.info(f'Error correction for "{e.error_name}" is allowed') + + # Check if it has run before + # TODO (wardlt): Should we allow errors to be run >1 time? + previously_run = e.error_name in observed_errors + if previously_run: + logger.info( + "Error has been observed before and mitigation did not fix the issue. Raising exception" + ) + raise e + + # Generate and apply the updated keywords + keyword_updates = e.create_keyword_update(local_input_data) + new_keywords = local_input_data.keywords.copy() + new_keywords.update(keyword_updates) + local_input_data = AtomicInput(**local_input_data.dict(exclude={"keywords"}), keywords=new_keywords) + + # Store the error details and mitigations employed + observed_errors[e.error_name] = {"details": e.details, "keyword_updates": keyword_updates} + + # Add the errors observed and corrected for, if any + if len(observed_errors) > 0: + result.extras["observed_errors"] = observed_errors + return result +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/stock_mols.html b/_modules/qcengine/stock_mols.html new file mode 100644 index 000000000..b9467dd6c --- /dev/null +++ b/_modules/qcengine/stock_mols.html @@ -0,0 +1,340 @@ + + + + + + qcengine.stock_mols — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.stock_mols

+"""
+A small list of molecules used to validate and tests computation.
+"""
+
+import copy
+
+from qcelemental.models import Molecule
+
+_test_mols = {
+    "hydrogen": {
+        "symbols": ["H", "H"],
+        "geometry": [0, 0, -0.65, 0.0, 0.0, 0.65],
+        "molecular_multiplicity": 1,
+        "connectivity": [[0, 1, 1]],
+    },
+    "lithium": {"symbols": ["Li"], "geometry": [0, 0, 0], "molecular_multiplicity": 2, "connectivity": None},
+    "water": {
+        "geometry": [
+            0.0,
+            0.0,
+            -0.1294769411935893,
+            0.0,
+            -1.494187339479985,
+            1.0274465079245698,
+            0.0,
+            1.494187339479985,
+            1.0274465079245698,
+        ],
+        "symbols": ["O", "H", "H"],
+        "connectivity": [[0, 1, 1], [0, 2, 1]],
+    },
+    "eneyne": {
+        "symbols": ["C", "C", "H", "H", "H", "H", "C", "C", "H", "H"],
+        "fragments": [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9]],
+        "geometry": [
+            0.000000,
+            -0.667578,
+            -2.124659,
+            0.000000,
+            0.667578,
+            -2.124659,
+            0.923621,
+            -1.232253,
+            -2.126185,
+            -0.923621,
+            -1.232253,
+            -2.126185,
+            -0.923621,
+            1.232253,
+            -2.126185,
+            0.923621,
+            1.232253,
+            -2.126185,
+            0.000000,
+            0.000000,
+            2.900503,
+            0.000000,
+            0.000000,
+            1.693240,
+            0.000000,
+            0.000000,
+            0.627352,
+            0.000000,
+            0.000000,
+            3.963929,
+        ],
+    },
+    "ethane": {
+        "geometry": [
+            [+1.54034068369141, -1.01730823913235, +0.93128102073425],
+            [+4.07197633001232, -0.09756825926424, -0.02203578938791],
+            [+0.00025636057017, +0.00139534039687, +0.00111211603233],
+            [+1.30983130616505, -3.03614919350581, +0.54918567185649],
+            [+1.38003941036405, -0.71812565437083, +2.97078783593882],
+            [+5.61209917480096, -1.11612498901607, +0.90799157528946],
+            [+4.30241880148479, +1.92102238874847, +0.36057345099335],
+            [+4.23222331256867, -0.39619160402976, -2.06158817835790],
+        ],
+        "symbols": ["C", "C", "H", "H", "H", "H", "H", "H"],
+        "connectivity": [[0, 1, 1], [0, 2, 1], [0, 3, 1], [0, 4, 1], [1, 5, 1], [1, 6, 1], [1, 7, 1]],
+    },
+    "mindless-01": {
+        "symbols": ["Na", "H", "O", "H", "F", "H", "H", "O", "N", "H", "H", "Cl", "B", "B", "N", "Al"],
+        "geometry": [
+            [-1.85528263484662, +3.58670515364616, -2.41763729306344],
+            [+4.40178023537845, +0.02338844412653, -4.95457749372945],
+            [-2.98706033463438, +4.76252065456814, +1.27043301573532],
+            [+0.79980886075526, +1.41103455609189, -5.04655321620119],
+            [-4.20647469409936, +1.84275767548460, +4.55038084858449],
+            [-3.54356121843970, -3.18835665176557, +1.46240021785588],
+            [+2.70032160109941, +1.06818452504054, -1.73234650374438],
+            [+3.73114088824361, -2.07001543363453, +2.23160937604731],
+            [-1.75306819230397, +0.35951417150421, +1.05323406177129],
+            [+5.41755788583825, -1.57881830078929, +1.75394002750038],
+            [-2.23462868255966, -2.13856505054269, +4.10922285746451],
+            [+1.01565866207568, -3.21952154552768, -3.36050963020778],
+            [+2.42119255723593, +0.26626435093114, -3.91862474360560],
+            [-3.02526098819107, +2.53667889095925, +2.31664984740423],
+            [-2.00438948664892, -2.29235136977220, +2.19782807357059],
+            [+1.12226554109716, -1.36942007032045, +0.48455055461782],
+        ],
+    },
+    "mindless-02": {
+        "symbols": ["H", "S", "B", "O", "Mg", "H", "H", "H", "Si", "H", "B", "Li", "F", "H", "H", "S"],
+        "geometry": [
+            [-1.79537625851198, -3.77866422935275, -1.07883558363403],
+            [-2.68278833302782, +0.38892666265890, +1.66214865238427],
+            [+0.11484649791305, +1.48857933226955, +3.65660396510375],
+            [-1.07998879593946, -0.16259121615748, -4.55703065871422],
+            [+0.60302832999383, +4.08816149622342, -0.02589373148029],
+            [-1.22534089315880, -1.79981382478068, -3.70773173318592],
+            [-1.33460982049866, -4.24819082475503, +2.72791902701083],
+            [-0.16278082578516, +2.41267994179303, +5.69030695190570],
+            [+2.87802444057103, -0.33120525058830, +1.88311373530297],
+            [+0.68489327931487, +0.32790204044961, -4.20547693710673],
+            [-1.20919773588330, -2.87253762561437, +0.94064204223101],
+            [-3.25572604597922, +2.21241092990940, -2.86715549314771],
+            [-1.83147468262373, +5.20527293771933, -2.26976270603341],
+            [+4.90885865772880, -1.92576561961811, +2.99069919443735],
+            [+1.26806242248758, -2.60409341782411, +0.55162805282247],
+            [+4.11956976339902, +1.59892866766766, -1.39117477789609],
+        ],
+        "molecular_multiplicity": 2,
+    },
+    "mindless-03": {
+        "symbols": ["C", "O", "H", "Li", "Mg", "Al", "C", "H", "H", "H", "F", "S", "C", "H", "Na", "H"],
+        "geometry": [
+            [-0.02148551327524, -0.67161751504297, -4.75078512817560],
+            [+1.37792545875526, -3.24818416423144, +3.83896600631495],
+            [-2.23986953822894, +1.64550402751694, +3.42773272178522],
+            [-0.87622711432790, -2.74068400827752, +1.43723692979592],
+            [+1.29492470653815, +1.86470311043681, -1.04536500695239],
+            [-3.65768365013010, +0.45437052179208, -1.41566056087159],
+            [-0.23245910487384, -1.83274112101585, -2.43395808606122],
+            [+0.30373451850419, -3.84228931776777, -2.44882782867802],
+            [-3.36159503902161, +4.20056392581975, +1.63352684198071],
+            [+0.49372989648081, -1.56245253044952, -6.53610501083288],
+            [+4.38566058812996, +1.86127331114460, +0.56178822055152],
+            [-1.17545963764009, +2.49456345795141, -4.90195191215762],
+            [-1.86623614216854, +2.76329843590746, +1.71572598870213],
+            [+1.02361259176985, -4.24377370348987, +5.32418288889440],
+            [+4.71194535010347, -1.03648125005561, +3.35573062118779],
+            [-0.16051737061546, +3.89394681976155, +2.23776331451663],
+        ],
+    },
+    "mindless-04": {
+        "symbols": ["H", "B", "H", "F", "B", "H", "H", "Si", "H", "H", "C", "Al", "Si", "O", "H", "B"],
+        "geometry": [
+            [-1.34544890768411, +2.85946545334720, +3.11183388215396],
+            [-0.36293929605305, +4.15983774640545, +1.36413101934678],
+            [-3.36268280924844, +4.92951597114402, -3.59085684882314],
+            [+3.78143178536443, -4.97181356229699, +1.59003443639387],
+            [+3.44227417874042, -3.46504338606415, +3.62082644591507],
+            [+1.88917586252014, +3.42088101960529, +1.28872629783483],
+            [-0.32747529934233, -4.29711514977711, -3.55330460209973],
+            [-3.58768360829779, -1.39509759062952, -1.10396714572410],
+            [-0.39440896193088, +6.31837673143592, +1.99105318714945],
+            [+4.34376903295874, -4.12502353873667, +5.57829602371555],
+            [-1.39570266622309, -2.60410756418652, -4.03149806979915],
+            [+0.21788515354592, +0.28610741675369, +1.29731097788136],
+            [-2.00000183598828, +3.04473467156937, -2.00578147078785],
+            [+2.12833842504876, -1.30141517432227, +3.38069910888504],
+            [-2.48411958079522, -2.81581487156584, -5.76829803496286],
+            [-0.54241147261516, -0.04348817268188, -3.16920520707912],
+        ],
+    },
+    "mindless-05": {
+        "symbols": ["B", "P", "H", "H", "B", "P", "H", "Cl", "N", "H", "P", "Si", "H", "H", "P", "N"],
+        "geometry": [
+            [+0.68391902268453, +0.21679405065309, -2.81441127558071],
+            [-2.67199537993843, -3.97743927106200, +0.03497540139192],
+            [+2.02325266152397, -0.16048070975416, -0.41980608052722],
+            [+4.26224346168617, +3.65384961705338, -2.81836810458488],
+            [-2.80378310343644, +1.84796600006216, +0.15107304476153],
+            [+1.58317082705122, +3.77079801391042, -2.86230158107979],
+            [+2.63670178694113, +3.13142099211650, +2.24139937019049],
+            [-6.27112533979613, -3.92471014080274, +1.62562669834852],
+            [-0.92594349239390, -2.94451283088352, +2.60616476876177],
+            [-1.79532342290201, -1.56841672860834, +3.65515689388732],
+            [-3.01460634915379, -0.47748181717446, -2.44834110183776],
+            [+2.18249449208515, -2.23505035804805, +1.77725119258081],
+            [+3.26068149442689, -4.54078259646428, +0.57204329987377],
+            [+1.73744972267909, -1.18654391698320, -4.24063427353503],
+            [+0.94405328902426, +4.99525793054843, +1.18501287451328],
+            [-1.83118967048165, +3.39933176543682, +1.75515887283605],
+        ],
+        "molecular_multiplicity": 2,
+    },
+}
+
+
+
[docs]def get_molecule(name): + """ + Returns a QC JSON representation of a test molecule. + """ + if name not in _test_mols: + raise KeyError("Molecule name '{}' not found".format(name)) + + return Molecule(**copy.deepcopy(_test_mols[name]))
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/qcengine/util.html b/_modules/qcengine/util.html new file mode 100644 index 000000000..28e9e3793 --- /dev/null +++ b/_modules/qcengine/util.html @@ -0,0 +1,776 @@ + + + + + + qcengine.util — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for qcengine.util

+"""
+Several import utilities
+"""
+
+import io
+import json
+import os
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import traceback
+from contextlib import contextmanager
+from functools import partial
+from pathlib import Path
+from threading import Thread
+from typing import Any, BinaryIO, Dict, List, Optional, TextIO, Tuple, Union
+
+try:
+    from pydantic.v1 import BaseModel, ValidationError
+except ImportError:
+    from pydantic import BaseModel, ValidationError
+from qcelemental.models import AtomicResult, FailedOperation, OptimizationResult
+
+from qcengine.config import TaskConfig
+
+from .config import LOGGER, get_provenance_augments
+from .exceptions import InputError, QCEngineException
+
+__all__ = ["compute_wrapper", "model_wrapper", "handle_output_metadata", "create_mpi_invocation", "execute"]
+
+
+
[docs]def create_mpi_invocation(executable: str, task_config: TaskConfig) -> List[str]: + """Create the launch command for an MPI-parallel task + + Parameters + ---------- + executable: str + Path to executable + task_config: TaskConfig + Specification for number of nodes, cores per node, etc. + """ + + # Make the mpirun invocation + mpirun_str = task_config.mpiexec_command.format( + nnodes=task_config.nnodes, + ranks_per_node=task_config.ncores // task_config.cores_per_rank, + total_ranks=task_config.nnodes * task_config.ncores // task_config.cores_per_rank, + cores_per_rank=task_config.cores_per_rank, + ) + command = mpirun_str.split() + + # Add in the desired executable + command.append(executable) + return command
+ + +
[docs]def model_wrapper(input_data: Dict[str, Any], model: BaseModel) -> BaseModel: + """ + Wrap input data in the given model, or return a controlled error + """ + + if isinstance(input_data, dict): + try: + input_data = model(**input_data) + except ValidationError as exc: + raise InputError( + f"Error creating '{model.__name__}', data could not be correctly parsed:\n{str(exc)}" + ) from None + elif isinstance(input_data, model): + input_data = input_data.copy() + else: + raise InputError("Input type of {} not understood.".format(type(model))) + + # Older QCElemental compat + try: + input_data.extras + except AttributeError: + input_data = input_data.copy(update={"extras": {}}) + + return input_data
+ + +@contextmanager +def capture_stdout(): + oldout, olderr = sys.stdout, sys.stderr + try: + out = [io.StringIO(), io.StringIO()] + sys.stdout, sys.stderr = out + yield out + finally: + sys.stdout, sys.stderr = oldout, olderr + out[0] = out[0].getvalue() + out[1] = out[1].getvalue() + + +
[docs]@contextmanager +def compute_wrapper(capture_output: bool = True, raise_error: bool = False) -> Dict[str, Any]: + """Wraps compute for timing, output capturing, and raise protection""" + + metadata = {"stdout": None, "stderr": None, "success": True, "retries": 0} + + # Start timer + comp_time = time.time() + + # Capture stdout/err + new_stdout = io.StringIO() + new_stderr = io.StringIO() + if capture_output: + + old_stdout, sys.stdout = sys.stdout, new_stdout + old_stderr, sys.stderr = sys.stderr, new_stderr + + try: + yield metadata + + # Canonical QCEngine, do not show traceback - return error + except QCEngineException as exc: + if raise_error: + raise exc + + metadata["error_type"] = exc.error_type + metadata["error_message"] = exc.error_message + metadata["success"] = False + + # Unknown QCEngine exception likely in the Python layer, show traceback + except Exception as exc: + if raise_error: + raise exc + + metadata["error_type"] = "unknown_error" + metadata["error_message"] = "QCEngine Execution Error:\n" + traceback.format_exc() + metadata["success"] = False + + # Place data + metadata["wall_time"] = time.time() - comp_time + if capture_output: + sys.stdout = old_stdout + sys.stderr = old_stderr + # Pull over values + metadata["stdout"] = new_stdout.getvalue() or None + metadata["stderr"] = new_stderr.getvalue() or None
+ + +
[docs]def handle_output_metadata( + output_data: Union[Dict[str, Any], "AtomicResult", "OptimizationResult", "FailedOperation"], + metadata: Dict[str, Any], + raise_error: bool = False, + return_dict: bool = True, +) -> Union[Dict[str, Any], "AtomicResult", "OptimizationResult", "FailedOperation"]: + """ + Fuses general metadata and output together. + + Parameters: + output_data: The original output object to be fused with metadata + metadata: Metadata produced by the compute_wrapper context manager + raise_error: Raise an exception if errors exist (True) or return FailedOperation (False) + return_dict: Return dictionary or object representation of data + + Returns + ------- + result : AtomicResult, OptimizationResult, FailedOperation, or dict representation of any one. + Output type depends on return_dict or a dict if an error was generated in model construction + """ + + if isinstance(output_data, dict): + output_fusion = output_data # Error handling + else: + output_fusion = output_data.dict() + + # Do not override if computer generates + output_fusion["stdout"] = output_fusion.get("stdout", None) or metadata["stdout"] + output_fusion["stderr"] = output_fusion.get("stderr", None) or metadata["stderr"] + + if metadata["success"] is not True: + output_fusion["success"] = False + output_fusion["error"] = {"error_type": metadata["error_type"], "error_message": metadata["error_message"]} + + # Raise an error if one exists and a user requested a raise + if raise_error and (output_fusion["success"] is not True): + msg = "stdout:\n{}".format(output_fusion["stdout"]) + msg += "\nstderr:\n{}".format(output_fusion["stderr"]) + LOGGER.info(msg) + raise ValueError(output_fusion["error"]["error_message"]) + + # Fill out provenance datadata + provenance_augments = get_provenance_augments() + provenance_augments["wall_time"] = metadata["wall_time"] + if "provenance" in output_fusion: + output_fusion["provenance"].update(provenance_augments) + else: + # Add onto the augments with some missing info + provenance_augments["creator"] = "QCEngine" + provenance_augments["version"] = provenance_augments["qcengine_version"] + output_fusion["provenance"] = provenance_augments + + if metadata["retries"] != 0: + output_fusion["provenance"]["retries"] = metadata["retries"] + + # Make sure pydantic sparsity is upheld + for val in ["stdout", "stderr"]: + if output_fusion[val] is None: + output_fusion.pop(val) + + # We need to return the correct objects; e.g. Results, Procedures + if output_fusion["success"]: + # This will only execute if everything went well + ret = output_data.__class__(**output_fusion) + else: + # Should only be reachable on failures + ret = FailedOperation( + success=output_fusion.pop("success", False), error=output_fusion.pop("error"), input_data=output_fusion + ) + + if return_dict: + return json.loads(ret.json()) # Use Pydantic to serialize, then reconstruct as Python dict of Python Primals + else: + return ret
+ + +def terminate_process(proc: Any, timeout: int = 15) -> None: + if proc.poll() is None: + + # Sigint (keyboard interupt) + if sys.platform.startswith("win"): + proc.send_signal(signal.CTRL_BREAK_EVENT) + else: + proc.send_signal(signal.SIGINT) + + try: + start = time.time() + while (proc.poll() is None) and (time.time() < (start + timeout)): + time.sleep(0.02) + + # Flat kill + finally: + proc.kill() + + +@contextmanager +def popen( + args: List[str], + append_prefix: bool = False, + popen_kwargs: Optional[Dict[str, Any]] = None, + pass_output_forward: bool = False, +) -> Dict[str, Any]: + """ + Opens a background task + + Code and idea from dask.distributed's testing suite + https://github.com/dask/distributed + + Parameters + ---------- + args: List[str] + Input arguments for the command + append_prefix: bool + Whether to prepend the Python path prefix to the command being executed + popen_kwargs: Dict[str, Any] + Any keyword arguments to use when launching the process + pass_output_forward: bool + Whether to pass the stdout and stderr forward to the system's stdout and stderr + Returns + ------- + exe: dict + Dictionary with the following keys: + <ul> + <li>proc: Popen object describing the background task</li> + <li>stdout: String value of the standard output of the task</li> + <li>stdeer: String value of the standard error of the task</li> + </ul> + """ + args = list(args) + if popen_kwargs is None: + popen_kwargs = {} + else: + popen_kwargs = popen_kwargs.copy() + + # Bin prefix + if sys.platform.startswith("win"): + bin_prefix = os.path.join(sys.prefix, "Scripts") + else: + bin_prefix = os.path.join(sys.prefix, "bin") + + # Do we prefix with Python? + if append_prefix: + args[0] = os.path.join(bin_prefix, args[0]) + + if sys.platform.startswith("win"): + # Allow using CTRL_C_EVENT / CTRL_BREAK_EVENT + popen_kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP + + # Route the standard error and output + popen_kwargs["stdout"] = subprocess.PIPE + popen_kwargs["stderr"] = subprocess.PIPE + + # Prepare StringIO objects to store the stdout and stderr + stdout = io.BytesIO() + stderr = io.BytesIO() + + # Launch the process + LOGGER.info("Popen", args, popen_kwargs) + + # Ready the output + ret = {"proc": subprocess.Popen(args, **popen_kwargs)} + + # Spawn threads that will read from the stderr/stdout + # The PIPE uses a buffer with finite capacity. The underlying + # process will stall if it is unable to write to the buffer + # because the buffer is full. These threads continuously read + # from the buffers to ensure that they do not fill. + # + def read_from_buffer(buffer: BinaryIO, storage: io.BytesIO, sysio: TextIO): + for r in iter(partial(buffer.read, 1024), b""): + storage.write(r) + if pass_output_forward: + sysio.write(r.decode()) + + stdout_reader = Thread(target=read_from_buffer, args=(ret["proc"].stdout, stdout, sys.stdout)) + stdout_reader.start() + stderr_reader = Thread(target=read_from_buffer, args=(ret["proc"].stderr, stderr, sys.stderr)) + stderr_reader.start() + + # Yield control back to the main thread + try: + yield ret + except Exception: + raise + + finally: + # Executes on an exception or once the context manager closes + try: + terminate_process(ret["proc"]) + finally: + # Wait for the reader threads to finish + stdout_reader.join() + stderr_reader.join() + + # Retrieve the standard output for the process + ret["stdout"] = stdout.getvalue().decode() + ret["stderr"] = stderr.getvalue().decode() + + +@contextmanager +def environ_context(config: Optional["TaskConfig"] = None, env: Optional[Dict[str, str]] = None) -> Dict[str, str]: + """Temporarily set environment variables inside the context manager and + fully restore previous environment afterwards. + + Parameters + ---------- + config : Optional[TaskConfig], optional + Automatically sets MKL/OMP num threads based off the input config. + env : Optional[Dict[str, str]], optional + A dictionary of environment variables to update. + + Yields + ------ + Dict[str, str] + The updated environment variables. + """ + + temporary_env = {} + if config: + temporary_env["OMP_NUM_THREADS"] = str(config.ncores) + temporary_env["MKL_NUM_THREADS"] = str(config.ncores) + + if env: + temporary_env.update(env) + + original_env = {key: os.getenv(key) for key in temporary_env} + os.environ.update(temporary_env) + try: + yield temporary_env + finally: + for key, value in original_env.items(): + if value is None: + del os.environ[key] + else: + os.environ[key] = value + + +
[docs]def execute( + command: List[str], + infiles: Optional[Dict[str, str]] = None, + outfiles: Optional[List[str]] = None, + *, + as_binary: Optional[List[str]] = None, + scratch_name: Optional[str] = None, + scratch_directory: Optional[str] = None, + scratch_suffix: Optional[str] = None, + scratch_messy: bool = False, + scratch_exist_ok: bool = False, + blocking_files: Optional[List[str]] = None, + timeout: Optional[int] = None, + interupt_after: Optional[int] = None, + environment: Optional[Dict[str, str]] = None, + shell: Optional[bool] = False, + exit_code: Optional[int] = 0, +) -> Tuple[bool, Dict[str, Any]]: + """ + Runs a process in the background until complete. + + Returns True if exit code <= exit_code (default 0) + + Parameters + ---------- + command : list of str + infiles : Dict[str] = str + Input file names (names, not full paths) and contents. + to be written in scratch dir. May be {}. + outfiles : List[str] = None + Output file names to be collected after execution into + values. May be {}. + as_binary : List[str] = None + Keys of `infiles` or `outfiles` to be treated as bytes. + scratch_name : str, optional + Passed to temporary_directory + scratch_directory : str, optional + Passed to temporary_directory + scratch_suffix : str, optional + Passed to temporary_directory + scratch_messy : bool, optional + Passed to temporary_directory + scratch_exist_ok : bool, optional + Passed to temporary_directory + blocking_files : list, optional + Files which should stop execution if present beforehand. + timeout : int, optional + Stop the process after n seconds. + interupt_after : int, optional + Interupt the process (not hard kill) after n seconds. + environment : dict, optional + The environment to run in + shell : bool, optional + Run command through the shell. + exit_code: int, optional + The exit code above which the process is considered failure. + + Raises + ------ + FileExistsError + If any file in `blocking` is present + + Examples + -------- + # execute multiple commands in same dir + >>> success, dexe = qcng.util.execute(['command_1'], infiles, [], scratch_messy=True) + >>> success, dexe = qcng.util.execute(['command_2'], {}, outfiles, scratch_messy=False, scratch_name=Path(dexe['scratch_directory']).name, scratch_exist_ok=True) + + """ + + # Format inputs + if infiles is None: + infiles = {} + + if outfiles is None: + outfiles = [] + outfiles = {k: None for k in outfiles} + + # Check for blocking files + if blocking_files is not None: + for fl in blocking_files: + if os.path.isfile(fl): + raise FileExistsError("Existing file can interfere with execute operation.", fl) + + # Format popen + popen_kwargs = {} + if environment is not None: + popen_kwargs["env"] = {k: v for k, v in environment.items() if v is not None} + + # Execute + with temporary_directory( + child=scratch_name, + parent=scratch_directory, + messy=scratch_messy, + exist_ok=scratch_exist_ok, + suffix=scratch_suffix, + ) as scrdir: + popen_kwargs["cwd"] = scrdir + popen_kwargs["shell"] = shell + with disk_files(infiles, outfiles, cwd=scrdir, as_binary=as_binary) as extrafiles: + with popen(command, popen_kwargs=popen_kwargs) as proc: + # Wait for the subprocess to complete or the timeout to expire + if interupt_after is None: + proc["proc"].wait(timeout=timeout) + else: + time.sleep(interupt_after) + terminate_process(proc["proc"]) + retcode = proc["proc"].poll() + proc["outfiles"] = extrafiles + proc["scratch_directory"] = scrdir + + return retcode <= exit_code, proc
+ + +@contextmanager +def temporary_directory( + child: str = None, *, parent: str = None, suffix: str = None, messy: bool = False, exist_ok: bool = False +) -> str: + """Create and cleanup a quarantined working directory with a parent scratch directory. + + Parameters + ---------- + child : str, optional + By default, `None`, quarantine directory generated through + `tempfile.mdktemp` so guaranteed unique and safe. When specified, + quarantine directory has exactly `name`. + parent : str, optional + Create directory `child` elsewhere than TMP default. + For TMP default, see https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir + suffix : str, optional + Create `child` with identifying label by passing to ``tempfile.mkdtemp``. + Encouraged use for debugging only. + messy : bool, optional + Leave scratch directory and contents on disk after completion. + exist_ok : bool, optional + Run commands in a possibly pre-existing directory. + + Yields + ------ + str + Full path of scratch directory. + + Raises + ------ + FileExistsError + If `child` specified and directory already exists (perhaps from a + previous `messy=True` run). + + Examples + -------- + parent child suffix --> creates + ------ ----- ------ ------- + None None None --> /tmp/tmpliyp1i7x/ + None None _anharm --> /tmp/tmpliyp1i7x_anharm/ + None myqcjob None --> /tmp/myqcjob/ + /scratch/johndoe None None --> /scratch/johndoe/tmpliyp1i7x/ + /scratch/johndoe myqcjob None --> /scratch/johndoe/myqcjob/ + + """ + if child is None: + tmpdir = Path(tempfile.mkdtemp(dir=parent, suffix=suffix)) + else: + if parent is None: + parent = Path(tempfile.gettempdir()) + else: + parent = Path(parent) + tmpdir = parent / child + try: + os.mkdir(tmpdir) + except FileExistsError: + if exist_ok: + pass + else: + raise + try: + yield tmpdir + + finally: + if not messy: + shutil.rmtree(tmpdir) + LOGGER.info(f"... Removing {tmpdir}") + + +@contextmanager +def disk_files( + infiles: Dict[str, Union[str, bytes]], + outfiles: Dict[str, None], + *, + cwd: Optional[str] = None, + as_binary: Optional[List[str]] = None, +) -> Dict[str, Union[str, bytes]]: + """Write and collect files. + + Parameters + ---------- + infiles : Dict[str] = str + Input file names (names, not full paths) and contents. + to be written in scratch dir. May be {}. + outfiles : Dict[str] = None + Output file names to be collected after execution into + values. May be {}. + cwd : str, optional + Directory to which to write and read files. + as_binary : List[str] = None + Keys in `infiles` (`outfiles`) to be written (read) as bytes, not decoded. + + Yields + ------ + Dict[str] = str + outfiles with RHS filled in. + + """ + if cwd is None: + lwd = Path.cwd() + else: + lwd = Path(cwd) + if as_binary is None: + as_binary = [] + assert set(as_binary) <= (set(infiles) | set(outfiles)) + + try: + for fl, content in infiles.items(): + omode = "wb" if fl in as_binary else "w" + filename = lwd / fl + if filename.parent != lwd: + filename.parent.mkdir(parents=True, exist_ok=True) + with filename.open(omode) as fp: + fp.write(content) + LOGGER.info(f"... Writing ({omode}): {filename}") + + yield outfiles + + finally: + for fl in outfiles.keys(): + omode = "rb" if fl in as_binary else "r" + try: + filename = lwd / fl + with open(filename, omode) as fp: + outfiles[fl] = fp.read() + LOGGER.info(f"... Writing ({omode}): {filename}") + except (OSError, FileNotFoundError): + if "*" in fl: + gfls = {} + for gfl in lwd.glob(fl): + with open(gfl, omode) as fp: + gfls[gfl.name] = fp.read() + LOGGER.info(f"... Writing ({omode}): {gfl}") + if not gfls: + gfls = None + outfiles[fl] = gfls + else: + outfiles[fl] = None +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/source/api.rst b/_sources/api.rst.txt similarity index 100% rename from docs/source/api.rst rename to _sources/api.rst.txt diff --git a/_sources/api/qcengine.MDIServer.rst.txt b/_sources/api/qcengine.MDIServer.rst.txt new file mode 100644 index 000000000..9544a399d --- /dev/null +++ b/_sources/api/qcengine.MDIServer.rst.txt @@ -0,0 +1,53 @@ +MDIServer +========= + +.. currentmodule:: qcengine + +.. autoclass:: MDIServer + :show-inheritance: + + .. rubric:: Methods Summary + + .. autosummary:: + + ~MDIServer.recv_coords + ~MDIServer.recv_elements + ~MDIServer.recv_masses + ~MDIServer.recv_multiplicity + ~MDIServer.recv_natoms + ~MDIServer.recv_total_charge + ~MDIServer.run_energy + ~MDIServer.send_coords + ~MDIServer.send_elements + ~MDIServer.send_energy + ~MDIServer.send_forces + ~MDIServer.send_masses + ~MDIServer.send_multiplicity + ~MDIServer.send_natoms + ~MDIServer.send_node + ~MDIServer.send_total_charge + ~MDIServer.start + ~MDIServer.stop + ~MDIServer.update_molecule + + .. rubric:: Methods Documentation + + .. automethod:: recv_coords + .. automethod:: recv_elements + .. automethod:: recv_masses + .. automethod:: recv_multiplicity + .. automethod:: recv_natoms + .. automethod:: recv_total_charge + .. automethod:: run_energy + .. automethod:: send_coords + .. automethod:: send_elements + .. automethod:: send_energy + .. automethod:: send_forces + .. automethod:: send_masses + .. automethod:: send_multiplicity + .. automethod:: send_natoms + .. automethod:: send_node + .. automethod:: send_total_charge + .. automethod:: start + .. automethod:: stop + .. automethod:: update_molecule diff --git a/_sources/api/qcengine.compute.rst.txt b/_sources/api/qcengine.compute.rst.txt new file mode 100644 index 000000000..f26a6ef37 --- /dev/null +++ b/_sources/api/qcengine.compute.rst.txt @@ -0,0 +1,6 @@ +compute +======= + +.. currentmodule:: qcengine + +.. autofunction:: compute diff --git a/_sources/api/qcengine.compute_procedure.rst.txt b/_sources/api/qcengine.compute_procedure.rst.txt new file mode 100644 index 000000000..9ecd6a06e --- /dev/null +++ b/_sources/api/qcengine.compute_procedure.rst.txt @@ -0,0 +1,6 @@ +compute_procedure +================= + +.. currentmodule:: qcengine + +.. autofunction:: compute_procedure diff --git a/_sources/api/qcengine.config.NodeDescriptor.rst.txt b/_sources/api/qcengine.config.NodeDescriptor.rst.txt new file mode 100644 index 000000000..3d4cf15db --- /dev/null +++ b/_sources/api/qcengine.config.NodeDescriptor.rst.txt @@ -0,0 +1,6 @@ +NodeDescriptor +============== + +.. currentmodule:: qcengine.config + +.. autopydantic_model:: NodeDescriptor diff --git a/_sources/api/qcengine.config.get_config.rst.txt b/_sources/api/qcengine.config.get_config.rst.txt new file mode 100644 index 000000000..30d9107b0 --- /dev/null +++ b/_sources/api/qcengine.config.get_config.rst.txt @@ -0,0 +1,6 @@ +get_config +========== + +.. currentmodule:: qcengine.config + +.. autofunction:: get_config diff --git a/_sources/api/qcengine.config.get_provenance_augments.rst.txt b/_sources/api/qcengine.config.get_provenance_augments.rst.txt new file mode 100644 index 000000000..c93427bed --- /dev/null +++ b/_sources/api/qcengine.config.get_provenance_augments.rst.txt @@ -0,0 +1,6 @@ +get_provenance_augments +======================= + +.. currentmodule:: qcengine.config + +.. autofunction:: get_provenance_augments diff --git a/_sources/api/qcengine.config.global_repr.rst.txt b/_sources/api/qcengine.config.global_repr.rst.txt new file mode 100644 index 000000000..4065dbadb --- /dev/null +++ b/_sources/api/qcengine.config.global_repr.rst.txt @@ -0,0 +1,6 @@ +global_repr +=========== + +.. currentmodule:: qcengine.config + +.. autofunction:: global_repr diff --git a/_sources/api/qcengine.get_config.rst.txt b/_sources/api/qcengine.get_config.rst.txt new file mode 100644 index 000000000..3d21539d7 --- /dev/null +++ b/_sources/api/qcengine.get_config.rst.txt @@ -0,0 +1,6 @@ +get_config +========== + +.. currentmodule:: qcengine + +.. autofunction:: get_config diff --git a/_sources/api/qcengine.get_molecule.rst.txt b/_sources/api/qcengine.get_molecule.rst.txt new file mode 100644 index 000000000..500d44cd4 --- /dev/null +++ b/_sources/api/qcengine.get_molecule.rst.txt @@ -0,0 +1,6 @@ +get_molecule +============ + +.. currentmodule:: qcengine + +.. autofunction:: get_molecule diff --git a/_sources/api/qcengine.get_procedure.rst.txt b/_sources/api/qcengine.get_procedure.rst.txt new file mode 100644 index 000000000..a58f0f11c --- /dev/null +++ b/_sources/api/qcengine.get_procedure.rst.txt @@ -0,0 +1,6 @@ +get_procedure +============= + +.. currentmodule:: qcengine + +.. autofunction:: get_procedure diff --git a/_sources/api/qcengine.get_program.rst.txt b/_sources/api/qcengine.get_program.rst.txt new file mode 100644 index 000000000..f9596b0c6 --- /dev/null +++ b/_sources/api/qcengine.get_program.rst.txt @@ -0,0 +1,6 @@ +get_program +=========== + +.. currentmodule:: qcengine + +.. autofunction:: get_program diff --git a/_sources/api/qcengine.list_all_procedures.rst.txt b/_sources/api/qcengine.list_all_procedures.rst.txt new file mode 100644 index 000000000..e5eb28783 --- /dev/null +++ b/_sources/api/qcengine.list_all_procedures.rst.txt @@ -0,0 +1,6 @@ +list_all_procedures +=================== + +.. currentmodule:: qcengine + +.. autofunction:: list_all_procedures diff --git a/_sources/api/qcengine.list_all_programs.rst.txt b/_sources/api/qcengine.list_all_programs.rst.txt new file mode 100644 index 000000000..42610293e --- /dev/null +++ b/_sources/api/qcengine.list_all_programs.rst.txt @@ -0,0 +1,6 @@ +list_all_programs +================= + +.. currentmodule:: qcengine + +.. autofunction:: list_all_programs diff --git a/_sources/api/qcengine.list_available_procedures.rst.txt b/_sources/api/qcengine.list_available_procedures.rst.txt new file mode 100644 index 000000000..2605d384f --- /dev/null +++ b/_sources/api/qcengine.list_available_procedures.rst.txt @@ -0,0 +1,6 @@ +list_available_procedures +========================= + +.. currentmodule:: qcengine + +.. autofunction:: list_available_procedures diff --git a/_sources/api/qcengine.list_available_programs.rst.txt b/_sources/api/qcengine.list_available_programs.rst.txt new file mode 100644 index 000000000..dcd893b1d --- /dev/null +++ b/_sources/api/qcengine.list_available_programs.rst.txt @@ -0,0 +1,6 @@ +list_available_programs +======================= + +.. currentmodule:: qcengine + +.. autofunction:: list_available_programs diff --git a/_sources/api/qcengine.programs.ProgramHarness.rst.txt b/_sources/api/qcengine.programs.ProgramHarness.rst.txt new file mode 100644 index 000000000..919034de9 --- /dev/null +++ b/_sources/api/qcengine.programs.ProgramHarness.rst.txt @@ -0,0 +1,6 @@ +ProgramHarness +============== + +.. currentmodule:: qcengine.programs + +.. autopydantic_model:: ProgramHarness diff --git a/_sources/api/qcengine.programs.get_program.rst.txt b/_sources/api/qcengine.programs.get_program.rst.txt new file mode 100644 index 000000000..03ae54d15 --- /dev/null +++ b/_sources/api/qcengine.programs.get_program.rst.txt @@ -0,0 +1,6 @@ +get_program +=========== + +.. currentmodule:: qcengine.programs + +.. autofunction:: get_program diff --git a/_sources/api/qcengine.programs.list_all_programs.rst.txt b/_sources/api/qcengine.programs.list_all_programs.rst.txt new file mode 100644 index 000000000..eaa21cf64 --- /dev/null +++ b/_sources/api/qcengine.programs.list_all_programs.rst.txt @@ -0,0 +1,6 @@ +list_all_programs +================= + +.. currentmodule:: qcengine.programs + +.. autofunction:: list_all_programs diff --git a/_sources/api/qcengine.programs.list_available_programs.rst.txt b/_sources/api/qcengine.programs.list_available_programs.rst.txt new file mode 100644 index 000000000..5367b5e6e --- /dev/null +++ b/_sources/api/qcengine.programs.list_available_programs.rst.txt @@ -0,0 +1,6 @@ +list_available_programs +======================= + +.. currentmodule:: qcengine.programs + +.. autofunction:: list_available_programs diff --git a/_sources/api/qcengine.programs.register_program.rst.txt b/_sources/api/qcengine.programs.register_program.rst.txt new file mode 100644 index 000000000..6acd8dee0 --- /dev/null +++ b/_sources/api/qcengine.programs.register_program.rst.txt @@ -0,0 +1,6 @@ +register_program +================ + +.. currentmodule:: qcengine.programs + +.. autofunction:: register_program diff --git a/_sources/api/qcengine.programs.unregister_program.rst.txt b/_sources/api/qcengine.programs.unregister_program.rst.txt new file mode 100644 index 000000000..f6a9382a5 --- /dev/null +++ b/_sources/api/qcengine.programs.unregister_program.rst.txt @@ -0,0 +1,6 @@ +unregister_program +================== + +.. currentmodule:: qcengine.programs + +.. autofunction:: unregister_program diff --git a/_sources/api/qcengine.register_program.rst.txt b/_sources/api/qcengine.register_program.rst.txt new file mode 100644 index 000000000..dfff5541e --- /dev/null +++ b/_sources/api/qcengine.register_program.rst.txt @@ -0,0 +1,6 @@ +register_program +================ + +.. currentmodule:: qcengine + +.. autofunction:: register_program diff --git a/_sources/api/qcengine.unregister_program.rst.txt b/_sources/api/qcengine.unregister_program.rst.txt new file mode 100644 index 000000000..f1e950a9a --- /dev/null +++ b/_sources/api/qcengine.unregister_program.rst.txt @@ -0,0 +1,6 @@ +unregister_program +================== + +.. currentmodule:: qcengine + +.. autofunction:: unregister_program diff --git a/_sources/api/qcengine.util.compute_wrapper.rst.txt b/_sources/api/qcengine.util.compute_wrapper.rst.txt new file mode 100644 index 000000000..896520559 --- /dev/null +++ b/_sources/api/qcengine.util.compute_wrapper.rst.txt @@ -0,0 +1,6 @@ +compute_wrapper +=============== + +.. currentmodule:: qcengine.util + +.. autofunction:: compute_wrapper diff --git a/_sources/api/qcengine.util.create_mpi_invocation.rst.txt b/_sources/api/qcengine.util.create_mpi_invocation.rst.txt new file mode 100644 index 000000000..25267a896 --- /dev/null +++ b/_sources/api/qcengine.util.create_mpi_invocation.rst.txt @@ -0,0 +1,6 @@ +create_mpi_invocation +===================== + +.. currentmodule:: qcengine.util + +.. autofunction:: create_mpi_invocation diff --git a/_sources/api/qcengine.util.execute.rst.txt b/_sources/api/qcengine.util.execute.rst.txt new file mode 100644 index 000000000..c76ac79f5 --- /dev/null +++ b/_sources/api/qcengine.util.execute.rst.txt @@ -0,0 +1,6 @@ +execute +======= + +.. currentmodule:: qcengine.util + +.. autofunction:: execute diff --git a/_sources/api/qcengine.util.handle_output_metadata.rst.txt b/_sources/api/qcengine.util.handle_output_metadata.rst.txt new file mode 100644 index 000000000..18f06b42f --- /dev/null +++ b/_sources/api/qcengine.util.handle_output_metadata.rst.txt @@ -0,0 +1,6 @@ +handle_output_metadata +====================== + +.. currentmodule:: qcengine.util + +.. autofunction:: handle_output_metadata diff --git a/_sources/api/qcengine.util.model_wrapper.rst.txt b/_sources/api/qcengine.util.model_wrapper.rst.txt new file mode 100644 index 000000000..a3b3df984 --- /dev/null +++ b/_sources/api/qcengine.util.model_wrapper.rst.txt @@ -0,0 +1,6 @@ +model_wrapper +============= + +.. currentmodule:: qcengine.util + +.. autofunction:: model_wrapper diff --git a/docs/source/changelog.rst b/_sources/changelog.rst.txt similarity index 100% rename from docs/source/changelog.rst rename to _sources/changelog.rst.txt diff --git a/docs/source/cli.rst b/_sources/cli.rst.txt similarity index 100% rename from docs/source/cli.rst rename to _sources/cli.rst.txt diff --git a/docs/source/dev_program_harness.rst b/_sources/dev_program_harness.rst.txt similarity index 100% rename from docs/source/dev_program_harness.rst rename to _sources/dev_program_harness.rst.txt diff --git a/docs/source/environment.rst b/_sources/environment.rst.txt similarity index 100% rename from docs/source/environment.rst rename to _sources/environment.rst.txt diff --git a/docs/source/index.rst b/_sources/index.rst.txt similarity index 100% rename from docs/source/index.rst rename to _sources/index.rst.txt diff --git a/docs/source/install.rst b/_sources/install.rst.txt similarity index 100% rename from docs/source/install.rst rename to _sources/install.rst.txt diff --git a/docs/source/program_overview.rst b/_sources/program_overview.rst.txt similarity index 100% rename from docs/source/program_overview.rst rename to _sources/program_overview.rst.txt diff --git a/docs/source/programs_molecular_mechanics.rst b/_sources/programs_molecular_mechanics.rst.txt similarity index 100% rename from docs/source/programs_molecular_mechanics.rst rename to _sources/programs_molecular_mechanics.rst.txt diff --git a/docs/source/programs_semiempirical.rst b/_sources/programs_semiempirical.rst.txt similarity index 100% rename from docs/source/programs_semiempirical.rst rename to _sources/programs_semiempirical.rst.txt diff --git a/docs/source/single_compute.rst b/_sources/single_compute.rst.txt similarity index 100% rename from docs/source/single_compute.rst rename to _sources/single_compute.rst.txt diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 000000000..81415803e --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/autodoc_pydantic.css b/_static/autodoc_pydantic.css new file mode 100644 index 000000000..994a3e548 --- /dev/null +++ b/_static/autodoc_pydantic.css @@ -0,0 +1,11 @@ +.autodoc_pydantic_validator_arrow { + padding-left: 8px; + } + +.autodoc_pydantic_collapsable_json { + cursor: pointer; + } + +.autodoc_pydantic_collapsable_erd { + cursor: pointer; + } \ No newline at end of file diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 000000000..cfc60b86c --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,921 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 000000000..c718cee44 --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff b/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 000000000..6cb600001 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff2 b/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 000000000..7059e2314 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff b/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 000000000..f815f63f9 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff2 b/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 000000000..f2c76e5bd Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/_static/css/fonts/fontawesome-webfont.svg b/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/_static/css/fonts/fontawesome-webfont.woff b/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/_static/css/fonts/lato-bold-italic.woff b/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 000000000..88ad05b9f Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff differ diff --git a/_static/css/fonts/lato-bold-italic.woff2 b/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 000000000..c4e3d804b Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/_static/css/fonts/lato-bold.woff b/_static/css/fonts/lato-bold.woff new file mode 100644 index 000000000..c6dff51f0 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff differ diff --git a/_static/css/fonts/lato-bold.woff2 b/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 000000000..bb195043c Binary files /dev/null and b/_static/css/fonts/lato-bold.woff2 differ diff --git a/_static/css/fonts/lato-normal-italic.woff b/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 000000000..76114bc03 Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff differ diff --git a/_static/css/fonts/lato-normal-italic.woff2 b/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 000000000..3404f37e2 Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/_static/css/fonts/lato-normal.woff b/_static/css/fonts/lato-normal.woff new file mode 100644 index 000000000..ae1307ff5 Binary files /dev/null and b/_static/css/fonts/lato-normal.woff differ diff --git a/_static/css/fonts/lato-normal.woff2 b/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 000000000..3bf984332 Binary files /dev/null and b/_static/css/fonts/lato-normal.woff2 differ diff --git a/_static/css/theme.css b/_static/css/theme.css new file mode 100644 index 000000000..19a446a0e --- /dev/null +++ b/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 000000000..d06a71d75 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 000000000..40a45750e --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '0+untagged.1.g3b9ed2a', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/_static/file.png differ diff --git a/_static/graphviz.css b/_static/graphviz.css new file mode 100644 index 000000000..8d81c02ed --- /dev/null +++ b/_static/graphviz.css @@ -0,0 +1,19 @@ +/* + * graphviz.css + * ~~~~~~~~~~~~ + * + * Sphinx stylesheet -- graphviz extension. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/_static/jquery.js b/_static/jquery.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/html5shiv.min.js b/_static/js/html5shiv.min.js new file mode 100644 index 000000000..cd1c674f5 --- /dev/null +++ b/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/theme.js b/_static/js/theme.js new file mode 100644 index 000000000..1fddb6ee4 --- /dev/null +++ b/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 000000000..84ab3030a --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 000000000..97d56a74d --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,566 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = docUrlRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = docUrlRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 000000000..aae669d7e --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,144 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + parent.insertBefore( + span, + parent.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/api.html b/api.html new file mode 100644 index 000000000..2369d5cc9 --- /dev/null +++ b/api.html @@ -0,0 +1,386 @@ + + + + + + + QCEngine API — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

QCEngine API

+
+

qcengine Package

+

Base file for the dqm_compute module.

+
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

compute(input_data, program[, raise_error, ...])

Executes a single CMS program given a QCSchema input.

compute_procedure(input_data, procedure[, ...])

Runs a procedure (a collection of the quantum chemistry executions)

get_config(*[, hostname, task_config])

Returns the configuration key for qcengine.

get_molecule(name)

Returns a QC JSON representation of a test molecule.

get_procedure(name)

Returns a procedures executor class

get_program(name[, check])

Returns a program's executor class

list_all_procedures()

List all procedures registered by QCEngine.

list_all_programs()

List all programs registered by QCEngine.

list_available_procedures()

List all procedures that can be exectued (found) by QCEngine.

list_available_programs()

List all programs that can be exectued (found) by QCEngine.

register_program(entry_point)

Register a new ProgramHarness with QCEngine.

unregister_program(name)

Unregisters a given program.

+
+
+

Classes

+ + + + + + +

MDIServer(mdi_options, program, molecule, ...)

+
+
+

Class Inheritance Diagram

+
Inheritance diagram of qcengine.mdi_server.MDIServer
+ + +
+
+
+

qcengine.config Module

+

Creates globals for the qcengine module

+
+

Functions

+ + + + + + + + + + + + +

get_config(*[, hostname, task_config])

Returns the configuration key for qcengine.

get_provenance_augments()

+
rtype:
+

Dict[str, str]

+
+
+

global_repr()

A representation of the current global configuration.

+
+
+

Classes

+ + + + + + +

NodeDescriptor

Description of an individual node

+
+
+

Class Inheritance Diagram

+
Inheritance diagram of qcengine.config.NodeDescriptor
+ + +
+
+
+

qcengine.util Module

+

Several import utilities

+
+

Functions

+ + + + + + + + + + + + + + + + + + +

compute_wrapper([capture_output, raise_error])

Wraps compute for timing, output capturing, and raise protection

model_wrapper(input_data, model)

Wrap input data in the given model, or return a controlled error

handle_output_metadata(output_data, metadata)

Fuses general metadata and output together.

create_mpi_invocation(executable, task_config)

Create the launch command for an MPI-parallel task

execute(command[, infiles, outfiles, ...])

Runs a process in the background until complete.

+
+
+
+

qcengine.programs Package

+
+

Functions

+ + + + + + + + + + + + + + + + + + +

get_program(name[, check])

Returns a program's executor class

list_all_programs()

List all programs registered by QCEngine.

list_available_programs()

List all programs that can be exectued (found) by QCEngine.

register_program(entry_point)

Register a new ProgramHarness with QCEngine.

unregister_program(name)

Unregisters a given program.

+
+
+

Classes

+ + + + + + +

ProgramHarness

+
+
+

Class Inheritance Diagram

+
Inheritance diagram of qcengine.programs.model.ProgramHarness
+ + +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.MDIServer.html b/api/qcengine.MDIServer.html new file mode 100644 index 000000000..51e2dd540 --- /dev/null +++ b/api/qcengine.MDIServer.html @@ -0,0 +1,501 @@ + + + + + + + MDIServer — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

MDIServer

+
+
+class qcengine.MDIServer(mdi_options, program, molecule, model, keywords, raise_error=False, local_options=None)[source]
+

Bases: object

+

Methods Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

recv_coords([coords])

Receive a set of nuclear coordinates through MDI and assign them to the atoms in the current molecule

recv_elements([elements])

Receive a set of atomic numbers through MDI and assign them to the atoms in the current molecule

recv_masses([masses])

Receive a set of nuclear masses through MDI and assign them to the atoms in the current molecule

recv_multiplicity([multiplicity])

Receive the electronic multiplicity through MDI

recv_natoms([natoms])

Receive the number of atoms in the system through MDI and create a new molecule with them

recv_total_charge([charge])

Receive the total system charge through MDI

run_energy()

+
rtype:
+

None

+
+
+

send_coords()

Send the nuclear coordinates through MDI

send_elements()

Send the atomic number of each nucleus through MDI

send_energy()

Send the total energy through MDI

send_forces()

Send the nuclear forces through MDI

send_masses()

Send the nuclear masses through MDI

send_multiplicity()

Send the electronic multiplicity through MDI

send_natoms()

Send the number of atoms through MDI

send_node()

Send the name of the current node through MDI

send_total_charge()

Send the total system charge through MDI

start()

Receive commands through MDI and respond to them as defined by the MDI Standard

stop()

Stop listening for MDI commands

update_molecule(key, value)

Update the molecule

+

Methods Documentation

+
+
Parameters:
+
    +
  • mdi_options (str) –

  • +
  • program (str) –

  • +
  • raise_error (bool) –

  • +
  • local_options (Dict[str, Any] | None) –

  • +
+
+
+
+
+recv_coords(coords=None)[source]
+

Receive a set of nuclear coordinates through MDI and assign them to the atoms in the current molecule

+
+
Parameters:
+

coords (np.ndarray, optional) – New nuclear coordinates. If None, receive through MDI.

+
+
Return type:
+

None

+
+
+
+ +
+
+recv_elements(elements=None)[source]
+

Receive a set of atomic numbers through MDI and assign them to the atoms in the current molecule

+
+
Parameters:
+

elements (list of int, optional) – New element numbers. If None, receive through MDI.

+
+
+
+ +
+
+recv_masses(masses=None)[source]
+

Receive a set of nuclear masses through MDI and assign them to the atoms in the current molecule

+
+
Parameters:
+

masses (list of float, optional) – New nuclear masses. If None, receive through MDI.

+
+
Return type:
+

None

+
+
+
+ +
+
+recv_multiplicity(multiplicity=None)[source]
+

Receive the electronic multiplicity through MDI

+
+
Parameters:
+

multiplicity (int, optional) – New multiplicity of the system. If None, receive through MDI.

+
+
Return type:
+

None

+
+
+
+ +
+
+recv_natoms(natoms=None)[source]
+

Receive the number of atoms in the system through MDI and create a new molecule with them

+
+
Parameters:
+

natoms (int, optional) – New number of atoms. If None, receive through MDI.

+
+
Return type:
+

None

+
+
+
+ +
+
+recv_total_charge(charge=None)[source]
+

Receive the total system charge through MDI

+
+
Parameters:
+

charge (float, optional) – New charge of the system. If None, receive through MDI.

+
+
Return type:
+

None

+
+
+
+ +
+
+run_energy()[source]
+
+
Return type:
+

None

+
+
+
+ +
+
+send_coords()[source]
+

Send the nuclear coordinates through MDI

+
+
Returns:
+

coords – Nuclear coordinates

+
+
Return type:
+

np.ndarray

+
+
+
+ +
+
+send_elements()[source]
+

Send the atomic number of each nucleus through MDI

+
+
Returns:
+

elements – Element of each atom

+
+
Return type:
+

list of int

+
+
+
+ +
+
+send_energy()[source]
+

Send the total energy through MDI

+
+
Returns:
+

energy – Energy of the system

+
+
Return type:
+

float

+
+
+
+ +
+
+send_forces()[source]
+

Send the nuclear forces through MDI

+
+
Returns:
+

forces – Forces on the nuclei

+
+
Return type:
+

np.ndarray

+
+
+
+ +
+
+send_masses()[source]
+

Send the nuclear masses through MDI

+
+
Returns:
+

masses – Atomic masses

+
+
Return type:
+

np.ndarray

+
+
+
+ +
+
+send_multiplicity()[source]
+

Send the electronic multiplicity through MDI

+
+
Returns:
+

multiplicity – Multiplicity of the system

+
+
Return type:
+

int

+
+
+
+ +
+
+send_natoms()[source]
+

Send the number of atoms through MDI

+
+
Returns:
+

natom – Number of atoms

+
+
Return type:
+

int

+
+
+
+ +
+
+send_node()[source]
+

Send the name of the current node through MDI

+
+
Returns:
+

node – Name of the current node

+
+
Return type:
+

str

+
+
+
+ +
+
+send_total_charge()[source]
+

Send the total system charge through MDI

+
+
Returns:
+

charge – Total charge of the system

+
+
Return type:
+

float

+
+
+
+ +
+
+start()[source]
+

Receive commands through MDI and respond to them as defined by the MDI Standard

+
+
Return type:
+

None

+
+
+
+ +
+
+stop()[source]
+

Stop listening for MDI commands

+
+
Return type:
+

None

+
+
+
+ +
+
+update_molecule(key, value)[source]
+

Update the molecule

+
+
Parameters:
+
    +
  • key (str) – Key of the molecular element to update

  • +
  • value – Update value

  • +
+
+
+
+ +
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.compute.html b/api/qcengine.compute.html new file mode 100644 index 000000000..21fa10706 --- /dev/null +++ b/api/qcengine.compute.html @@ -0,0 +1,205 @@ + + + + + + + compute — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

compute

+
+
+qcengine.compute(input_data, program, raise_error=False, task_config=None, local_options=None, return_dict=False)[source]
+

Executes a single CMS program given a QCSchema input.

+
+
The full specification can be found at:

http://molssi-qc-schema.readthedocs.io/en/latest/index.html#

+
+
+
+
Parameters:
+
    +
  • input_data (Union[Dict[str, Any], AtomicInput]) – A QCSchema input specification in dictionary or model from QCElemental.models

  • +
  • program (str) – The CMS program with which to execute the input.

  • +
  • raise_error (bool) – Determines if compute should raise an error or not.

  • +
  • retries (int, optional) – The number of random tries to retry for.

  • +
  • task_config (Optional[Dict[str, Any]]) – A dictionary of local configuration options corresponding to a TaskConfig object.

  • +
  • local_options (Optional[Dict[str, Any]]) – Deprecated parameter, renamed to task_config

  • +
  • return_dict (bool) – Returns a dict instead of qcelemental.models.AtomicResult

  • +
+
+
Returns:
+

AtomicResult, FailedOperation, or Dict representation of either object type +A QCSchema representation of the requested output, type depends on return_dict key.

+
+
Return type:
+

result

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.compute_procedure.html b/api/qcengine.compute_procedure.html new file mode 100644 index 000000000..c4d91a761 --- /dev/null +++ b/api/qcengine.compute_procedure.html @@ -0,0 +1,199 @@ + + + + + + + compute_procedure — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

compute_procedure

+
+
+qcengine.compute_procedure(input_data, procedure, raise_error=False, task_config=None, local_options=None, return_dict=False)[source]
+

Runs a procedure (a collection of the quantum chemistry executions)

+
+
Parameters:
+
    +
  • input_data (dict or qcelemental.models.OptimizationInput) – A JSON input specific to the procedure executed in dictionary or model from QCElemental.models

  • +
  • procedure ({"geometric", "berny"}) – The name of the procedure to run

  • +
  • raise_error (bool, option) – Determines if compute should raise an error or not.

  • +
  • task_config (Optional[Dict[str, str]]) – A dictionary of local configuration options corresponding to a TaskConfig object.

  • +
  • local_options (Optional[Dict[str, str]]) – Deprecated parameter, renamed to task_config

  • +
  • return_dict (bool, optional, default True) – Returns a dict instead of qcelemental.models.AtomicInput

  • +
+
+
Returns:
+

A QC Schema representation of the requested output, type depends on return_dict key.

+
+
Return type:
+

dict, OptimizationResult, FailedOperation

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.config.NodeDescriptor.html b/api/qcengine.config.NodeDescriptor.html new file mode 100644 index 000000000..5df9da280 --- /dev/null +++ b/api/qcengine.config.NodeDescriptor.html @@ -0,0 +1,319 @@ + + + + + + + NodeDescriptor — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

NodeDescriptor

+
+
+pydantic model qcengine.config.NodeDescriptor[source]
+

Description of an individual node

+

+Show JSON schema
{
+   "title": "NodeDescriptor",
+   "description": "Description of an individual node",
+   "type": "object",
+   "properties": {
+      "hostname_pattern": {
+         "title": "Hostname Pattern",
+         "type": "string"
+      },
+      "name": {
+         "title": "Name",
+         "type": "string"
+      },
+      "scratch_directory": {
+         "title": "Scratch Directory",
+         "type": "string"
+      },
+      "memory": {
+         "title": "Memory",
+         "type": "number"
+      },
+      "memory_safety_factor": {
+         "title": "Memory Safety Factor",
+         "default": 10,
+         "type": "integer"
+      },
+      "ncores": {
+         "title": "Ncores",
+         "description": "Number of cores accessible to each task on this node\n    \n    The default value, ``None``, will allow QCEngine to autodetect the number of cores.",
+         "type": "integer"
+      },
+      "jobs_per_node": {
+         "title": "Jobs Per Node",
+         "default": 1,
+         "type": "integer"
+      },
+      "retries": {
+         "title": "Retries",
+         "default": 0,
+         "type": "integer"
+      },
+      "is_batch_node": {
+         "title": "Is Batch Node",
+         "default": false,
+         "help": "Whether the node running QCEngine is a batch node\n    \n    Some clusters are configured such that tasks are launched from a special \"batch\" or \"MOM\" onto the compute nodes.\n    The compute nodes on such clusters often have a different CPU architecture than the batch nodes and \n    often are unable to launch MPI tasks, which has two implications:\n        1) QCEngine must make *all* calls to an executable via ``mpirun`` because the executables might not\n        be able to run on the batch node. \n        2) QCEngine must run on the batch node to be able to launch tasks on the more than one compute nodes  \n    \n    ``is_batch_node`` is used when creating the task configuration as a means of determining whether\n    ``mpiexec_command`` must always be used even for serial jobs (e.g., getting the version number)\n    ",
+         "type": "boolean"
+      },
+      "mpiexec_command": {
+         "title": "Mpiexec Command",
+         "description": "Invocation for launching node-parallel tasks with MPI\n        \n        The invocation need not specify the number of nodes, tasks, or cores per node.\n        Information about the task configuration will be added to the command by use of\n        Python's string formatting. The configuration will be supplied as the following variables:\n        \n            {nnodes} - Number of nodes\n            {ranks_per_node} - Number of MPI ranks per node\n            {cores_per_rank} - Number of cores to use for each MPI rank\n            {total_ranks} - Total number of MPI ranks\n            \n        As examples, the ``aprun`` command on Cray systems should be similar to \n        ``aprun -n {total_ranks} -N {ranks_per_node}`` and ``mpirun`` from OpenMPI should\n        be similar to ``mpirun -np {total_ranks} -N {ranks_per_node}``.\n        \n        Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can \n        use the {cores_per_rank} option to control the hybrid parallelism. \n        As an example, the Cray ``aprun`` command using this figure could be:\n        ``aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1``.\n        The appropriate number of ranks per node will be determined based on the number of\n        cores per node and the number of cores per rank.\n        ",
+         "type": "string"
+      }
+   },
+   "required": [
+      "hostname_pattern",
+      "name"
+   ],
+   "additionalProperties": false
+}
+
+
+

+
Fields:
+
+
+
+
+
+field hostname_pattern: str [Required]
+
+ +
+
+field is_batch_node: bool = False
+
+ +
+
+field jobs_per_node: int = 1
+
+ +
+
+field memory: Optional[float] = None
+
+ +
+
+field memory_safety_factor: int = 10
+
+ +
+
+field mpiexec_command: Optional[str] = None
+

Invocation for launching node-parallel tasks with MPI

+

The invocation need not specify the number of nodes, tasks, or cores per node. +Information about the task configuration will be added to the command by use of +Python’s string formatting. The configuration will be supplied as the following variables:

+
+

{nnodes} - Number of nodes +{ranks_per_node} - Number of MPI ranks per node +{cores_per_rank} - Number of cores to use for each MPI rank +{total_ranks} - Total number of MPI ranks

+
+

As examples, the aprun command on Cray systems should be similar to +aprun -n {total_ranks} -N {ranks_per_node} and mpirun from OpenMPI should +be similar to mpirun -np {total_ranks} -N {ranks_per_node}.

+

Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can +use the {cores_per_rank} option to control the hybrid parallelism. +As an example, the Cray aprun command using this figure could be: +aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1. +The appropriate number of ranks per node will be determined based on the number of +cores per node and the number of cores per rank.

+
+ +
+
+field name: str [Required]
+
+ +
+
+field ncores: Optional[int] = None
+

Number of cores accessible to each task on this node

+

The default value, None, will allow QCEngine to autodetect the number of cores.

+
+ +
+
+field retries: int = 0
+
+ +
+
+field scratch_directory: Optional[str] = None
+
+ +
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.config.get_config.html b/api/qcengine.config.get_config.html new file mode 100644 index 000000000..112253616 --- /dev/null +++ b/api/qcengine.config.get_config.html @@ -0,0 +1,183 @@ + + + + + + + get_config — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_config

+
+
+qcengine.config.get_config(*, hostname=None, task_config=None)[source]
+

Returns the configuration key for qcengine.

+
+
Return type:
+

TaskConfig

+
+
Parameters:
+
    +
  • hostname (str | None) –

  • +
  • task_config (Dict[str, Any] | None) –

  • +
+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.config.get_provenance_augments.html b/api/qcengine.config.get_provenance_augments.html new file mode 100644 index 000000000..8ece655e9 --- /dev/null +++ b/api/qcengine.config.get_provenance_augments.html @@ -0,0 +1,176 @@ + + + + + + + get_provenance_augments — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_provenance_augments

+
+
+qcengine.config.get_provenance_augments()[source]
+
+
Return type:
+

Dict[str, str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.config.global_repr.html b/api/qcengine.config.global_repr.html new file mode 100644 index 000000000..655734c9c --- /dev/null +++ b/api/qcengine.config.global_repr.html @@ -0,0 +1,177 @@ + + + + + + + global_repr — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

global_repr

+
+
+qcengine.config.global_repr()[source]
+

A representation of the current global configuration.

+
+
Return type:
+

str

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.get_config.html b/api/qcengine.get_config.html new file mode 100644 index 000000000..10bf48f6d --- /dev/null +++ b/api/qcengine.get_config.html @@ -0,0 +1,192 @@ + + + + + + + get_config — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_config

+
+
+qcengine.get_config(*, hostname=None, task_config=None)[source]
+

Returns the configuration key for qcengine.

+
+
Return type:
+

TaskConfig

+
+
Parameters:
+
    +
  • hostname (str | None) –

  • +
  • task_config (Dict[str, Any] | None) –

  • +
+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.get_molecule.html b/api/qcengine.get_molecule.html new file mode 100644 index 000000000..3e6f99e8b --- /dev/null +++ b/api/qcengine.get_molecule.html @@ -0,0 +1,181 @@ + + + + + + + get_molecule — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_molecule

+
+
+qcengine.get_molecule(name)[source]
+

Returns a QC JSON representation of a test molecule.

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.get_procedure.html b/api/qcengine.get_procedure.html new file mode 100644 index 000000000..81e11240c --- /dev/null +++ b/api/qcengine.get_procedure.html @@ -0,0 +1,189 @@ + + + + + + + get_procedure — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_procedure

+
+
+qcengine.get_procedure(name)[source]
+

Returns a procedures executor class

+
+
Return type:
+

ProcedureHarness

+
+
Parameters:
+

name (str) –

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.get_program.html b/api/qcengine.get_program.html new file mode 100644 index 000000000..2bdcf9971 --- /dev/null +++ b/api/qcengine.get_program.html @@ -0,0 +1,194 @@ + + + + + + + get_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_program

+
+
+qcengine.get_program(name, check=True)[source]
+

Returns a program’s executor class

+
+
Parameters:
+
    +
  • check (bool) – True Do raise error if program not found. False is handy for +the specialized case of calling non-execution methods (like parsing for testing) +on the returned Harness.

  • +
  • name (str) –

  • +
+
+
Return type:
+

ProgramHarness

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.list_all_procedures.html b/api/qcengine.list_all_procedures.html new file mode 100644 index 000000000..42513a3b8 --- /dev/null +++ b/api/qcengine.list_all_procedures.html @@ -0,0 +1,186 @@ + + + + + + + list_all_procedures — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_all_procedures

+
+
+qcengine.list_all_procedures()[source]
+

List all procedures registered by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.list_all_programs.html b/api/qcengine.list_all_programs.html new file mode 100644 index 000000000..e2fc44d17 --- /dev/null +++ b/api/qcengine.list_all_programs.html @@ -0,0 +1,186 @@ + + + + + + + list_all_programs — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_all_programs

+
+
+qcengine.list_all_programs()[source]
+

List all programs registered by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.list_available_procedures.html b/api/qcengine.list_available_procedures.html new file mode 100644 index 000000000..e57177975 --- /dev/null +++ b/api/qcengine.list_available_procedures.html @@ -0,0 +1,186 @@ + + + + + + + list_available_procedures — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_available_procedures

+
+
+qcengine.list_available_procedures()[source]
+

List all procedures that can be exectued (found) by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.list_available_programs.html b/api/qcengine.list_available_programs.html new file mode 100644 index 000000000..97ad3e88e --- /dev/null +++ b/api/qcengine.list_available_programs.html @@ -0,0 +1,186 @@ + + + + + + + list_available_programs — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_available_programs

+
+
+qcengine.list_available_programs()[source]
+

List all programs that can be exectued (found) by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.ProgramHarness.html b/api/qcengine.programs.ProgramHarness.html new file mode 100644 index 000000000..5c61c4a79 --- /dev/null +++ b/api/qcengine.programs.ProgramHarness.html @@ -0,0 +1,383 @@ + + + + + + + ProgramHarness — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ProgramHarness

+
+
+pydantic model qcengine.programs.ProgramHarness[source]
+

+Show JSON schema
{
+   "title": "ProgramHarness",
+   "type": "object",
+   "properties": {
+      "name": {
+         "title": "Name",
+         "type": "string"
+      },
+      "scratch": {
+         "title": "Scratch",
+         "type": "boolean"
+      },
+      "thread_safe": {
+         "title": "Thread Safe",
+         "type": "boolean"
+      },
+      "thread_parallel": {
+         "title": "Thread Parallel",
+         "type": "boolean"
+      },
+      "node_parallel": {
+         "title": "Node Parallel",
+         "type": "boolean"
+      },
+      "managed_memory": {
+         "title": "Managed Memory",
+         "type": "boolean"
+      },
+      "extras": {
+         "title": "Extras",
+         "type": "object"
+      }
+   },
+   "required": [
+      "name",
+      "scratch",
+      "thread_safe",
+      "thread_parallel",
+      "node_parallel",
+      "managed_memory"
+   ]
+}
+
+
+

+
Fields:
+
+
+
+
+
+field extras: Optional[Dict[str, Any]] = None
+
+ +
+
+field managed_memory: bool [Required]
+
+ +
+
+field name: str [Required]
+
+ +
+
+field node_parallel: bool [Required]
+
+ +
+
+field scratch: bool [Required]
+
+ +
+
+field thread_parallel: bool [Required]
+
+ +
+
+field thread_safe: bool [Required]
+
+ +
+
+build_input(input_model, config, template=None)[source]
+
+
Return type:
+

Dict[str, Any]

+
+
Parameters:
+
    +
  • input_model (AtomicInput) –

  • +
  • config (TaskConfig) –

  • +
  • template (str | None) –

  • +
+
+
+
+ +
+
+abstract compute(input_data, config)[source]
+

Top-level compute method to be implemented for every ProgramHarness +:rtype: Union[AtomicResult, FailedOperation]

+
+

Note

+
+
This method behave in any of the following ways:
    +
  1. Return AtomicResult upon successful completion of a calculation

  2. +
  3. +
    Return FailedOperation object if an operation was unsuccessful or raised an exception. This is most

    likely to occur if the underlying QC package has a QCSchema API that catches exceptions and +returns them as FailedOperation objects to end users.

    +
    +
    +
  4. +
  5. +
    Raise an exception if a computation failed. The raised exception will be handled by the

    qcng.compute() method and either raised or packaged as a FailedOperation object.

    +
    +
    +
  6. +
+
+
+
+
+
Parameters:
+
    +
  • input_data (AtomicInput) –

  • +
  • config (TaskConfig) –

  • +
+
+
Return type:
+

AtomicResult | FailedOperation

+
+
+
+ +
+
+execute(inputs, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None)[source]
+
+
Return type:
+

Tuple[bool, Dict[str, Any]]

+
+
Parameters:
+
    +
  • inputs (Dict[str, Any]) –

  • +
  • extra_outfiles (List[str] | None) –

  • +
  • extra_commands (List[str] | None) –

  • +
  • scratch_name (str | None) –

  • +
  • timeout (int | None) –

  • +
+
+
+
+ +
+
+abstract static found(raise_error=False)[source]
+

Checks if the program can be found.

+
+
Parameters:
+

raise_error (bool, optional) – If True, raises an error if the program cannot be found.

+
+
Returns:
+

Returns True if the program was found, False otherwise.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_version()[source]
+

Finds program, extracts version, returns normalized version string.

+
+
Returns:
+

Return a valid, safe python version string.

+
+
Return type:
+

str

+
+
+
+ +
+
+parse_output(outfiles, input_model)[source]
+
+
Return type:
+

AtomicResult

+
+
Parameters:
+
    +
  • outfiles (Dict[str, str]) –

  • +
  • input_model (AtomicInput) –

  • +
+
+
+
+ +
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.get_program.html b/api/qcengine.programs.get_program.html new file mode 100644 index 000000000..0a56df2e2 --- /dev/null +++ b/api/qcengine.programs.get_program.html @@ -0,0 +1,187 @@ + + + + + + + get_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

get_program

+
+
+qcengine.programs.get_program(name, check=True)[source]
+

Returns a program’s executor class

+
+
Parameters:
+
    +
  • check (bool) – True Do raise error if program not found. False is handy for +the specialized case of calling non-execution methods (like parsing for testing) +on the returned Harness.

  • +
  • name (str) –

  • +
+
+
Return type:
+

ProgramHarness

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.list_all_programs.html b/api/qcengine.programs.list_all_programs.html new file mode 100644 index 000000000..ae1fb8d08 --- /dev/null +++ b/api/qcengine.programs.list_all_programs.html @@ -0,0 +1,179 @@ + + + + + + + list_all_programs — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_all_programs

+
+
+qcengine.programs.list_all_programs()[source]
+

List all programs registered by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.list_available_programs.html b/api/qcengine.programs.list_available_programs.html new file mode 100644 index 000000000..ba0c7e4c8 --- /dev/null +++ b/api/qcengine.programs.list_available_programs.html @@ -0,0 +1,179 @@ + + + + + + + list_available_programs — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

list_available_programs

+
+
+qcengine.programs.list_available_programs()[source]
+

List all programs that can be exectued (found) by QCEngine.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.register_program.html b/api/qcengine.programs.register_program.html new file mode 100644 index 000000000..db7b834af --- /dev/null +++ b/api/qcengine.programs.register_program.html @@ -0,0 +1,182 @@ + + + + + + + register_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

register_program

+
+
+qcengine.programs.register_program(entry_point)[source]
+

Register a new ProgramHarness with QCEngine.

+
+
Return type:
+

None

+
+
Parameters:
+

entry_point (ProgramHarness) –

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.programs.unregister_program.html b/api/qcengine.programs.unregister_program.html new file mode 100644 index 000000000..0855b5922 --- /dev/null +++ b/api/qcengine.programs.unregister_program.html @@ -0,0 +1,182 @@ + + + + + + + unregister_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

unregister_program

+
+
+qcengine.programs.unregister_program(name)[source]
+

Unregisters a given program.

+
+
Return type:
+

None

+
+
Parameters:
+

name (str) –

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.register_program.html b/api/qcengine.register_program.html new file mode 100644 index 000000000..102fa4525 --- /dev/null +++ b/api/qcengine.register_program.html @@ -0,0 +1,189 @@ + + + + + + + register_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

register_program

+
+
+qcengine.register_program(entry_point)[source]
+

Register a new ProgramHarness with QCEngine.

+
+
Return type:
+

None

+
+
Parameters:
+

entry_point (ProgramHarness) –

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.unregister_program.html b/api/qcengine.unregister_program.html new file mode 100644 index 000000000..50d963284 --- /dev/null +++ b/api/qcengine.unregister_program.html @@ -0,0 +1,189 @@ + + + + + + + unregister_program — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

unregister_program

+
+
+qcengine.unregister_program(name)[source]
+

Unregisters a given program.

+
+
Return type:
+

None

+
+
Parameters:
+

name (str) –

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.util.compute_wrapper.html b/api/qcengine.util.compute_wrapper.html new file mode 100644 index 000000000..fe55e6dd1 --- /dev/null +++ b/api/qcengine.util.compute_wrapper.html @@ -0,0 +1,183 @@ + + + + + + + compute_wrapper — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

compute_wrapper

+
+
+qcengine.util.compute_wrapper(capture_output=True, raise_error=False)[source]
+

Wraps compute for timing, output capturing, and raise protection

+
+
Return type:
+

Dict[str, Any]

+
+
Parameters:
+
    +
  • capture_output (bool) –

  • +
  • raise_error (bool) –

  • +
+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.util.create_mpi_invocation.html b/api/qcengine.util.create_mpi_invocation.html new file mode 100644 index 000000000..469505e12 --- /dev/null +++ b/api/qcengine.util.create_mpi_invocation.html @@ -0,0 +1,183 @@ + + + + + + + create_mpi_invocation — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

create_mpi_invocation

+
+
+qcengine.util.create_mpi_invocation(executable, task_config)[source]
+

Create the launch command for an MPI-parallel task

+
+
Parameters:
+
    +
  • executable (str) – Path to executable

  • +
  • task_config (TaskConfig) – Specification for number of nodes, cores per node, etc.

  • +
+
+
Return type:
+

List[str]

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.util.execute.html b/api/qcengine.util.execute.html new file mode 100644 index 000000000..9ec65340c --- /dev/null +++ b/api/qcengine.util.execute.html @@ -0,0 +1,206 @@ + + + + + + + execute — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

execute

+
+
+qcengine.util.execute(command, infiles=None, outfiles=None, *, as_binary=None, scratch_name=None, scratch_directory=None, scratch_suffix=None, scratch_messy=False, scratch_exist_ok=False, blocking_files=None, timeout=None, interupt_after=None, environment=None, shell=False, exit_code=0)[source]
+

Runs a process in the background until complete.

+

Returns True if exit code <= exit_code (default 0)

+
+
Parameters:
+
    +
  • command (list of str) –

  • +
  • infiles (Dict[str] = str) – Input file names (names, not full paths) and contents. +to be written in scratch dir. May be {}.

  • +
  • outfiles (List[str] = None) – Output file names to be collected after execution into +values. May be {}.

  • +
  • as_binary (List[str] = None) – Keys of infiles or outfiles to be treated as bytes.

  • +
  • scratch_name (str, optional) – Passed to temporary_directory

  • +
  • scratch_directory (str, optional) – Passed to temporary_directory

  • +
  • scratch_suffix (str, optional) – Passed to temporary_directory

  • +
  • scratch_messy (bool, optional) – Passed to temporary_directory

  • +
  • scratch_exist_ok (bool, optional) – Passed to temporary_directory

  • +
  • blocking_files (list, optional) – Files which should stop execution if present beforehand.

  • +
  • timeout (int, optional) – Stop the process after n seconds.

  • +
  • interupt_after (int, optional) – Interupt the process (not hard kill) after n seconds.

  • +
  • environment (dict, optional) – The environment to run in

  • +
  • shell (bool, optional) – Run command through the shell.

  • +
  • exit_code (int, optional) – The exit code above which the process is considered failure.

  • +
+
+
Raises:
+

FileExistsError – If any file in blocking is present

+
+
Return type:
+

Tuple[bool, Dict[str, Any]]

+
+
+

Examples

+

# execute multiple commands in same dir +>>> success, dexe = qcng.util.execute([‘command_1’], infiles, [], scratch_messy=True) +>>> success, dexe = qcng.util.execute([‘command_2’], {}, outfiles, scratch_messy=False, scratch_name=Path(dexe[‘scratch_directory’]).name, scratch_exist_ok=True)

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.util.handle_output_metadata.html b/api/qcengine.util.handle_output_metadata.html new file mode 100644 index 000000000..92d9e177e --- /dev/null +++ b/api/qcengine.util.handle_output_metadata.html @@ -0,0 +1,188 @@ + + + + + + + handle_output_metadata — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

handle_output_metadata

+
+
+qcengine.util.handle_output_metadata(output_data, metadata, raise_error=False, return_dict=True)[source]
+

Fuses general metadata and output together.

+
+
Parameters:
+
    +
  • output_data (Union[Dict[str, Any], AtomicResult, OptimizationResult, FailedOperation]) – The original output object to be fused with metadata

  • +
  • metadata (Dict[str, Any]) – Metadata produced by the compute_wrapper context manager

  • +
  • raise_error (bool) – Raise an exception if errors exist (True) or return FailedOperation (False)

  • +
  • return_dict (bool) – Return dictionary or object representation of data

  • +
+
+
Returns:
+

result – Output type depends on return_dict or a dict if an error was generated in model construction

+
+
Return type:
+

AtomicResult, OptimizationResult, FailedOperation, or dict representation of any one.

+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/api/qcengine.util.model_wrapper.html b/api/qcengine.util.model_wrapper.html new file mode 100644 index 000000000..63e80f873 --- /dev/null +++ b/api/qcengine.util.model_wrapper.html @@ -0,0 +1,183 @@ + + + + + + + model_wrapper — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

model_wrapper

+
+
+qcengine.util.model_wrapper(input_data, model)[source]
+

Wrap input data in the given model, or return a controlled error

+
+
Return type:
+

BaseModel

+
+
Parameters:
+
    +
  • input_data (Dict[str, Any]) –

  • +
  • model (BaseModel) –

  • +
+
+
+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/changelog.html b/changelog.html new file mode 100644 index 000000000..318685315 --- /dev/null +++ b/changelog.html @@ -0,0 +1,1269 @@ + + + + + + + Changelog — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Changelog

+
+

v0.30.0 / 2024-06-25

+
+

New Features

+
    +
  • (GH#441) MACE - Added harness for MACE-OFF23 and local MACE models. @jthorton

  • +
  • (GH#443) AIMNET2 - Added harness for AIMNET2 NN ML models. @jthorton

  • +
+
+
+

Misc.

+
    +
  • (GH#445) CI - fix some test regex issues.

  • +
  • (GH#449) Maint - bump the QCElemental compatibility range.

  • +
+
+
+
+

v0.29.0 / 2023-10-31

+
+

Bug Fixes

+
    +
  • (GH#427) Config - Once again, expand environment variables (now more flexibly) and newly expand ~ +passed into TaskConfig. Particularly relevant for scratch setting. @yueyericardo

  • +
  • (GH#428) MDI - Ensure that molecule orientation remains fixed for MDI. @taylor-a-barnes

  • +
  • (GH#405, GH#415, GH#417) Config - change default jobs_per_node from 2 to more expected 1 +so a single job fills the node. Alter CPU count formula to return physical cores on Hyperthreading +machines, affecting default ncores. The net effect (both changes) for default cores running on +Hyperthreading machines is unchanged. Nevertheless, fixes some Windows problems. @cvsik, @loriab

  • +
  • (GH#433) Turbomole, Q-Chem - Use raw strings when needed to avoid py312 warnings. @loriab

  • +
  • (GH#435) GAMESS - Collect the correct MP2 module in parsing for newer versions (>2021,<=2023). @loriab

  • +
+
+
+

Misc.

+
    +
  • (GH#433) CI - Check py312 and some Windows lanes. @loriab

  • +
+
+
+
+

v0.28.1 / 2023-08-18

+
+

Bug Fixes

+
    +
  • (GH#426) Psi4 - fix get_version on Windows where whole path and command were getting passed to version parser. @loriab

  • +
+
+
+
+

v0.28.0 / 2023-08-15

+
+

Breaking Changes

+
+
+

New Features

+
    +
  • (GH#400) Config - task configuration can now be set via CLI (qcengine run -h for details) or +by environment variables beginning with QCENGINE_. @bennybp

  • +
  • (GH#393, GH#392) MCTC-GCP - Adds b973c and r2scan3c methods to the gcp (mctc only, not classic) harness. @hokru

  • +
  • (GH#393) DFTD4 - Allows ga, gc, wf parameters to be tweaked (needed for r2scan-3c). This feature requires dftd4 3.5.0. @hokru

  • +
+
+
+

Enhancements

+
    +
  • (GH#410, GH#408) TorsionDrive - silence warnings by using the task_config argument internally. @jthorton

  • +
  • (GH#409) Psi4 - improve no-valid-error message so classifies as a RandomError and is eligible for +restart. @jthorton

  • +
  • (GH#405) Turbomole - correctly enable OpenMP and environment passing. Pass SCF convergence and +maximum iterations to define. @cvsik

  • +
  • (GH#403, GH#402) PyBerny - fix optimizer to respect the task_config options. @q-posev

  • +
  • (GH#386) CI - turn on formerly LGTM now GitHub CodeQL analysis. @lgtm-migrator

  • +
  • (GH#388) MRChem - more detailed info about the parallel setup saved to output provenance. @robertodr

  • +
  • (GH#424) testing - update SVWN Hessian reference values from Psi4. @loriab

  • +
  • (GH#423, GH#377) NWChem - allow two answers for test test_atom_labels[nwchem] to accommodate SCF +solutions in different versions. @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#401) MDI - fix bug in the shape of the MDI forces structure. @taylor-a-barnes

  • +
  • (GH#399, GH#401) MPI - remove MPI setup for MDI. This eliminates a bug where interfering +MPI environment variables were getting set upon import qcengine when pymdi and mpi4py packages +were present. @awvwgk, @taylor-a-barnes

  • +
  • (GH#418, GH#389, GH#292) Psi4 - make Psi4 exe/module detection and version parsing more robust. @Flamefire, @coltonbh, @loriab

  • +
+
+
+

Misc.

+
    +
  • (GH#419) CI - remove disabled LGTM and update badges. @loriab

  • +
  • (GH#422) CI - turn on crontab CI running to better notice external trouble. @loriab

  • +
+
+
+
+

v0.27.0 / 2023-08-02

+
+

Bug Fixes

+
    +
  • (GH#414) Import pydantic.v1 from pydantic v2 so that QCEngine can work with any >=1.8.2 pydantic +until QCEngine is updated for v2. If using v2, use QCElemental >=v0.26.0 that has a similar change. +QCEngineRecords received similar treatment. @Lnaden, @loriab

  • +
  • (GH#414) Versioneer - update so works with Python 3.12.

  • +
  • +
    (GH#414) Maintenance
      +
    • Sphinx - fix build errors.

    • +
    • Lint - pin black to 2022 format.

    • +
    • GHA - switch to mamba solver. @loriab

    • +
    +
    +
    +
  • +
  • (GH#394) Entos/Qcore - updated model environments. @loriab

  • +
+
+
+
+

v0.26.0 / 2022-11-30

+
+

Breaking Changes

+
    +
  • (GH#385) Dispersion - the dispersion parameters resources file has been altered so that for D3 variants there’s a +2b set (e.g., d3bj2b) that is pure 2-body and doesn’t accept s9 (effectively fixed at 0.0) and a atm set (e.g., +d3zeroatm) that does accept s9 (by default 1.0 but user-variable). Previous D3 levels are aliased to 2b. Only +downstreams that call the dispersion resources directly should be affected, and retrofits are in place for the known +victim/instigator (Psi4). @loriab

  • +
+
+
+

New Features

+
+
+

Enhancements

+
    +
  • (GH#380) MRChem - added gradient and thus geometry optimizations support. @robertodr

  • +
  • (GH#385) dftd3 - the classic interface now accepts e.g., d3mbj2b as a level hint. @loriab

  • +
  • (GH#385) s-dftd3 - added keyword apply_qcengine_aliases that when True and level_hint present allows the +levels and aliases in the dispersion resources (e.g., d3, d3atm, d32b) to be given as level_hint. The +resource parameters are passed to s-dftd3 as param_tweaks. @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#383) yaml - uses safe loading. @mbanck, @loriab

  • +
  • (GH#385) dftd3 - the pairwise analysis requested through AtomicInput.keywords["pair_resolved"] = True and +returned in AtomicResult.extras["qcvars"]["2-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] was elementwise too +large by a factor of 2. It now matches the s-dftd3 harness and fulfills that the sum of the array equals the +2-body dispersion energy. @loriab

  • +
+
+
+
+

v0.25.0 / 2022-11-11

+
+

Breaking Changes

+
    +
  • (GH#376) GAMESS - slight breaking changes of (1) ROHF MP2 ZAPT quantities now stored in “ZAPT” variables, not “MP2” +variables; and (2) “HF TOTAL ENERGY” no longer stores DFT energy in DFT computation. @loriab

  • +
  • (GH#376) testing - reference quantities now indexed by “standard” or “semicanonical” orbitals since program defaults +differ (mostly in CCSD ROHF FC). Downstream projects using the stdsuite interface will need to add an extra argument to query +reference data. @loriab

  • +
+
+
+

New Features

+
+
+

Enhancements

+
    +
  • (GH#376) Cfour - added parsing for BCCD and BCCD(T) methods. @loriab

  • +
  • (GH#376) NWChem - B2PLYP double-hybrid can now be run and parsed. Added CC2 parsing. @loriab

  • +
  • (GH#376) testing - added parsing contracts for ZAPT2, CEPA(1), CEPA(3), ACPF, AQCC, BCCD, BCCD(T), CC2, CC3, and DH-DFT. Added conventional references for most. @loriab

  • +
  • (GH#378) OpenFF - Support OpenFF Toolkit v0.11+. @Yoshanuikabundi

  • +
+
+
+

Bug Fixes

+
+
+
+

v0.24.1 / 2022-08-16

+
+

Enhancements

+
    +
  • (GH#375) testing - in standard suite, add reference values for occ, a-ccsd(t), olccd grad, remp2, omp2, omp2.5, omp3, oremp2, density fitted ccsd, ccsd(t), a-ccsd(t). @loriab

  • +
+
+
+
+

v0.24.0 / 2022-07-08

+
+

Upcoming Breaking Changes

+
    +
  • (GH#372) QCSchema - changes are planned to schema layout and QCEngine API that will be outlined in an issue. These are not expected to involve detailed changes to the harnesses, and update helper functions will be provided. In preparation, QCEngine is pinned to a maximum compatible QCElemental v0.25.0 (current release). Projects using QCSchema through QCElemental are advised to pin to maximum v0.25.0 qcel and v0.24.0 to avert trouble, since this is our first experience with schema increments. @loriab

  • +
+
+
+

New Features

+
    +
  • (GH#343) DFT-D3 - added the SDFTD3Harness to handle DFT-D3 via a Python API. This has native QCSchema support and programmatic access to the parameter database. @awvwgk

  • +
  • (GH#353) TeraChem - added the TeraChemFrontEndHarness to handle file I/O in extension to the protocol buffer TeraChemPBSHarness interface. @coltonbh

  • +
+
+
+

Enhancements

+
    +
  • (GH#350) Rename the compute(..., local_options) argument to compute(..., task_config). Former still works and will for a while. @coltonbh

  • +
  • (GH#361) testing - in standard suite, add references for Hartree–Fock density-fitten Hessians. @loriab

  • +
  • (GH#362) docs - update setup with theme and fuller information on Pydantic models. @loriab

  • +
  • (GH#363) CFOUR - learned not to set DERIV_LEVEL when atomicinput.driver=properties. Helps properties like DBOC. @loriab

  • +
  • (GH#363) Allow directory structure in execute(..., infiles) argument, not just flat-level files. @loriab

  • +
  • (GH#364) CFOUR - learned to harvest gradients when ghost atoms involved. Any CFOUR job with ghost atoms involves a hack that may go amiss when Xenon atoms in target molecule. @loriab

  • +
  • (GH#364) NWChem - learned to handle keyword geometry__autosym to tighten or loosen automatic symmetry detection. @loriab

  • +
  • (GH#372) testing - 2022 OpenMopac now actively tested in GHA. Note fields and output slightly different since 2019 harness. @awvwgk, @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#301, GH#367) PyBerny - learned how to fail informatively when something goes wrong instead of assuming all is well and failing misleadingly while processing success. @coltonbh

  • +
  • (GH#333) NWChem - learned to skip writing the original atomicinput.molecule geometry to the input file only when both (1) the job is known to be part of a restart and (2) the job originates from the NWChem “driver” (that is, the optimizer). Previously, the geometry writing was skipped under (1) circumstances, so single-point e/g/h didn’t have a geometry to work from. @WardLT

  • +
  • (GH#349) Turbomole - learned to correctly parse Hessian files when molecule contains more than 33 atoms. @eljost

  • +
+
+
+

Misc.

+ +
+
+
+

v0.23.0 / 2022-03-10

+
+

Enhancements

+
    +
  • (GH#351) Torsiondrive procedure refactored to make it easier for users to implement a parallel version via subclassing and overwriting the _spawn_optimizations method. @jthorton

  • +
+
+
+
+

v0.22.0 / 2022-01-25

+
+

Bug Fixes

+
    +
  • (GH#338) Correctly export version to tarballs created by git-archive. @mbanck, @loriab

  • +
  • (GH#339) QCEngine now tolerant of cpuinfo failure to populate brand_raw, brand. @dotsdl, @loriab, @WardLT

  • +
+
+
+
+

v0.21.0 / 2021-11-22

+
+

Enhancements

+
    +
  • (GH#321) CFOUR, GAMESS, NWChem, Psi4, DFTD3, MP2D, gCP - learned to return certain native text +files under control of the native_files protocol. GAMESS users are strongly advised to at +least set protocols.native_files = "input" so that the job is reproducible. @loriab

  • +
  • (GH#325) Torsiondrive - learned to use multiple molecules as input to torsiondrives. @jthorton

  • +
  • (GH#327) TorchANI - learned to use GPUs if available. @kexul

  • +
  • (GH#330, GH#332) NWChem - learned to restart from existing scratch if QCEngine is killed. @WardLT

  • +
+
+
+
+

v0.20.1 / 2021-10-08

+
+

Bug Fixes

+ +
+
+
+

v0.20.0 / 2021-10-01

+
+

New Features

+
    +
  • (GH#305) TorsionDrive - new procedure to automate constrained optimizations along a geometry +grid. Akin to the longstanding QCFractal TorsionDrive service. @SimonBoothroyd

  • +
+
+
+

Enhancements

+
    +
  • (GH#307) NWChem - learns to automatically increase the number of iterations when SCF, CC, etc. +fails to converge. @WardLT

  • +
  • (GH#309) qcengine info learned to print the location of found CMS programs, and geometric, +OpenMM, and RDKit learned to return their versions. @loriab

  • +
  • (GH#311) CFOUR, GAMESS, NWChem harnesses learned to notice which internal module performs a calc +(e.g., tce/cc for NWChem) and to store it in AtomicResult.provenance.module. Psi4 already does +this. @loriab

  • +
  • (GH#312) CFOUR, GAMESS, NWChem harnesses learned to run and harvest several new methods in the +MP, CC, CI, DFT families. @loriab

  • +
  • (GH#316) Config - TaskConfig learned a new field scratch_messy to instruct a +qcng.compute() run to not clean up the scratch directory at the end. @loriab

  • +
  • (GH#316) GAMESS - harness learned to obey ncores and scratch_messy local_config options. When +ncores > 1, the memory option is partitioned into replicated and non after exetyp=check trials. @loriab

  • +
  • (GH#316) Psi4 - harness learned to obey scratch_messy and memory local_config options. Memory +was previously off by a little (GB vs GiB). @loriab

  • +
  • (GH#316) CFOUR - harness learned to obey scratch_messy and memory local_config options. Memory +was previously off by a little. @loriab

  • +
  • (GH#316) NWChem - harness learned to obey scratch_messy and memory local_config options. Memory +was previously very off for v7. @loriab

  • +
  • (GH#315) CFOUR, GAMESS, NWChem – learned to return in AtomicInput or program native orientation +depending on fix_com & fix_orientation= T or F. Psi4 already did this. Previously these three +always returned AtomicInput orientation. Note that when returning program native orientation, the +molecule is overwritten, so AtomicResult is not a superset of AtomicInput. @loriab

  • +
  • (GH#315) CFOUR, GAMESS, NWChem – learned to harvest gradients and Hessians. @loriab

  • +
  • (GH#317) Docs - start “new harness” docs, so contributors have a coarse roadmap. @loriab

  • +
  • (GH#318) Docs - documentation is now served from https://molssi.github.io/QCEngine/ and built +by https://github.com/MolSSI/QCEngine/blob/master/.github/workflows/CI.yml . +https://qcengine.readthedocs.io/en/latest/ will soon be retired. @loriab

  • +
  • (GH#320) CFOUR, NWChem – learned to run with ghost atoms, tentatively. @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#313, GH#319) OpenMM - accommocate both old and new simtk/openmm import patterns. @dotsdl

  • +
+
+
+
+

v0.19.0 / 2021-05-16

+
+

New Features

+
    +
  • (GH#290) MCTC-GCP - harness for new implementation of gCP, mctc-gcp, whose cmdline interface is drop-in replacement. @loriab

  • +
  • (GH#291) DFTD4 - new harness for standalone DFT-D4 executable. @awvwgk

  • +
  • (GH#289) TeraChem - new harness for TeraChem Protocol Buffer Server mode. @coltonbh

  • +
+
+
+

Enhancements

+
    +
  • (GH#288) GAMESS, Cfour, NWChem - add calcinfo harvesting, HF and MP2 gradient harvesting. @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#288) Avert running model.basis = BasisSet schema even though they validate. @loriab

  • +
  • (GH#294) NWChem - fixed bug where was retrieving only the first step in a geometry relaxation with line-search off. @WardLT

  • +
  • (GH#297) MDI - Update interface for v1.2. @loriab

  • +
+
+
+
+

v0.18.0 / 2021-02-16

+
+

New Features

+
    +
  • (GH#206) OptKing - new procedure harness for OptKing optimizer. @AlexHeide

  • +
  • (GH#269) MRChem - new multiresolution chemistry program harness. @robertodr

  • +
  • (GH#277) ADCC - new program harness for ADC-connect. (Requires Psi4 for SCF.) @maxscheurer

  • +
  • (GH#278) gCP - new program harness for geometric counterpoise. @hokru

  • +
  • (GH#280) Add framework to register identifying known outfile errors, modify input schema, and rerun. @WardLT

  • +
  • (GH#281) NWChem - new procedure harness to use NWChem’s DRIVER geometry optimizer with NWChem’s program harness gradients. @WardLT

  • +
  • (GH#282) DFTD3 - added D3m and D3m(bj) parameters for SAPT0/HF. Allow pairwise analysis to be returned. @jeffschriber

  • +
+
+
+

Enhancements

+
    +
  • (GH#274) Entos/Qcore - renamed harness and updated to new Python bindings. @dgasmith

  • +
  • (GH#283) OpenMM - transition harness from openforcefield packages on omnia channel to openff.toolkit packages on conda-forge channel. @SimonBoothroyd

  • +
  • (GH#286, GH#287) CI - moves from Travis-CI to GHA for open-source testing. @loriab

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#273) TeraChem - fixed bug of missing method field. @stvogt

  • +
+
+
+
+

v0.17.0 / 2020-10-02

+
+

New Features

+
    +
  • (GH#262) Add project authors information. @loriab

  • +
+
+
+

Enhancements

+
    +
  • (GH#264) Turbomole - add analytic and finite difference Hessians. @eljost

  • +
  • (GH#266) Psi4- error messages from Psi4Harness no longer swallowed by KeyError. @dotsdl

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#264) Turbomole - fix output properties handling. @eljost

  • +
  • (GH#265) xtb - ensure extra tags are preserved in XTB harness. @WardLT

  • +
  • (GH#270) TorchANI - now lazily loads models as requested for compute. @dotsdl

  • +
+
+
+
+

v0.16.0 / 2020-08-19

+
+

New Features

+
+
+

Enhancements

+
    +
  • (GH#241) NWChem - improved performance by turning on atoms_map=True, which does seem to be true. @WardLT

  • +
  • (GH#257) TorchANI - learned the ANI2x model and to work with v2. @farhadrgh

  • +
  • (GH#259) Added MP2.5 & MP3 energies and HF, MP2.5, MP3, LCCD gradients reference data to stdsuite. @loriab

  • +
  • (GH#261) Q-Chem - learned to return more informative Provenance, learned to work with v5.1. @loriab

  • +
  • (GH#263) NWChem - learned how to turn off automatic Z-Matrix coordinates with geometry__noautoz = True. @WardLT

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#261) Molpro - learned to error cleanly if version too old for XML parsing. @loriab

  • +
  • (GH#261) Q-Chem - learned to extract version from output file instead of qchem -h since command isn’t available +from a source install. @loriab

  • +
+
+
+
+

v0.15.0 / 2020-06-26

+
+

New Features

+
    +
  • (GH#232) PyBerny - new geometry optimizer procedure harness. @jhrmnn

  • +
  • (GH#238) Set up testing infrastructure, “stdsuite”, where method reference values and expected results names (e.g., +total energy and correlation energy from MP2) are stored here in QCEngine but may be used from anywhere (presently, +Psi4). Earlier MP2 and CCSD tests here converted to new scheme, removing test_standard_suite_mp2.py and ccsd.

  • +
  • (GH#249, GH#254) XTB - new harness for xtb-python that natively speaks QCSchema. @awvwgk

  • +
+
+
+

Enhancements

+
    +
  • (GH#230) NWChem - improved dipole, HOMO, LUMO harvesting.

  • +
  • (GH#233) qcng.util.execute learned argument exit_code above which to fail, rather than just != 0.

  • +
  • (GH#234) MDI - harness updated to support release verion v1.0.0 .

  • +
  • (GH#238) Cfour, GAMESS, NWChem – harnesses updated to collect available spin components for MP2 and CCSD. +Also updated to set appropriate qcel.models.AtomicProperties from collected QCVariables.

  • +
  • (GH#239) OpenMM - OpenMM harness now looks for cmiles information in the +molecule extras field when typing. Also we allow for the use of gaff +forcefields. @jthorton

  • +
  • (GH#243) NWChem - more useful stdout error return.

  • +
  • (GH#244) Added CCSD(T), LCCD, and LCCSD reference data to stdsuite. @loriab

  • +
  • (GH#246) TorchANI - harness does not support v2 releases.

  • +
  • (GH#251) DFTD3 - added D3(0) and D3(BJ) parameters for PBE0-DH functional.

  • +
+
+
+

Bug Fixes

+ +
+
+
+

v0.14.0 / 2020-02-06

+
+

New Features

+
    +
  • (GH#212) NWChem - Adds CI for the NWChem harness.

  • +
  • (GH#226) OpenMM - Moves the OpenMM harness to a canonical forcefield based method/basis language combination.

  • +
  • (GH#228) RDKit - Adds MMFF94 force field capabilities.

  • +
+
+
+

Enhancements

+
    +
  • (GH#201) Psi4 - psi4 --version collection to only grab the last line.

  • +
  • (GH#202) Entos - Adds wavefunction parsing.

  • +
  • (GH#203) NWChem - Parses DFT empirical dispersion energy.

  • +
  • (GH#204) NWChem - Allows custom DFT functionals to be run.

  • +
  • (GH#205) NWChem - Improved gradient output and added Hessian support for NWChem.

  • +
  • (GH#215) Psi4 - if Psi4 location can be found by either PATH or PYTHONPATH, harness sets up both subprocesses and API execution.

  • +
  • (GH#215) get_program shows the helpful “install this” messages from found() rather than just saying “cannot be found”.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#199) Fix typo breaking NWChem property parsing.

  • +
  • (GH#215) NWChem complains before a calculation if the necessary networkx package not available.

  • +
  • (GH#207) NWChem - Minor bug fixes for NWChem when more than core per MPI rank is used.

  • +
  • (GH#209) NWChem - Fixed missing extras tags in NWChem harness.

  • +
+
+
+
+

v0.13.0 / 2019-12-10

+
+

New Features

+
    +
  • (GH#151) Adds a OpenMM Harness for evaluation of SMIRNOFF force fields.

  • +
  • (GH#189) General MPI support and MPI CLI generator.

  • +
+
+
+

Enhancements

+
    +
  • (GH#175) Allows specifications for nnodes to begin MPI support.

  • +
  • (GH#177) NWChem - Parsing updates including Hessian abilities.

  • +
  • (GH#180) GAMESS - Output properties improvements.

  • +
  • (GH#181) NWChem - Output properties improvements.

  • +
  • (GH#183) Entos - Hessian and XTB support.

  • +
  • (GH#185) Entos - Improved subcommand support.

  • +
  • (GH#187) QChem - Support for raw log files without the binary file requirements and improved output properties support.

  • +
  • (GH#188) Automatic buffer reads to prevent deadlocking of process for very large outputs.

  • +
  • (GH#194) DFTD3 - Improved error message on failed evaluations.

  • +
  • (GH#195) Blackens the code base add GHA-based lint checks.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#179) QChem - fixes print issue when driver is of an incorrect value.

  • +
  • (GH#190) Psi4 - fixes issues for methods without basis sets such as HF-3c.

  • +
+
+
+
+

v0.12.0 / 2019-11-13

+
+

New Features

+
    +
  • (GH#159) Adds MolSSI Driver Interface support.

  • +
  • (GH#160) Adds Turbomole support.

  • +
  • (GH#164) Adds Q-Chem support.

  • +
+
+
+

Enhancements

+
    +
  • (GH#155) Support for Psi4 Wavefunctions using v1.4a2 or greater.

  • +
  • (GH#162) Adds test for geometry optimization with trajectory protocol truncation.

  • +
  • (GH#167) CFOUR and NWChem parsing improvements for CCSD(T) properties.

  • +
  • (GH#168) Standardizes on dispatch.out for the common output files.

  • +
  • (GH#170) Increases coverage and begins a common documentation page.

  • +
  • (GH#171) Add Molpro to the standard suite.

  • +
  • (GH#172) Models renamed according to https://github.com/MolSSI/QCElemental/issues/155, particularly ResultInput -> AtomicInput, Result -> AtomicResult, Optimization -> OptimizationResult.

  • +
+
+
+

Bug Fixes

+
+
+
+

v0.11.0 / 2019-10-01

+
+

New Features

+
    +
  • (GH#162) Adds a test to take advantage of Elemental’s Protocols. +Although this PR does not technically change anything in Engine, bumping the minor version here allows +upstream programs to note when this feature was available because the minimum version dependency on Elemental +has been bumped as well.

  • +
+
+
+

Enhancements

+
    +
  • (GH#143) Updates to Entos and Molpro to allow Entos to execute functions from the Molpro Harness. Also helps +the two drivers to conform to GH#86.

  • +
  • (GH#145, GH#148) Initial CLI tests have been added to help further ensure Engine is running proper.

  • +
  • (GH#149) The GAMESS Harness has been improved by adding testing.

  • +
  • (GH#150, GH#153) TorchANI has been improved by adding a Hessian driver to it and additional information +is returned in the extra field when energy is the driver. +This also bumped the minimum version of TorchANI Engine supports from 0.5 to 0.9.

  • +
  • (GH#154) Molpro’s harness has been improved to support callinfo_X properties, unrestricted HF and DFT +calculations, and the initial support for parsing local correlation calculations.

  • +
  • (GH#158) Entos’ output parsing has been improved to read the json dictionary produced by the program +directly. Also updates the input file generation.

  • +
  • (GH#161) Updates MOPAC to have more sensible quantum-chemistry like keywords by default.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#156) Fixed a compatibility bug in specific version of Intel-OpenMP by skipping version +2019.5-281.

  • +
  • (GH#161) Improved error handling in MOPAC if the execution was incorrect.

  • +
+
+
+
+

v0.10.0 / 2019-08-25

+
+

New Features

+
    +
  • (GH#132) Expands CLI for info, run, and run-procedure options.

  • +
  • (GH#137) A new CI pipeline through Azure has been developed which uses custom, private Docker images +to house non-public code which will enable us to test Engine through integrated CI on these codes securely.

  • +
  • (GH#140) GAMESS, CFOUR, NWChem preliminary implementations.

  • +
+
+
+

Enhancements

+
    +
  • (GH#138) Documentation on Azure triggers.

  • +
  • (GH#139) Overhauls install documentation and clearly defines dev install vs production installs.

  • +
+
+
+
+

v0.9.0 / 2019-08-14

+
+

New Features

+
    +
  • (GH#120) Engine now takes advantage of Elemental’s new Msgpack serialization option for Models. Serialization +defaults to msgpack when available (conda install msgpack-python [-c conda-forge]), falling back to JSON +otherwise. This results in substantial speedups for both serialization and deserialization actions and should be a +transparent replacement for users within Engine and Elemental themselves.

  • +
+
+
+

Enhancements

+
    +
  • (GH#112) The MolproHarness has been updated to handle DFT and CCSD(T) energies and gradients.

  • +
  • (GH#116) An environment context manager has been added to catch NumPy style parallelization with Python functions.

  • +
  • (GH#117) MOPAC and DFTD3 can now accept an extras field which can pass around additional +data, conforming to the rest of the Harnesses.

  • +
  • (GH#119) Small visual improvements to the docs have been made.

  • +
  • (GH#120) Lists inside models are now generally converted to numpy arrays for internal storage to maximize the +benefit of the new Msgpack feature from Elemental.

  • +
  • (GH#133) The GAMESS Harness now collects the CCSD as part of its output.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#127) Removed unused imports from the NWChem Harvester module.

  • +
  • (GH#129) Missing type hints from the MolproHarness have been added.

  • +
  • (GH#131) A code formatting redundancy in the GAMESS input file parser has been removed.

  • +
+
+
+
+

v0.8.2 / 2019-07-25

+
+

Bug Fixes

+
    +
  • (GH#114) Make compute and compute_procedure not have required kwargs while debugging +a Fractal serialization issue. This is intended to be a temporary change and likely reverted +in a later release

  • +
+
+
+
+

v0.8.1 / 2019-07-22

+
+

Enhancements

+
    +
  • (GH#110) Psi4’s auto-retry exception handlers now catch more classes of random errors

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#109) Geometric auto-retry settings now correctly propagate through the base code.

  • +
+
+
+
+

v0.8.0 / 2019-07-19

+
+

New Features

+
    +
  • (GH#95, GH#96, GH#97, and GH#98) The NWChem interface from QCDB has been added. +Thanks to @vivacebelles and @jygrace for this addition!

  • +
  • (GH#100) The MOPAC interface has now been added to QCEngine thanks help to from @godotalgorithm.

  • +
+
+
+

Enhancements

+
    +
  • (GH#94) The gradient and molecule parsed from a GAMESS calculation output file are now returned in parse_output

  • +
  • (GH#101) Enabled extra files in TeraChem scratch folder to be requested by users, collected after program +execution, and recorded in the Result object as extras.

  • +
  • (GH#103) Random errors can now be retried a finite, controllable number of times (current default is zero retries). +Geometry optimizations automatically set retries to 2. This only impacts errors which are categorized as +RandomError by QCEngine and all other errors are raised as normal.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#99) QCEngine now manages an explicit folder for each Psi4 job to write into and passes the scratch directory +via -s command line. This resolves a key mismatch which could cause an error.

  • +
  • (GH#102) DFTD3 errors are now correctly returned as a FailedOperation instead of a raw dict.

  • +
+
+
+
+

v0.7.1 / 2019-06-18

+
+

Bug Fixes

+
    +
  • (GH#92) Added an __init__.py file to the programs/tests directory so they are correctly bundled with the +package.

  • +
+
+
+
+

v0.7.0 / 2019-06-17

+
+

Breaking Changes

+
    +
  • (GH#85) The resource file programs.dftd3.dashparam.py has relocated and renamed to +programs.empirical_dispersion_resources.py.

  • +
  • (GH#89) Function util.execute forgot str argument scratch_location and learned scratch_directory in +the same role of existing directory within which temporary directories are created and cleaned up. Non-user-facing +function util.scratch_directory renamed to util.temporary_directory.

  • +
+
+
+

New Features

+
    +
  • (GH#60) WIP: QCEngine interface to GAMESS can run the program (after light editing of rungms) +and parse selected output (HF, CC, FCI) into QCSchema.

  • +
  • (GH#73) WIP: QCEngine interface to CFOUR can run the program and parse a variety of output into QCSchema.

  • +
  • (GH#59, GH#71, GH#75, GH#76, GH#78, GH#88) Molpro improvements: Molpro can be run by QCEngine; and +the input generator and output parser now supports CCSD energy and gradient calculations. Large thanks to +@sjrl for many of the improvements

  • +
  • (GH#69) Custom Exceptions have been added to QCEngine’s returns which will make parsing and +diagnosing them easier and more programmatic for codes which invoke QCEngine. Thanks to @dgasmith for implementation.

  • +
  • (GH#82) QCEngine interface to entos can create input files (dft energy and gradients), run the program, +and parse the output.

  • +
  • (GH#85) MP2D interface switched to upstream repo (https://github.com/Chandemonium/MP2D v1.1) and now produces +correct analytic gradients.

  • +
+
+
+

Enhancements

+
    +
  • (GH#62, GH#67, GH#83) A large block of TeraChem improvements thanks to @ffangliu contributions. +Changed the input parser to call qcelemental to_string method with bohr unit, improved output of parser to turn stdout +into Result, and modified how version is parsed.

  • +
  • (GH#63) QCEngine functions util.which, util.which_version, util.parse_version, and +util.safe_version removed after migrating to QCElemental.

  • +
  • (GH#65) Torchani can now handle the ANI1-x and ANI1-ccx models. Credit to @dgasmith for implementation

  • +
  • (GH#74) Removes caching and reduces pytorch overhead from Travis CI. Credit to @dgasmith for implementation

  • +
  • (GH#77) Rename ProgramExecutor to ProgramHarness and BaseProcedure to ProcedureHarness.

  • +
  • (GH#77) Function util.execute(..., outfiles=[]) learned to collect output files matching a globbed filename.

  • +
  • (GH#81) Function util.execute learned list argument as_binary to handle input or output +files as binary rather than string.

  • +
  • (GH#81) Function util.execute learned bool argument scratch_exist_ok to run in a preexisting directory. +This is handy for stringing together execute calls.

  • +
  • (GH#84) Function util.execute learned str argument scratch_suffix to identify temp dictionaries for debugging.

  • +
  • (GH#90) DFTD3 now supports preliminary parameters for zero and Becke-Johnson damping to use with SAPT0-D

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#80) Fix “psi4:qcvars” handling for older Psi4 versions.

  • +
+
+
+
+

v0.6.4 / 2019-03-21

+
+

Bug Fixes

+
    +
  • (GH#54) Psi4’s Engine implementation now checks its key words in a case insensitive way to give the same value +whether you called Psi4 or Engine to do the compute.

  • +
  • (GH#55) Fixed an error handling routine in Engine to match Psi4.

  • +
  • (GH#56) Complex inputs are now handled better through Psi4’s wrapper which caused Engine to hang while trying +to write to stdout.

  • +
+
+
+
+

v0.6.3 / 2019-03-15

+
+

New Features

+
    +
  • (GH#28) TeraChem is now a registered executor in Engine! Thanks to @ffangliu for implementing.

  • +
  • (GH#46) MP2D is now a registered executor in Engine! Thanks to @loriab for implementing.

  • +
+
+
+

Enhancements

+
    +
  • (GH#46) dftd3’s workings received an overhaul. The mol keyword has been replaced with dtype=2, +full Psi4 support is now provided, and an MP2D interface has been added.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#50 and GH#51) Executing Psi4 on a single node with multiprocessing is more stable because Psi4 temps are +moved to scratch directories. This behavior is now better documented with an example as well.

  • +
  • (GH#52) Psi4 calls are now executed through the subprocess module to prevent possible multiprocessing issues +and memory leak after thousands of runs. A trade off is this adds about 0.5 seconds to task start-up, but its safe. +A future Psi4 release will correct this issue and the change can be reverted.

  • +
+
+
+
+

v0.6.2 / 2019-03-07

+
+

Enhancements

+
    +
  • (GH#38 and GH#39) Documentation now pulls from the custom QC Archive Sphinx Theme, but can fall back to the standard +RTD theme. This allows all docs across QCA to appear consistent with each other.

  • +
  • (GH#43) Added a base model for all Procedure objects to derive from. This allows +procedures’ interactions with compute programs to be more unified. This PR also ensured +GeomeTRIC provides Provenance information.

  • +
+
+
+

Bug Fixes

+
    +
  • (GH#40) This PR improved numerous back-end and testing quality of life aspects. +Fixed setup.py to call pytest instead of unittest when running tests on install. +Some conda packages for Travis-CI are cached to reduce the download time of the larger computation codes. +Psi4 is now pinned to the 1.3 version to fix build-level pin of libint. +Conda-build recipe removed to avoid possible confusion for everyone who isn’t a Conda-Forge +recipe maintainer. Tests now rely exclusively on the conda env setups.

  • +
+
+
+
+

v0.6.1 / 2019-02-20

+
+

Bug Fixes

+
    +
  • (GH#37) Fixed an issue where RDKit methods were not case agnostic.

  • +
+
+
+
+

v0.6.0 / 2019-02-28

+
+

Breaking Changes

+
    +
  • (GH#36) breaking change Model objects are returned by default rather than a dictionary.

  • +
+
+
+

New Features

+
    +
  • (GH#18) Add the dftd3 program to available computers.

  • +
  • (GH#29) Adds preliminary support for the Molpro compute engine.

  • +
  • (GH#31) Moves all computation to ProgramExecutor to allow for a more flexible input generation, execution, output parsing interface.

  • +
  • (GH#32) Adds a general execute process which safely runs subprocess jobs.

  • +
+
+
+

Enhancements

+
    +
  • (GH#33) Moves the dftd3 executor to the new ProgramExecutor interface.

  • +
  • (GH#34) Updates models to the more strict QCElemental v0.3.0 model classes.

  • +
  • (GH#35) Updates CI to avoid pulling CUDA libraries for torchani.

  • +
  • (GH#36) First pass at documentation.

  • +
+
+
+
+

v0.5.2 / 2019-02-13

+
+

Enhancements

+
    +
  • (GH#24) Improves load times dramatically by delaying imports and cpuutils.

  • +
  • (GH#25) Code base linting.

  • +
  • (GH#30) Ensures Psi4 output is already returned and Pydantic v0.20+ changes.

  • +
+
+
+
+

v0.5.1 / 2019-01-29

+
+

Enhancements

+
    +
  • (GH#22) Compute results are now returned as a dict of Python Primals which have +been serialized-deserialized through Pydantic instead of returning un-processed Python objects +or json-compatible string.

  • +
+
+
+
+

v0.5.0 / 2019-01-28

+
+

New Features

+
    +
  • (GH#8) Adds the TorchANI program for ANI-1 like energies and potentials.

  • +
  • (GH#16) Adds QCElemental models based off QCSchema to QCEngine for both validation and object-based manipulation of input and output data.

  • +
+
+
+

Enhancements

+
    +
  • (GH#14) Migrates option to Pydantic objects for validation and creation.

  • +
  • (GH#14) Introduces NodeDescriptor (for individual node description) and JobConfig (individual job configuration) objects.

  • +
  • (GH#17) NodeDescriptor overhauled to work better with Parsl/Balsam/Dask/etc.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/cli.html b/cli.html new file mode 100644 index 000000000..1cb60ee9c --- /dev/null +++ b/cli.html @@ -0,0 +1,257 @@ + + + + + + + Command Line Interface — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Command Line Interface

+

QCEngine provides a command line interface with three commands:

+
    +
  • qcengine info displays information about the environment detected by QCEngine.

  • +
  • qcengine run runs a program.

  • +
  • qcengine run-procedure runs a procedure.

  • +
+
+

Info Command

+
+

Command Invocation

+
qcengine info <options>
+
+
+
+
+

Command Description

+

This command prints information about the QCEngine environment.

+
+
+

Arguments

+
+
category

The information categories to show. Choices include:

+
    +
  • version: Print version of QCEngine and QCElemental.

  • +
  • programs: Print detected and supported programs.

  • +
  • procedures: Print detected and supported procedures.

  • +
  • config: Print host, compute, and job configuration

  • +
  • all: Print all available information.

  • +
+

By default, all available information is printed.

+
+
+
+
+
+

Run Command

+
+

Command Invocation

+
qcengine run <program> <data>
+
+
+
+
+

Command Description

+

This command runs a program on a given task and outputs the result as a JSON blob.

+
+
+

Arguments

+
+
program

The program to run.

+
+
data

Data describing the task. One of:

+
    +
  • A JSON blob.

  • +
  • A file name.

  • +
  • ‘-’, indicating data will be read from STDIN.

  • +
+
+
+
+
+
+

Run-Procedure Command

+
+

Command Invocation

+
qcengine run-procedure <program> <data>
+
+
+
+
+

Command Description

+

This command runs a procedure on a given task and outputs the result as a JSON blob.

+
+
+

Arguments

+
+
procedure

The procedure to run.

+
+
data

Data describing the task. One of:

+
    +
  • A JSON blob.

  • +
  • A file name.

  • +
  • ‘-’, indicating data will be read from STDIN.

  • +
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/codemeta.json b/codemeta.json deleted file mode 100644 index 2ae280938..000000000 --- a/codemeta.json +++ /dev/null @@ -1,339 +0,0 @@ -{ - "@context": "https://doi.org/10.5063/schema/codemeta-2.0", - "@type": "SoftwareSourceCode", - "description": "QCEngine: computational molecular sciences program executor and IO standardizer (QCSchema)", - "name": "QCEngine", - "codeRepository": "https://github.com/MolSSI/QCEngine", - "issueTracker": "https://github.com/MolSSI/QCEngine/issues", - "license": "https://opensource.org/licenses/BSD-3-Clause", - "version": "0.15", - "author": [ - { - "@type": "Person", - "givenName": "Daniel", - "additionalName": "G. A.", - "familyName": "Smith", - "affiliation": "The Molecular Sciences Software Institute", - "@id": "https://orcid.org/0000-0001-8626-0900", - "identifier": "https://github.com/dgasmith" - }, - { - "@type": "Person", - "givenName": "Asem", - "familyName": "Alenaizan", - "affiliation": "Center for Computational Molecular Science and Technology, Georgia Institute of Technology", - "@id": "https://orcid.org/0000-0002-0871-664X", - "identifier": "https://github.com/alenaizan" - }, - { - "@type": "Person", - "givenName": "Doaa", - "familyName": "Altarawy", - "affiliation": "The Molecular Sciences Software Institute", - "@id": "https://orcid.org/0000-0002-7795-4422", - "identifier": "https://github.com/doaa-altarawy" - }, - { - "@type": "Person", - "givenName": "Taylor", - "additionalName": "A.", - "familyName": "Barnes", - "affiliation": "The Molecular Sciences Software Institute", - "@id": "https://orcid.org/0000-0001-9396-1094", - "identifier": "https://github.com/taylor-a-barnes" - }, - { - "@type": "Person", - "givenName": "Lori", - "additionalName": "A.", - "familyName": "Burns", - "affiliation": "Center for Computational Molecular Science and Technology, Georgia Institute of Technology", - "@id": "https://orcid.org/0000-0003-2852-5864", - "identifier": "https://github.com/loriab" - }, - { - "@type": "Person", - "givenName": "Nuwan", - "familyName": "De Silva", - "affiliation": "Department of Physical and Biological Sciences, Western New England University", - "@id": "https://orcid.org/0000-0002-1443-5764", - "identifier": "https://github.com/nuwandesilva" - }, - { - "@type": "Person", - "givenName": "David", - "additionalName": "L.", - "familyName": "Dotson", - "affiliation": "Open Force Field Initiative", - "@id": "https://orcid.org/0000-0001-5879-2942", - "identifier": "https://github.com/dotsdl" - }, - { - "@type": "Person", - "givenName": "Sebastian", - "familyName": "Ehlert", - "affiliation": "Mulliken Center for Theoretical Chemistry, Institut für Physikalische und Theoretische Chemie, Universität Bonn", - "@id": "https://orcid.org/0000-0001-7809-771X", - "identifier": "https://github.com/awvwgk" - }, - { - "@type": "Person", - "givenName": "Zachary", - "additionalName": "L.", - "familyName": "Glick", - "affiliation": "Center for Computational Molecular Science and Technology, Georgia Institute of Technology", - "@id": "https://orcid.org/0000-0003-0900-2849", - "identifier": "https://github.com/zachglick" - }, - { - "@type": "Person", - "givenName": "Mark", - "additionalName": "S.", - "familyName": "Gordon", - "affiliation": "Department of Chemistry, Iowa State University", - "@id": "https://orcid.org/0000-0003-2371-1318" - }, - { - "@type": "Person", - "givenName": "Alex", - "additionalName": "G.", - "familyName": "Heide", - "affiliation": "Center for Computational Quantum Chemistry, University of Georgia", - "@id": "https://orcid.org/0000-0002-9780-8123", - "identifier": "https://github.com/AlexHeide" - }, - { - "@type": "Person", - "givenName": "Jan", - "familyName": "Hermann", - "affiliation": "Free University of Berlin", - "@id": "https://orcid.org/0000-0002-2779-0749", - "identifier": "https://github.com/jhrmnn" - }, - { - "@type": "Person", - "givenName": "Josh", - "familyName": "Horton", - "affiliation": "Department of Chemistry, Lancaster University; Open Force Field Inititive", - "@id": "https://orcid.org/0000-0001-8694-7200", - "identifier": "https://github.com/jthorton" - }, - { - "@type": "Person", - "givenName": "Adrian", - "additionalName": "G.", - "familyName": "Hurtado", - "affiliation": "Institute for Advanced Computational Science, Stony Brook University", - "@id": "https://orcid.org/0000-0002-2460-8059", - "identifier": "https://github.com/ahurta92" - }, - { - "@type": "Person", - "givenName": "Rollin", - "additionalName": "A.", - "familyName": "King", - "affiliation": "Department of Chemistry, Bethel University", - "@id": "https://orcid.org/0000-0002-1173-4187", - "identifier": "https://github.com/psi-rking" - }, - { - "@type": "Person", - "givenName": "Jiyoung", - "familyName": "Lee", - "affiliation": "Department of Chemistry, Iowa State University", - "@id": "https://orcid.org/0000-0002-5216-2859", - "identifier": "https://github.com/jygrace2" - }, - { - "@type": "Person", - "givenName": "Sebastian", - "additionalName": "J. R.", - "familyName": "Lee", - "affiliation": "Division of Chemistry and Chemical Engineering, California Institute of Technology", - "@id": "https://orcid.org/0000-0001-7006-9378", - "identifier": "https://github.com/sjrl" - }, - { - "@type": "Person", - "givenName": "Fang", - "familyName": "Liu", - "affiliation": "Emory University", - "@id": "https://orcid.org/0000-0003-1322-4997", - "identifier": "https://github.com/ffangliu" - }, - { - "@type": "Person", - "givenName": "Annabelle", - "additionalName": "T.", - "familyName": "Lolinco", - "affiliation": "Department of Chemistry, Iowa State University", - "@id": "https://orcid.org/0000-0002-1686-778X", - "identifier": "https://github.com/vivacebelles" - }, - { - "@type": "Person", - "givenName": "Devin", - "additionalName": "A.", - "familyName": "Matthews", - "affiliation": "Department of Chemistry, Southern Methodist University", - "@id": "https://orcid.org/0000-0003-2795-5483", - "identifier": "https://github.com/devinamatthews" - }, - { - "@type": "Person", - "givenName": "Jonathon", - "additionalName": "P.", - "familyName": "Misiewicz", - "affiliation": "Center for Computational Quantum Chemistry, University of Georgia", - "@id": "https://orcid.org/0000-0002-6425-9551", - "identifier": "https://github.com/JonathonMisiewicz" - }, - { - "@type": "Person", - "givenName": "Levi", - "additionalName": "N.", - "familyName": "Naden", - "affiliation": "The Molecular Sciences Software Institute", - "@id": "https://orcid.org/0000-0002-3692-5027", - "identifier": "https://github.com/Lnaden" - }, - { - "@type": "Person", - "givenName": "Farhad", - "familyName": "Ramezanghorbani", - "affiliation": "University of Florida", - "@id": "http://orcid.org/0000-0002-7545-4416", - "identifier": "https://github.com/farhadrgh" - }, - { - "@type": "Person", - "givenName": "Jeffrey", - "additionalName": "B.", - "familyName": "Schriber", - "affiliation": "Center for Computational Molecular Science and Technology, Georgia Institute of Technology", - "@id": "https://orcid.org/0000-0001-6906-8191", - "identifier": "https://github.com/jeffschriber" - }, - { - "@type": "Person", - "givenName": "C.", - "additionalName": "David", - "familyName": "Sherrill", - "affiliation": "Center for Computational Molecular Science and Technology, Georgia Institute of Technology", - "@id": "https://orcid.org/0000-0002-5570-7666", - "identifier": "https://github.com/CDSherrill" - }, - { - "@type": "Person", - "givenName": "Andrew", - "additionalName": "C.", - "familyName": "Simmonett", - "affiliation": "National Institutes of Health -- National Heart, Lung and Blood Institute", - "@id": "https://orcid.org/0000-0002-5921-9272", - "identifier": "https://github.com/andysim" - }, - { - "@type": "Person", - "givenName": "Johannes", - "familyName": "Steinmetzer", - "affiliation": "Friedrich-Schiller-University Jena", - "@id": "https://orcid.org/0000-0003-2493-6893", - "identifier": "https://github.com/eljost" - }, - { - "@type": "Person", - "givenName": "Justin", - "additionalName": "M.", - "familyName": "Turney", - "affiliation": "Center for Computational Quantum Chemistry, University of Georgia", - "@id": "https://orcid.org/0000-0003-3659-0711", - "identifier": "https://github.com/jturney" - }, - { - "@type": "Person", - "givenName": "Jeffrey", - "additionalName": "R.", - "familyName": "Wagner", - "affiliation": "Open Force Field Initiative", - "@id": "https://orcid.org/0000-0001-6448-0873", - "identifier": "https://github.com/j-wags" - }, - { - "@type": "Person", - "givenName": "Logan", - "familyName": "Ward", - "affiliation": "Data Science and Learning Division, Argonne National Laboratory", - "@id": "https://orcid.org/0000-0002-1323-5939", - "identifier": "https://github.com/WardLT" - }, - { - "@type": "Person", - "givenName": "Matthew", - "familyName": "Welborn", - "affiliation": "Entos, Inc.", - "identifier": "https://github.com/mattwelborn" - }, - { - "@type": "Person", - "givenName": "Theresa", - "additionalName": "L.", - "familyName": "Windus", - "affiliation": "Department of Chemistry, Iowa State University", - "@id": "https://orcid.org/0000-0001-6065-3167", - "identifier": "https://github.com/twindus" - }, - { - "@type": "Person", - "givenName": "Roberto", - "familyName": "Di Remigio", - "affiliation": "Hylleraas Centre for Quantum Molecular Sciences, UiT -- The Arctic University of Norway", - "@id": "https://orcid.org/0000-0002-5452-9239", - "identifier": "https://github.com/robertodr" - }, - { - "@type": "Person", - "givenName": "Maximilian", - "familyName": "Scheurer", - "affiliation": "Interdisciplinary Center for Scientific Computing, Heidelberg University", - "@id": "https://orcid.org/0000-0003-0592-3464", - "identifier": "https://github.com/maxscheurer" - }, - { - "@type": "Person", - "givenName": "Michael", - "additionalName": "F.", - "familyName": "Herbst", - "affiliation": "Inria Paris and CERMICS, \u00C9cole des Ponts ParisTech", - "@id": "https://orcid.org/0000-0003-0378-7921", - "identifier": "https://github.com/mfherbst" - }, - { - "@type": "Person", - "givenName": "Holger", - "familyName": "Kruse", - "affiliation": "Institute of Biophysics of the CAS, v.v.i., Czech Republic", - "@id": "https://orcid.org/0000-0002-0560-1513", - "identifier": "https://github.com/hokru" - }, - { - "@type": "Person", - "givenName": "Colton", - "familyName": "Hicks", - "affiliation": "Stanford University", - "@id": "https://orcid.org/0000-0003-2834-318X", - "identifier": "https://github.com/coltonbh" - } - ], - "developmentStatus": "active", - "downloadUrl": "https://github.com/MolSSI/QCEngine", - "keywords": [ - "quantum-chemistry", - "computational-chemistry", - "chemistry", - "physics" - ], - "maintainer": "https://orcid.org/0000-0002-7961-7016", - "programmingLanguage": [ - "Python" - ] -} diff --git a/dev_program_harness.html b/dev_program_harness.html new file mode 100644 index 000000000..ae9c5b8d5 --- /dev/null +++ b/dev_program_harness.html @@ -0,0 +1,253 @@ + + + + + + + Adding a New Program Harness — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Adding a New Program Harness

+

Program harnesses are for community CMS codes that can independently +(or with a SCF bootstrap) compute single-point energies, derivatives, +or properties or components thereof (e.g., dispersion corrections).

+

A single CMS code generally has one program harness. However, if there +are drastically different ways of running a code (e.g., TeraChem text +input file and TeraChem PBS), separate harnesses may be created. Also, +if there are specialty capabilites not fitting into “single-point +energies …” (e.g., GAMESS makefp task), an additional procedure +harness may be created.

+

This guide is a coarse path through adding a new program harness.

+
    +
  1. Open up communication with the QCEngine maintainers. Post an issue +to GitHub and join the Slack channel (link off GH README) so you can +get advice.

  2. +
+
    +
  1. Copy a similar harness. Choose a program that your code behaves +roughly like (mostly consider parsed vs. API access) and copy that +harness, renaming it as your own and commenting out all but the +structure. Search for the (copied) harness name to register it.

  2. +
+
    +
  1. Fill in the _defaults section with program name and characteristics.

  2. +
+
    +
  1. Fill in the def found function using which and +which_import from QCElemental. See NWChem and OpenMM for examples +of handling additional dependencies.

  2. +
+
    +
  1. If your code’s version can be extracted short of parsing an output +file, fill in def get_version next. After this, > qcengine info +should show your code (provided it’s in path).

  2. +
+
    +
  1. To get a string output of QCSchema Molecule in your code’s format, +you may need to add a dtype at qcelemental/molparse/to_string.py.

  2. +
+
    +
  1. If your code’s of the common translate-QCSchema-to-input, run, +translate-output-to-QCSchema variety, next work on the def execute +function. This is fairly simple because it calls the powerful +qcengine.util.execute to handle scratch, timeout, file writing and +collection, etc. The harness function needs the names of input files +(hard-code a string for now), the execution command, and the names +of any scratch files to return for processing. Once ready, fill in +def get_version if not done above.

  2. +
+
    +
  1. Now fill in the short def compute entirely and def build_input +and def parse_output skeletally. Set up a simple molecule-and-model +AtomicInput dictionary and run it with qcng.compute(atomicinput, +"yourcode") to get something to iterate on.

  2. +
+
    +
  1. Fill in def build_input to form your code’s usual input format +from the fields of AtomicInput.

  2. +
+
    +
  1. Fill in def parse_output to take results and put them +into AtomicResult. Most important is the return_result +field. AtomicResultProperties can be populated when +convenient. WavefunctionProperties is great but save for a later +pass.

  2. +
+
    +
  1. At this point your harness can correctly run one or more QCSchema +inputs of your devising. Time to put it through paces. Register your +code in _programs in testing.py. Most tests will then need a +@using("yourcode") decorator so that they don’t run (and fail the +test suite) when your code isn’t available.

  2. +
+
    +
  1. Add basic tests to qcengine/tests/test_harness_canonical.py +* def test_compute_energy(program, model, keywords): +* def test_compute_gradient(program, model, keywords): +* def test_compute_energy_qcsk_basis(program, model, keywords):

  2. +
+
    +
  1. Add basic failure tests to qcengine/tests/test_harness_canonical.py +* def test_compute_bad_models(program, model):

  2. +
+
    +
  1. Add tests for the runtime config and to qcengine/programs/tests/test_canonical_config.py

  2. +
+
    +
  1. For QM codes, consider adding lines to the +qcengine/programs/tests/test_standard_suite.py to check energies, +gradients, and Hessians against other codes.

  2. +
+
    +
  1. For codes that can produce a Hartree–Fock, add lines to the +qcengine/program/tests/test_alignment.py to check molecular and +properties orientation handling.

  2. +
+
    +
  1. If your code is available as a visible binary (e.g., pip, conda, +docker download with no or trivial build), create a testing lane by +adding to devtools/conda-envs/ and .github/workflows/CI.yml. +This will check your code for every PR. We’re looking into private +testing for codes that aren’t available.

  2. +
+
    +
  1. Throughout, talk with the maintainers with questions. Error handling, +especially, is intricate.

  2. +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2018-2024, The Molecular Sciences Software Institute.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/devtools/README.md b/devtools/README.md deleted file mode 100644 index 2eec3da18..000000000 --- a/devtools/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Development, testing, and deployment tools - -This directory contains a collection of tools for running Continuous Integration (CI) tests, -conda installation, and other development tools not directly related to the coding process. - - -## Manifest - -### Continuous Integration - -You should test your code, but do not feel compelled to use these specific programs. You also may not need Unix and -Windows testing if you only plan to deploy on specific platforms. These are just to help you get started - -* `travis-ci`: Linux and OSX based testing through [Travis-CI](https://about.travis-ci.com/) - * `install.sh`: Pip/Miniconda installation script for Travis -* `appveyor`: Windows based testing through [AppVeyor](https://www.appveyor.com/) - * `install_conda.ps1` Powershell installation script of Conda components - -## How to contribute changes -- Clone the repository if you have write access to the main repo, fork the repository if you are a collaborator. -- Make a new branch with `git checkout -b {your branch name}` -- Make changes and test your code -- Push the branch to the repo (either the main or your fork) with `git push -u origin {your branch name}` - * Note that `origin` is the default name assigned to the remote, yours may be different -- Make a PR on GitHub with your changes -- We'll review the changes and get your code into the repo after lively discussion! - - -## Checklist for updates -- [ ] Make sure there is an/are issue(s) opened for your specific update -- [ ] Create the PR, referencing the issue -- [ ] Debug the PR as needed until tests pass -- [ ] Tag the final, debugged version - * `git tag -a X.Y.Z [latest pushed commit] && git push --follow-tags` -- [ ] Get the PR merged in - -## Conda build comments -- `conda-build build devtools/conda-recipe --user molssi -c conda-forge --no-force-upload` - -## PyPI upload comments -Asumming your `.pypirc` file is setup [correctly](https://packaging.python.org/guides/migrating-to-pypi-org/#uploading). -- `python setup.py sdist upload` - - -## Versioneer Auto-version -[Versioneer](https://github.com/warner/python-versioneer) will automatically infer what version -is installed by looking at the `git` tags and how many commits ahead this version is. The format follows -[PEP 400](https://www.python.org/dev/peps/pep-0440/) and has the regular expression of: -```regexp -\d+.\d+.\d+(?\+\d+-[a-z0-9]+) -``` -If the version of this commit is the same as a `git` tag, the installed version is the same as the tag, -e.g. `QCEngine-0.1.2`, otherwise it will be appended with `+X` where `X` is the number of commits -ahead from the last tag, and then `-YYYYYY` where the `Y`'s are replaced with the `git` commit hash. diff --git a/devtools/azure_ci_images.md b/devtools/azure_ci_images.md deleted file mode 100644 index 09b709ba3..000000000 --- a/devtools/azure_ci_images.md +++ /dev/null @@ -1,246 +0,0 @@ -# QCEngine Azure CI for Private Code - - -Engine is a wrapper for many quantum chemistry codes. However, not all of those codes are public, free, or shipped -in pre-packaged ways (e.g. conda). Testing such codes on public CI suites such as Travis, Circle, or Azure can be a -security issue as the codes have to be reachable by the CI system. One feature that the Engine developers offer is -a secure and private Docker repository hosted through -[Azure Container Registry (ACR)](https://azure.microsoft.com/en-us/services/container-registry/?cdn=disable). This -allows us to test these codes (with permission) through a mature CI suite that plugs directly into GitHub, without -exposing the code to the outside world. - -The Container Registry is paid for by MolSSI and only the QCArchive developers have access to it. - -## Security - -The Azure Container Registry (ACR) is private in that only a single developer has access control to the registry. Any -additional registry managers must be explicitly given access by the owner to take actions, and those users are -currently limited to a select few QCArchive developers. Although more control can be given, for now, only these -users have any permission to view, add, or pull images from the private Registry. These accounts are restricted -to single-user email addresses. The Docker images uploaded to this registry *do not* exist on DockerHub or DockerCloud; -they *only* live in the private Azure Container Registry. - -Only ACR authorized users are allowed to hook the Private Container Registry into the Azure -Pipelines account. This must be done from the administration panels of Azure DevOps, and even then can only be done -if said user is first invited (by an administrator) into the DevOps Pipeline Group. Within DevOps, accessing the -ACR can only be done if the following conditions are met: - -* The user with access to the ACR is considered an "Owner" by the ACR security, this permission can only be - authorized by the ACR administrator -* The user with access to the ACR is a member of the Azure DevOps Pipeline team which is wanting to queue up the build. -* The user with access to the ACR is an Administrator on the DevOps Pipeline team -* The user with access to the ACR is signed into DevOps and approves using their credentials for the Pipeline team - to access the ACR. -* The user with access to the ACR approves specific builds to have access to the ACR. - -Then, and only then, can a Pipeline build access images in the ACR. Further, no unauthorized user can see, manage, -or change these credentials. - -## CI Runtime Security (When the builds trigger) - -Engine is, itself, a public code base, even though the codes it wraps may be private. Similarly, the Azure YAML file -which has the build and test instructions exist inside the Engine code and could be edited by anyone. In order to -block malicious edits from running and exposing the proprietary code by triggering the CI, the Engine developers only -allow builds to trigger on code which has been reviewed and approved, and merged into the code base already. No -outside user can propose a change which will automatically trigger the CI and execute unapproved code. - -The trade off to this level of security is that benign PRs which would actually benefit from said testing cannot -trigger tests on the secure ACR images. The code will be tested on merge into master, and we are working on a better -manual trigger on request system to let us test PRs which the administrators deem safe. @QCArchiveBot may make an -appearance as well through a manual trigger phrase. - -## Authenticating a new users for ACR and Pipelines - -These are instructions for allowing a new user permission to use the private ACR instance paid for by MolSSI/VT. These -are a one time thing and if you cannot meet these criteria, skip down to section on "Adding a new code to the CI" and -coordinate with one of the approved users who can upload your custom Docker image (or help you make one) to the private -ACR. - -1. Have an [Azure Portal](https://azure.microsoft.com/en-us/account/) account through a Virginia Tech email address - (this is something which has to be authorized by VT so we can allow access to the ACR, this may be relaxed in the - future.) -2. Create an [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/?nav=min) account with the same email - address. These are different login nodes, even though they both run through Azure. This will be for access to the - Pipelines team. -3. Request access to the QCArchive DevOps team. This will require reaching out to one of the QCArchive developers. -4. Request access to the QCArchive ACR. This will require reaching out to one of the QCArchive developers, this is - also a smaller group. - -*If you only want to upload images to the ACR (i.e. the Pipeline is already connected to the ACR)* - -* Request that you be made a "Contributor" in the ACR administration. - -*If you want to manage account connections between the ACR and Pipelines* - -* Request that you be made an "Owner" in the ACR administration. -* Request that you be made an "Administrator" in the Pipelines team. - -## Connecting ACR to Pipelines - -See also (its missing details about the security setup) - -https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml - -*Note: This only has to be done once per ACR (not per image) and Pipelines Team (not per build).* - -### Pre-requisites - -* Do everything in the previous section -* Be made an "Owner" of ACR and an "Administrator" of the Pipelines team -* Be signed into the account which meets these criteria. - -### Linking the services - -1. Sign into the account which meets the pre-requisites through the DevOps login. -2. From the DevOps team, go to the group account, and then go to the "Project settings" blade. - * If you are not an "Administrator" of the Pipelines team, you will not see this button. -3. Go to the "Service Connections" menu. -4. Choose "+ New Service Connection" and then "Docker Registry" -5. Select the "Azure Container Registry" radio button. -6. Choose a "Connection Name" which is easy to recognize and human readable (arbitrary). - * Make note of this, you will use it when referencing images. -7. Select the "Azure Subscription" drop down, and choose the Azure Subscription which is linked to the ACR you want to - make available to this Pipeline group. - * This will auto-fill if there is only 1 connected Azure Subscription. If nothing appears, the account you are - signed in with is missing permissions and likely not associated with the Azure Subscription (e.g. you are using the - wrong account). -8. Select the "Azure Container Registry" drop down and follow the authentication before selecting which ACR (if there - are several) you want to get authorization to. -9. Click "OK" and this Pipeline will now have access to the ACR through this Service Connection. All authorization - will be handled by Azure DevOps invisibly and not exposed in the builds. - * If the user is not an "Owner" of the ACR, this will throw an authentication error before the window is closed. - -See also: - -https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-docreg - -## Creating and uploading a new image to the ACR - -### Pre-requisites - -* Docker -* Access to the private software you want to include for CI testing -* (For uploading) Be a "Contributor" (or otherwise Push access) to the ACR in question. See some of the "Authenticating - a new users for ACR and Pipelines" for more details. -* (For uploading) Install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) - -### Creating the Image - -1. Start a new [Dockerfile](https://docs.docker.com/engine/reference/builder/) with the following one line header: - ```Dockerfile - FROM condaforge/linux-anvil-comp7 - - ``` -2. Build the rest of your docker file as normal to include your code. -3. Use `docker build` but importantly add `-t qcarchive.azurecr.io/{IMAGE NAME}:{TAG}` to the build command. - * The `{IMAGE NAME}` and `{TAG}` are developer choices, but the repository location is exact. - -See also (for general ACR manipulation). Use the above instructions for making production CI images - -https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal - -### Uploading the image - -Note: There is likely better way to authenticate, but this work for now. - -See also: - -https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication - -1. Using the AZ CLI, sign into the QCArchive ACR with `az acr login --name QCArchive` - * This targets the repository name from the "Access keys" of the Azure Portal->ACR page inside the container - service. - * You will be prompted with a window to authenticate with your Azure Portal account. Use the one which - has access to the ACR. -2. You now have access to ACR to take `docker` based actions against it for 60 minutes by default. -3. Ensure the image you want to push (`docker image ls`) is correctly tagged such that its REPOSITORY location starts - with `qcarchive.azurecr.io` and then is given a formal name and TAG as well. -4. `docker push` your image, and it should be pushed to the private ACR. - -## Adding a new container to the CI Pipeline - -These instructions are for adding new codes to the CI suite. - -### Pre-requisites - -* ACR and the Pipeline are linked services and you know the `Connection Name` inside the DevOps Pipeline - (see "Linking the services"). -* The Docker image has been uploaded to the private ACR and you know the image name and tag you want to use - (see "Creating and uploading a new image to the ACR") -* You have access to edit the `.azure-pipelines.yml` file of the Engine repository. - -### Adding the Custom Image to the YAML Pipeline Container Resource - -Assuming you have met the prerequisites, you can request that the CI use the private docker images in its process by -specifying they [run as a container](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml) -inside the host agent itself. However, we first have to indicate that this container *can* be used by the builds. -In the `.azure-pipelines.yml` file, there is a header called `resources:` and a sub-header called `containers:` within -it. This sub-header defines all the containers we want to use and will look like this: - -```yaml -resources: - containers: - - container: 'qcengineci' # Arbitrary name for reference in pipelines - image: qcarchive.azurecr.io/qcengineci:latest # pointer to the Image in our Azure CR - endpoint: "QCArchive Azure Container Registry" # Name of the Service Connection the pipeline is configured with - - container: 'waffleiron' - image: qcarchive.azurecr.io/something_about_waffles:maple_syrup - endpoint: "QCArchive Azure Container Registry" # Name of the Service Connection the pipeline is configured with -``` - -In this file, you need to specify each custom image in the ACR you want to use as an entry in the list. So for this -example, we have 2 images, one which we call `qcengineci` and the other `waffleiron`. The names after the -`container:` directive are arbitrary, but used later, so keep those in mind. - -Next we have the `image:` directive which is the pointer to the image in the ACR of the form -`qcarchive.azurecr.io/{IMAGE_NAME}:{TAG}`. Even if you did not not upload the image yourself, if you know the name, you -can request the CI fetch it. In these examples, there are two images we reference, both in the `qcarchive.azureacr.io` -registry, one image called `qcengineci` with the tag `latest`, and one image called `something_about_waffles` with the -tag `maple_syrup`. - -Finally, and most importantly, we have the `endpoint:` directive. This is the human-defined name when the ACR and the -Pipeline were linked through a "Service connection" in the `Connection Name` field. The specification of this field -instructs Azure Pipelines to authenticate through that named service when attempting to pull down the image, but only -in its own Pipeline, i.e. there is no way to reverse engineer that service hook outside of the Pipeline CI, its just a -string everywhere else. Without this field, Azure Pipeline will try to pull the repository in the `image` -field, but fail because it has nothing to authenticate against. No passwords/usernames are exposed through this since -its a string reference to an authentication which can only be seen, created, or edited by administrators though several -layers of permissions (see "Security"). - -Once the containers have been requisitioned, they can be used in jobs later. - -### Using a custom image in a CI job - -To use a previously allocated container (see "Adding the Custom Image to the YAML Pipeline Container Resource"), we -need to tell the worker Agent to run its job though it. For example: - -```yaml -jobs: -- job: proprietary_ci - displayName: 'CI with Proprietary Code' - pool: - vmImage: 'ubuntu-latest' - container: qcengineci -``` - -Everything about this should look like a normal Azure Pipelines job, but we have an additional `container` field which -matches the `container` filed from the `resources: {containers: [{container:...}]` field from above. This effectively -says that this job, and all actual `steps`, will be run inside this container. - -There are some caveats for this. We use Docker images which are derivatives of the Conda-Forge Anvil, which define an -`ENTRYPOINT` and a `CMD` directive in their `Dockerfile`. Unfortunately, -[Azure does things which bypasses those directives](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml#linux-based-containers), -so we have to add something in each of our `steps` which tells the container to provision, i.e. - -```yaml - steps: - - script: | - source /opt/conda/etc/profile.d/conda.sh - # The rest of the script... -``` - -### Running against multiple images - -One avenue for future improvements is having multiple images on the ACR we want to reference. For that, we can -[take advantage of the `strategy` directive](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml#multiple-jobs) -of Azure YAML files to auto-queue up multiple jobs. diff --git a/devtools/conda-envs/README.md b/devtools/conda-envs/README.md deleted file mode 100644 index 7000ac7db..000000000 --- a/devtools/conda-envs/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# QCEngine Pre-built Conda Environments - -base.yaml -nwchem.yaml -openmm.yaml -psi-nightly.yaml -psi.yaml -rdkit.yaml -README.md -torchani.yaml - -The QCEngine program has few requirements on its own `meta.yaml` file, however, -you may want to emulate the server side of things on your own. To help make that -possible, we have provided the various YAML files here which can be used -to quickly and mostly automatically build a working environment for to emulate -the server. - -These use the `conda env create` commands (examples below) instead of the -more common `conda create` (the commands are slightly different as of writing, -circa Conda 4.3), so note the difference in commands. - -* `base.yaml` is environment specification for general use. -* `psi-nightly.yaml` additionally installs the latest development Psi4 and anything it needs. -* `nwchem.yaml` doesn't install NWChem (no conda package), but it installs QCEngine and - NWChem dependencies. - -Similarly, other environments are tailored for other CMS harnesses. These -are in active use in CI and so should be reliable. - -## Requirements to use Environments - -1. `git` -2. `conda` -3. `conda` installed `pip` (pretty much always available unless you are in - some custom Python-less Conda environment such as an `R`-based env.) -4. Network access - -## Setup/Install - -Run the following command to configure a new environment with the replacements: - -* `{name}`: Replace with whatever you want to call the new env -* `{file}`: Replace with target file - -```bash -conda env create -n {name} -f {file} -``` - -To access the new environment: -```bash -conda activate {name} -``` - diff --git a/devtools/conda-envs/adcc.yaml b/devtools/conda-envs/adcc.yaml deleted file mode 100644 index 421a2afee..000000000 --- a/devtools/conda-envs/adcc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: test -channels: - #- adcc - - conda-forge - - nodefaults -dependencies: - - adcc>=0.15.7 - - psi4>=1.8.1 - - # Core - - python - - py-cpuinfo - - psutil - - qcelemental >=0.24.0 - - pydantic=1 - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov - diff --git a/devtools/conda-envs/aimnet2.yaml b/devtools/conda-envs/aimnet2.yaml deleted file mode 100644 index 511d7a0d1..000000000 --- a/devtools/conda-envs/aimnet2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - # Core - - python - - pip - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.12.0 - - pydantic>=1.0.0 - - # Testing - - pytest - - pytest-cov - - codecov - - pyaimnet2 diff --git a/devtools/conda-envs/base.yaml b/devtools/conda-envs/base.yaml deleted file mode 100644 index 2a74047f9..000000000 --- a/devtools/conda-envs/base.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.12.0 - - pydantic>=1.0.0 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/docs-cf.yaml b/devtools/conda-envs/docs-cf.yaml deleted file mode 100644 index 19b37fa32..000000000 --- a/devtools/conda-envs/docs-cf.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: docs -channels: - - conda-forge - - nodefaults -dependencies: - - python - - networkx - - pydantic=1 - - numpy - - pint - - # qc - - qcelemental - - # docs - - python-graphviz - - sphinx - - sphinx-autodoc-typehints - - sphinx-automodapi - - sphinx_rtd_theme - - autodoc-pydantic - - # testing - - pytest>=4.0.0 - - pytest-cov - - codecov - - #- pip: - # - git+https://github.com/MolSSI/qcarchive-sphinx-theme#egg=qcarchive_sphinx_theme diff --git a/devtools/conda-envs/mace.yaml b/devtools/conda-envs/mace.yaml deleted file mode 100644 index 7d7abb698..000000000 --- a/devtools/conda-envs/mace.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: mace -channels: - - conda-forge -dependencies: - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.12.0 - - pydantic>=1.0.0 - - # mace deps - - pymace - - numpy<2.0 # until conda package imposes - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/mrchem.yaml b/devtools/conda-envs/mrchem.yaml deleted file mode 100644 index d0d0b752b..000000000 --- a/devtools/conda-envs/mrchem.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - mrchem >=1.1=*openmpi* - - geometric - - optking - - # Core - - python - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental>=0.24 - - pydantic - - # Testing - - pytest - - pytest-cov - - codecov - - - pip - - pip: - - pyberny diff --git a/devtools/conda-envs/nwchem-cf.yaml b/devtools/conda-envs/nwchem-cf.yaml deleted file mode 100644 index 8f4683fcc..000000000 --- a/devtools/conda-envs/nwchem-cf.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - nwchem - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.24.0 - - pydantic>=1.0.0 - - networkx>=2.4.0 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/nwchem.yaml b/devtools/conda-envs/nwchem.yaml deleted file mode 100644 index e86623e79..000000000 --- a/devtools/conda-envs/nwchem.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.24.0 - - pydantic>=1.0.0 - - networkx>=2.4.0 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/openmm.yaml b/devtools/conda-envs/openmm.yaml deleted file mode 100644 index ddd49e3b4..000000000 --- a/devtools/conda-envs/openmm.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - rdkit - - openmm - - openff-toolkit - - openff-forcefields - - openmmforcefields - - geometric - - torsiondrive - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.11.1 - - pydantic >=1.8.2 - - pint <0.22 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/opt-disp-cf.yaml b/devtools/conda-envs/opt-disp-cf.yaml deleted file mode 100644 index 0a54cd77f..000000000 --- a/devtools/conda-envs/opt-disp-cf.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: test -channels: - - conda-forge - - nodefaults -dependencies: - - psi4=1.9.1 - - rdkit - - mopac - - # Mixed Tests - - dftd3-python - - dftd4-python - - gcp-correction - - geometric - - optking - - pymdi - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.26.0 - - pydantic=1.10.13 - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov - - - pip - - pip: - - pyberny diff --git a/devtools/conda-envs/opt-disp.yaml b/devtools/conda-envs/opt-disp.yaml deleted file mode 100644 index 15cb53fe3..000000000 --- a/devtools/conda-envs/opt-disp.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: test -channels: - - conda-forge - - defaults - - psi4/label/dev # for old dftd3 and mp2d -dependencies: - - psi4=1.9.1 - - blas=*=mkl # not needed but an example of disuading solver from openblas and old psi - #- intel-openmp!=2019.5 - - rdkit - - mopac - - # Mixed Tests - - dftd3 3.2.1 - - dftd4-python=3.3.0 - - mp2d >=1.1 - - gcp - - geometric - - optking - - pymdi - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.26.0 - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov - - - pip - - pip: - - pyberny diff --git a/devtools/conda-envs/psi-cf.yaml b/devtools/conda-envs/psi-cf.yaml deleted file mode 100644 index c96032546..000000000 --- a/devtools/conda-envs/psi-cf.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: test -channels: - - conda-forge - - conda-forge/label/libint_dev -dependencies: - - psi4=1.8 - - numpy<2 - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/psi-nightly.yaml b/devtools/conda-envs/psi-nightly.yaml deleted file mode 100644 index 04d408993..000000000 --- a/devtools/conda-envs/psi-nightly.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: test -channels: - - psi4/label/dev - - conda-forge -dependencies: - - psi4=1.6 - - blas=*=mkl # not needed but an example of disuading solver from openblas and old psi - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.26.0 - - pydantic>=1.0.0 - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/psi.yaml b/devtools/conda-envs/psi.yaml deleted file mode 100644 index dcff46c1e..000000000 --- a/devtools/conda-envs/psi.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: test -channels: - - psi4 - - conda-forge -dependencies: - - psi4=1.5 - - libint2=*=hc9558a2_9 - - dftd3 - - geometric - - intel-openmp!=2019.5 - - pymdi - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental=0.24.0 - - pydantic=1.8.2 # test minimun stated version. - - msgpack-python - - # Testing - - pytest - - pytest-cov - - codecov - - - pip - - pip: - - pyberny diff --git a/devtools/conda-envs/qcore.yaml b/devtools/conda-envs/qcore.yaml deleted file mode 100644 index 8e848bc27..000000000 --- a/devtools/conda-envs/qcore.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: test -channels: - - entos - - conda-forge -dependencies: - - py-qcore - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.24 - - pydantic >=1.8.2 - - tbb<2021 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/rdkit.yaml b/devtools/conda-envs/rdkit.yaml deleted file mode 100644 index 1ed05aff9..000000000 --- a/devtools/conda-envs/rdkit.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: test -channels: - - conda-forge - - rdkit -dependencies: - - rdkit - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.12.0 - - pydantic>=1.0.0 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/conda-envs/torchani.yaml b/devtools/conda-envs/torchani.yaml deleted file mode 100644 index 40ba67d10..000000000 --- a/devtools/conda-envs/torchani.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: test -channels: - - conda-forge - - pytorch-nightly -dependencies: - - # Core - - python - - pip - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.12.0 - - pydantic>=1.0.0 - - - pytorch - - # Testing - - pytest - - pytest-cov - - codecov - - pip: - - torchani diff --git a/devtools/conda-envs/xtb.yaml b/devtools/conda-envs/xtb.yaml deleted file mode 100644 index fb710e50d..000000000 --- a/devtools/conda-envs/xtb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - xtb - - xtb-python - - # Core - - python - - pyyaml - - py-cpuinfo - - psutil - - qcelemental >=0.11.1 - - pydantic >=1.8.2 - - # Extras - - gcp-correction - - dftd3-python >=0.5.1 - - # Testing - - pytest - - pytest-cov - - codecov diff --git a/devtools/scripts/conda_env.py b/devtools/scripts/conda_env.py deleted file mode 100644 index 53332f0fe..000000000 --- a/devtools/scripts/conda_env.py +++ /dev/null @@ -1,31 +0,0 @@ -import argparse -import os -import shutil -import subprocess as sp - -# Args -parser = argparse.ArgumentParser(description='Creates a conda environment from file for a given Python version.') -parser.add_argument('-n', '--name', type=str, nargs=1, help='The name of the created Python environment') -parser.add_argument('-p', '--python', type=str, nargs=1, help='The version of the created Python environment') -parser.add_argument('conda_file', nargs='*', help='The file for the created Python environment') - -args = parser.parse_args() - -with open(args.conda_file[0], "r") as handle: - script = handle.read() - -tmp_file = "tmp_env.yaml" -script = script.replace("- python", "- python {}*".format(args.python[0])) - -with open(tmp_file, "w") as handle: - handle.write(script) - -conda_path = shutil.which("conda") - -print("CONDA ENV NAME {}".format(args.name[0])) -print("PYTHON VERSION {}".format(args.python[0])) -print("CONDA FILE NAME {}".format(args.conda_file[0])) -print("CONDA path {}".format(conda_path)) - -sp.call("{} env create -n {} -f {}".format(conda_path, args.name[0], tmp_file), shell=True) -os.unlink(tmp_file) diff --git a/devtools/travis-ci/before_install.sh b/devtools/travis-ci/before_install.sh deleted file mode 100755 index 86a4c2de2..000000000 --- a/devtools/travis-ci/before_install.sh +++ /dev/null @@ -1,51 +0,0 @@ -# Temporarily change directory to $HOME to install software -pushd . -cd "$HOME" - -# Install Miniconda -if [ "$TRAVIS_OS_NAME" == "osx" ]; then - # Make OSX md5 mimic md5sum from linux, alias does not work - md5sum () { - command md5 -r "$@" - } - MINICONDA=Miniconda3-latest-MacOSX-x86_64.sh -else - MINICONDA=Miniconda3-latest-Linux-x86_64.sh -fi -MINICONDA_HOME=$HOME/miniconda - -echo "-- Installing latest Miniconda" -if [ -d "$MINICONDA_HOME/bin" ]; then - echo "-- Miniconda latest version FOUND in cache" - - # Config settings are not saved in the cache - export PIP_ARGS="-U" - export PATH=$MINICONDA_HOME/bin:$PATH - - conda config --set always_yes yes --set changeps1 no -else - MINICONDA_MD5=$(wget -qO- https://repo.anaconda.com/miniconda/ | grep -A3 $MINICONDA | sed -n '4p' | sed -n 's/ *\(.*\)<\/td> */\1/p') - echo "-- Miniconda latest version NOT FOUND in cache" - wget -q https://repo.continuum.io/miniconda/$MINICONDA - if [[ $MINICONDA_MD5 != $(md5sum $MINICONDA | cut -d ' ' -f 1) ]]; then - echo "Miniconda MD5 mismatch" - exit 1 - fi - # Travis creates the cached directories for us. - # This is problematic when wanting to install Anaconda for the first time... - rm -rf "$MINICONDA_HOME" - bash $MINICONDA -b -p "$MINICONDA_HOME" - - # Configure miniconda - export PIP_ARGS="-U" - export PATH=$MINICONDA_HOME/bin:$PATH - - conda config --set always_yes yes --set changeps1 no - conda update -q conda - - rm -f $MINICONDA -fi -echo "-- Done with latest Miniconda" - -# Restore original directory -popd diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 2b1c9a53f..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = QCEngine -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index efbd5912c..000000000 --- a/docs/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Compiling QCEngine's Documentation - -The docs for this project are built with Sphinx. To compile the docs, first ensure that Sphinx and the ReadTheDocs theme are installed. - -``` -conda install sphinx sphinx_rtd_theme -``` - -Once installed, you can use the Makefile in this directory to compile static HTML pages by - -``` -make html -``` - -The compiled docs will be in the _build directory and can be viewed by opening index.html (which may itself be inside a directory called html/ depending on what version of Sphinx is installed). diff --git a/docs/requirements.yml b/docs/requirements.yml deleted file mode 100644 index e1ff1a1f2..000000000 --- a/docs/requirements.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: qcengine-docs -channels: - - defaults - - conda-forge -dependencies: - - python=3 - - sphinx <4 - - sphinx_rtd_theme - - sphinx-automodapi - - graphviz - - # QCEngine depends - - numpy - - pydantic >=0.30.1 - - qcelemental >=0.9.0 - - - pip: - - git+git://github.com/MolSSI/qcarchive-sphinx-theme#egg=qcarchive_sphinx_theme diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html deleted file mode 100644 index acf5c8e37..000000000 --- a/docs/source/_templates/layout.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "!layout.html" %} - -{% block extrahead %} - - - -{{ super() }} -{% endblock %} - -{% block extrabody %} - - - -{{ super() }} -{% endblock %} diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 1d210f1ff..000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,212 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -import datetime -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -import qcengine - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'QCEngine' -copyright = f'2018-{datetime.datetime.today().year}, The Molecular Sciences Software Institute' -author = 'The QCArchive Development Team' - -# The short X.Y version -version = qcengine.__version__ -# The full version, including alpha/beta/rc tags -release = qcengine.__version__ - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.doctest', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.extlinks', - 'sphinx.ext.graphviz', - 'sphinx.ext.autosummary', - 'sphinx.ext.napoleon', - 'sphinx_automodapi.automodapi', - 'sphinx_automodapi.automodsumm', - 'sphinx_automodapi.smart_resolver', - "sphinx_autodoc_typehints", - "sphinxcontrib.autodoc_pydantic", -] - -autosummary_generate = True -automodapi_toctreedirnm = 'api' -autodoc_typehints = "description" -napoleon_use_param = True -napoleon_use_rtype = True -autodoc_pydantic_model_hide_paramlist = True -autodoc_pydantic_model_show_config_summary = False -autodoc_pydantic_field_swap_name_and_alias = True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = "en" - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path . -exclude_patterns = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'default' - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'QCEnginedoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'QCEngine.tex', 'QCEngine Documentation', - 'The QCArchive Development Team', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'qcengine', 'QCEngine Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'QCEngine', 'QCEngine Documentation', - author, 'QCEngine', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Extension configuration ------------------------------------------------- - -extlinks = { - 'issue': ('https://github.com/MolSSI/QCEngine/issues/%s', 'GH#%s'), - 'pr': ('https://github.com/MolSSI/QCEngine/pull/%s', 'GH#%s') -} - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('https://docs.python.org/3.10', None), - "numpy": ("https://numpy.org/doc/stable/", None), - 'scipy': ('https://docs.scipy.org/doc/scipy/', None), - 'matplotlib': ('https://matplotlib.org/stable/', None), - "qcelemental": ("http://docs.qcarchive.molssi.org/projects/QCElemental/en/latest/", None), - "qcportal": ("http://docs.qcarchive.molssi.org/projects/QCPortal/en/latest/", None), - "qcfractal": ("http://docs.qcarchive.molssi.org/projects/QCFractal/en/latest/", None), - } - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True diff --git a/environment.html b/environment.html new file mode 100644 index 000000000..27db71730 --- /dev/null +++ b/environment.html @@ -0,0 +1,386 @@ + + + + + + + Environment Detection — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Environment Detection

+

QCEngine can inspect the current compute environment to determine the resources available to it.

+
+

Node Description

+

QCEngine can detect node descriptions to obtain general information about the current node.

+
>>> qcng.config.get_node_descriptor()
+<NodeDescriptor hostname_pattern='*' name='default' scratch_directory=None
+                memory=5.568 memory_safety_factor=10 ncores=4 jobs_per_node=2>
+
+
+
+
+

Config

+

The configuration file operated based on the current node descriptor and can be overridden:

+
>>> qcng.get_config()
+<JobConfig ncores=2 memory=2.506 scratch_directory=None>
+
+>>> qcng.get_config(task_config={"scratch_directory": "/tmp"})
+<JobConfig ncores=2 memory=2.506 scratch_directory='/tmp'>
+
+>>> os.environ["SCRATCH"] = "/my_scratch"
+>>> qcng.get_config(task_config={"scratch_directory": "$SCRATCH"})
+<JobConfig ncores=2 memory=2.506 scratch_directory='/my_scratch'>
+
+
+
+
+

Global Environment

+

The global environment can also be inspected directly.

+
>>> qcng.config.get_global()
+{
+    'hostname': 'qcarchive.molssi.org',
+    'memory': 5.568,
+    'username': 'user',
+    'ncores': 4,
+    'cpuinfo': {
+        'python_version': '3.6.7.final.0 (64 bit)',
+        'brand': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',
+        'hz_advertised': '2.9000 GHz',
+        ...
+    },
+    'cpu_brand': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz'
+}
+
+
+
+
+

Configuration Files

+

The computational environment defaults can be overridden by configuration files.

+

Configuration files must be named qcengine.yaml and stored either in the directory +from which you run QCEngine, a folder named .qcarchive in your home directory, +or in a folder specified by the DQM_CONFIG_PATH environmental variable. +Only one configuration file will be used if multiple are available. +The DQM_CONFIG_PATH configuration file takes precedence over the current directory, +which takes precedence over the .qcarchive folder.

+

The configuration file is a YAML file that contains a dictionary of different node configurations. +The keys in the YAML file are human-friendly names for the configurations. +The values are dictionaries that define configurations for different nodes, +following the NodeDescription schema:

+
+
+pydantic model qcengine.config.NodeDescriptor[source]
+

Description of an individual node

+

+Show JSON schema
{
+   "title": "NodeDescriptor",
+   "description": "Description of an individual node",
+   "type": "object",
+   "properties": {
+      "hostname_pattern": {
+         "title": "Hostname Pattern",
+         "type": "string"
+      },
+      "name": {
+         "title": "Name",
+         "type": "string"
+      },
+      "scratch_directory": {
+         "title": "Scratch Directory",
+         "type": "string"
+      },
+      "memory": {
+         "title": "Memory",
+         "type": "number"
+      },
+      "memory_safety_factor": {
+         "title": "Memory Safety Factor",
+         "default": 10,
+         "type": "integer"
+      },
+      "ncores": {
+         "title": "Ncores",
+         "description": "Number of cores accessible to each task on this node\n    \n    The default value, ``None``, will allow QCEngine to autodetect the number of cores.",
+         "type": "integer"
+      },
+      "jobs_per_node": {
+         "title": "Jobs Per Node",
+         "default": 1,
+         "type": "integer"
+      },
+      "retries": {
+         "title": "Retries",
+         "default": 0,
+         "type": "integer"
+      },
+      "is_batch_node": {
+         "title": "Is Batch Node",
+         "default": false,
+         "help": "Whether the node running QCEngine is a batch node\n    \n    Some clusters are configured such that tasks are launched from a special \"batch\" or \"MOM\" onto the compute nodes.\n    The compute nodes on such clusters often have a different CPU architecture than the batch nodes and \n    often are unable to launch MPI tasks, which has two implications:\n        1) QCEngine must make *all* calls to an executable via ``mpirun`` because the executables might not\n        be able to run on the batch node. \n        2) QCEngine must run on the batch node to be able to launch tasks on the more than one compute nodes  \n    \n    ``is_batch_node`` is used when creating the task configuration as a means of determining whether\n    ``mpiexec_command`` must always be used even for serial jobs (e.g., getting the version number)\n    ",
+         "type": "boolean"
+      },
+      "mpiexec_command": {
+         "title": "Mpiexec Command",
+         "description": "Invocation for launching node-parallel tasks with MPI\n        \n        The invocation need not specify the number of nodes, tasks, or cores per node.\n        Information about the task configuration will be added to the command by use of\n        Python's string formatting. The configuration will be supplied as the following variables:\n        \n            {nnodes} - Number of nodes\n            {ranks_per_node} - Number of MPI ranks per node\n            {cores_per_rank} - Number of cores to use for each MPI rank\n            {total_ranks} - Total number of MPI ranks\n            \n        As examples, the ``aprun`` command on Cray systems should be similar to \n        ``aprun -n {total_ranks} -N {ranks_per_node}`` and ``mpirun`` from OpenMPI should\n        be similar to ``mpirun -np {total_ranks} -N {ranks_per_node}``.\n        \n        Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can \n        use the {cores_per_rank} option to control the hybrid parallelism. \n        As an example, the Cray ``aprun`` command using this figure could be:\n        ``aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1``.\n        The appropriate number of ranks per node will be determined based on the number of\n        cores per node and the number of cores per rank.\n        ",
+         "type": "string"
+      }
+   },
+   "required": [
+      "hostname_pattern",
+      "name"
+   ],
+   "additionalProperties": false
+}
+
+
+

+
Fields:
+
+
+
+
+
+field hostname_pattern: str [Required]
+
+ +
+
+field is_batch_node: bool = False
+
+ +
+
+field jobs_per_node: int = 1
+
+ +
+
+field memory: Optional[float] = None
+
+ +
+
+field memory_safety_factor: int = 10
+
+ +
+
+field mpiexec_command: Optional[str] = None
+

Invocation for launching node-parallel tasks with MPI

+

The invocation need not specify the number of nodes, tasks, or cores per node. +Information about the task configuration will be added to the command by use of +Python’s string formatting. The configuration will be supplied as the following variables:

+
+

{nnodes} - Number of nodes +{ranks_per_node} - Number of MPI ranks per node +{cores_per_rank} - Number of cores to use for each MPI rank +{total_ranks} - Total number of MPI ranks

+
+

As examples, the aprun command on Cray systems should be similar to +aprun -n {total_ranks} -N {ranks_per_node} and mpirun from OpenMPI should +be similar to mpirun -np {total_ranks} -N {ranks_per_node}.

+

Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can +use the {cores_per_rank} option to control the hybrid parallelism. +As an example, the Cray aprun command using this figure could be: +aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1. +The appropriate number of ranks per node will be determined based on the number of +cores per node and the number of cores per rank.

+
+ +
+
+field name: str [Required]
+
+ +
+
+field ncores: Optional[int] = None
+

Number of cores accessible to each task on this node

+

The default value, None, will allow QCEngine to autodetect the number of cores.

+
+ +
+
+field retries: int = 0
+
+ +
+
+field scratch_directory: Optional[str] = None
+
+ +
+ +

When running QCEngine, the proper configuration for a node is determined based on the hostname of the node +and matching the hostname_pattern to each of the configurations defined in qcengine.yaml.

+

An example qcengine.yaml file that sets the scratch directory for all nodes is as follows:

+
all:
+  hostname_pattern: "*"
+  scratch_directory: ./scratch
+
+
+
+
+

Cluster Configuration

+

A node configuration file is required when using node-parallel tasks on a compute cluster. +The configuration file must contain a description of the command used to launch MPI tasks and, +in some cases, the designation that a certain node is a compute node. +See the descriptions for mpiexec_command and is_batch_node in the NodeDescriptor +documentation for further details.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/Examples.ipynb b/examples/Examples.ipynb deleted file mode 100644 index b1d071d3a..000000000 --- a/examples/Examples.ipynb +++ /dev/null @@ -1,729 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "import qcelemental\n", - "import qcengine\n", - "import json" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## A simple quantum chemistry task\n", - "\n", - "A single task can be written" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "mol = qcelemental.models.Molecule.from_data(\"\"\"\n", - " O 0.000000000000 0.000000000000 -0.068516245955\n", - " H 0.000000000000 -0.790689888800 0.543701278274\n", - " H 0.000000000000 0.790689888800 0.543701278274\n", - "\"\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'schema_name': 'qcschema_input',\n", - " 'schema_version': 1,\n", - " 'molecule': < Geometry (in Angstrom), charge = 0.0, multiplicity = 1:\n", - " \n", - " Center X Y Z \n", - " ------------ ----------------- ----------------- -----------------\n", - " O 0.000000000000 0.000000000000 -0.068516245955\n", - " H 0.000000000000 -0.790689888800 0.543701278274\n", - " H 0.000000000000 0.790689888800 0.543701278274\n", - " \n", - " >,\n", - " 'driver': 'energy',\n", - " 'model': {'method': 'SCF', 'basis': 'sto-3g'},\n", - " 'keywords': {'scf_type': 'df'},\n", - " 'return_output': False}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "psi4_task = {\n", - " \"schema_name\": \"qcschema_input\",\n", - " \"schema_version\": 1,\n", - " \"molecule\": mol,\n", - " \"driver\": \"energy\",\n", - " \"model\": {\"method\": \"SCF\", \"basis\": \"sto-3g\"},\n", - " \"keywords\": {\"scf_type\": \"df\"},\n", - " \"return_output\": False\n", - "}\n", - "\n", - "psi4_task" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'name': 'H2O',\n", - " 'molecular_charge': 0.0,\n", - " 'molecular_multiplicity': 1,\n", - " 'masses': [15.99491461957, 1.00782503223, 1.00782503223],\n", - " 'real': [True, True, True],\n", - " 'atom_labels': ['', '', ''],\n", - " 'atomic_numbers': [8, 1, 1],\n", - " 'mass_numbers': [16, 1, 1],\n", - " 'fragments': [[0, 1, 2]],\n", - " 'fragment_charges': [0.0],\n", - " 'fragment_multiplicities': [1],\n", - " 'fix_com': False,\n", - " 'fix_orientation': False,\n", - " 'provenance': {'creator': 'QCElemental',\n", - " 'version': 'v0.2.6',\n", - " 'routine': 'qcelemental.molparse.from_string'}},\n", - " 'driver': 'energy',\n", - " 'model': {'method': 'SCF', 'basis': 'sto-3g'},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {'scf_type': 'df'},\n", - " 'provenance': {'creator': 'Psi4',\n", - " 'version': '1.2.1',\n", - " 'routine': 'psi4.json.run_json',\n", - " 'nthreads': 2,\n", - " 'memory': 2.363,\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.39830708503723145,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': -74.96475169983995,\n", - " 'properties': {'calcinfo_nbasis': 7,\n", - " 'calcinfo_nmo': 7,\n", - " 'calcinfo_nalpha': 5,\n", - " 'calcinfo_nbeta': 5,\n", - " 'calcinfo_natom': 3,\n", - " 'nuclear_repulsion_energy': 8.80146205625184,\n", - " 'return_energy': -74.96475169983995,\n", - " 'scf_one_electron_energy': -121.67648822135493,\n", - " 'scf_two_electron_energy': 37.91027446526314,\n", - " 'scf_dipole_moment': [0.0, 0.0, 1.6686844760157988],\n", - " 'scf_total_energy': -74.96475169983995,\n", - " 'scf_iterations': 6},\n", - " 'error': None,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'psi4:qcvars': {'CURRENT DIPOLE X': 0.0,\n", - " 'CURRENT DIPOLE Y': 0.0,\n", - " 'CURRENT DIPOLE Z': 1.6686844760157988,\n", - " 'CURRENT ENERGY': -74.96475169983995,\n", - " 'CURRENT REFERENCE ENERGY': -74.96475169983995,\n", - " 'HF TOTAL ENERGY': -74.96475169983995,\n", - " 'NUCLEAR REPULSION ENERGY': 8.80146205625184,\n", - " 'ONE-ELECTRON ENERGY': -121.67648822135493,\n", - " 'PCM POLARIZATION ENERGY': 0.0,\n", - " 'SCF DIPOLE X': 0.0,\n", - " 'SCF DIPOLE Y': 0.0,\n", - " 'SCF DIPOLE Z': 1.6686844760157988,\n", - " 'SCF ITERATION ENERGY': -74.96475169983995,\n", - " 'SCF ITERATIONS': 6.0,\n", - " 'SCF N ITERS': 6.0,\n", - " 'SCF TOTAL ENERGY': -74.96475169983995,\n", - " 'TWO-ELECTRON ENERGY': 37.91027446526314},\n", - " 'stdout': '\\n Memory set to 2.363 GiB by Python driver.\\n\\n*** tstart() called on Daniels-MacBook-Pro.local\\n*** at Thu Feb 21 14:26:27 2019\\n\\n => Loading Basis Set <=\\n\\n Name: STO-3G\\n Role: ORBITAL\\n Keyword: BASIS\\n atoms 1 entry O line 81 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/sto-3g.gbs \\n atoms 2-3 entry H line 19 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/sto-3g.gbs \\n\\n\\n ---------------------------------------------------------\\n SCF\\n by Justin Turney, Rob Parrish, Andy Simmonett\\n and Daniel Smith\\n RHF Reference\\n 2 Threads, 2419 MiB Core\\n ---------------------------------------------------------\\n\\n ==> Geometry <==\\n\\n Molecular point group: c2v\\n Full point group: C2v\\n\\n Geometry (in Bohr), charge = 0, multiplicity = 1:\\n\\n Center X Y Z Mass \\n ------------ ----------------- ----------------- ----------------- -----------------\\n O 0.000000000000 0.000000000000 -0.129476941311 15.994914619570\\n H 0.000000000000 -1.494187340000 1.027446508689 1.007825032230\\n H -0.000000000000 1.494187340000 1.027446508689 1.007825032230\\n\\n Running in c2v symmetry.\\n\\n Rotational constants: A = 25.12553 B = 13.37732 C = 8.72954 [cm^-1]\\n Rotational constants: A = 753244.47078 B = 401041.84756 C = 261705.04616 [MHz]\\n Nuclear repulsion = 8.801462056251840\\n\\n Charge = 0\\n Multiplicity = 1\\n Electrons = 10\\n Nalpha = 5\\n Nbeta = 5\\n\\n ==> Algorithm <==\\n\\n SCF Algorithm Type is DF.\\n DIIS enabled.\\n MOM disabled.\\n Fractional occupation disabled.\\n Guess Type is SAD.\\n Energy threshold = 1.00e-06\\n Density threshold = 1.00e-06\\n Integral threshold = 0.00e+00\\n\\n ==> Primary Basis <==\\n\\n Basis Set: STO-3G\\n Blend: STO-3G\\n Number of shells: 5\\n Number of basis function: 7\\n Number of Cartesian functions: 7\\n Spherical Harmonics?: true\\n Max angular momentum: 1\\n\\n => Loading Basis Set <=\\n\\n Name: (STO-3G AUX)\\n Role: JKFIT\\n Keyword: DF_BASIS_SCF\\n atoms 1 entry O line 323 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/def2-svp-jkfit.gbs \\n atoms 2-3 entry H line 23 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/def2-svp-jkfit.gbs \\n\\n ==> Pre-Iterations <==\\n\\n -------------------------------------------------------\\n Irrep Nso Nmo Nalpha Nbeta Ndocc Nsocc\\n -------------------------------------------------------\\n A1 4 4 0 0 0 0\\n A2 0 0 0 0 0 0\\n B1 1 1 0 0 0 0\\n B2 2 2 0 0 0 0\\n -------------------------------------------------------\\n Total 7 7 5 5 5 0\\n -------------------------------------------------------\\n\\n ==> Integral Setup <==\\n\\n DFHelper Memory: AOs need 0.000 [GiB]; user supplied 1.772 [GiB]. Using in-core AOs.\\n\\n ==> MemDFJK: Density-Fitted J/K Matrices <==\\n\\n J tasked: Yes\\n K tasked: Yes\\n wK tasked: No\\n OpenMP threads: 2\\n Memory (MB): 1814\\n Algorithm: Core\\n Schwarz Cutoff: 1E-12\\n Mask sparsity (%): 0.0000\\n Fitting Condition: 1E-12\\n\\n => Auxiliary Basis Set <=\\n\\n Basis Set: (STO-3G AUX)\\n Blend: DEF2-SVP-JKFIT\\n Number of shells: 37\\n Number of basis function: 113\\n Number of Cartesian functions: 133\\n Spherical Harmonics?: true\\n Max angular momentum: 4\\n\\n Minimum eigenvalue in the overlap matrix is 3.7028426405E-01.\\n Using Symmetric Orthogonalization.\\n\\n SCF Guess: Superposition of Atomic Densities via on-the-fly atomic UHF.\\n\\n ==> Iterations <==\\n\\n Total Energy Delta E RMS |[F,P]|\\n\\n @DF-RHF iter 0: -74.69402902983985 -7.46940e+01 2.97675e-01 \\n @DF-RHF iter 1: -74.91405389559668 -2.20025e-01 5.19907e-02 \\n @DF-RHF iter 2: -74.96251196514112 -4.84581e-02 9.52327e-03 DIIS\\n @DF-RHF iter 3: -74.96429870282233 -1.78674e-03 3.64833e-03 DIIS\\n @DF-RHF iter 4: -74.96474887540938 -4.50173e-04 3.39468e-04 DIIS\\n @DF-RHF iter 5: -74.96475167030835 -2.79490e-06 3.89206e-05 DIIS\\n @DF-RHF iter 6: -74.96475169983995 -2.95316e-08 8.30820e-07 DIIS\\n\\n ==> Post-Iterations <==\\n\\n Orbital Energies [Eh]\\n ---------------------\\n\\n Doubly Occupied: \\n\\n 1A1 -20.247493 2A1 -1.247793 1B2 -0.595851 \\n 3A1 -0.447885 1B1 -0.388958 \\n\\n Virtual: \\n\\n 4A1 0.564174 2B2 0.693043 \\n\\n Final Occupation by Irrep:\\n A1 A2 B1 B2 \\n DOCC [ 3, 0, 1, 1 ]\\n\\n Energy converged.\\n\\n @DF-RHF Final Energy: -74.96475169983995\\n\\n => Energetics <=\\n\\n Nuclear Repulsion Energy = 8.8014620562518395\\n One-Electron Energy = -121.6764882213549299\\n Two-Electron Energy = 37.9102744652631429\\n Total Energy = -74.9647516998399510\\n\\n\\n\\nProperties will be evaluated at 0.000000, 0.000000, 0.000000 [a0]\\n\\nProperties computed using the SCF density matrix\\n\\n Nuclear Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: 1.0191\\n\\n Electronic Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: -0.3626\\n\\n Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: 0.6565 Total: 0.6565\\n\\n Dipole Moment: [D]\\n X: 0.0000 Y: 0.0000 Z: 1.6687 Total: 1.6687\\n\\n\\n*** tstop() called on Daniels-MacBook-Pro.local at Thu Feb 21 14:26:27 2019\\nModule time:\\n\\tuser time = 0.44 seconds = 0.01 minutes\\n\\tsystem time = 0.02 seconds = 0.00 minutes\\n\\ttotal time = 0 seconds = 0.00 minutes\\nTotal time:\\n\\tuser time = 1.23 seconds = 0.02 minutes\\n\\tsystem time = 0.17 seconds = 0.00 minutes\\n\\ttotal time = 259 seconds = 4.32 minutes\\n'}" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qcengine.compute(psi4_task, \"psi4\")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'name': 'H2O',\n", - " 'molecular_charge': 0.0,\n", - " 'molecular_multiplicity': 1,\n", - " 'masses': [15.99491461957, 1.00782503223, 1.00782503223],\n", - " 'real': [True, True, True],\n", - " 'atom_labels': ['', '', ''],\n", - " 'atomic_numbers': [8, 1, 1],\n", - " 'mass_numbers': [16, 1, 1],\n", - " 'fragments': [[0, 1, 2]],\n", - " 'fragment_charges': [0.0],\n", - " 'fragment_multiplicities': [1],\n", - " 'fix_com': False,\n", - " 'fix_orientation': False,\n", - " 'provenance': {'creator': 'QCElemental',\n", - " 'version': 'v0.2.6',\n", - " 'routine': 'qcelemental.molparse.from_string'}},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'SCF', 'basis': 'sto-3g'},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {'scf_type': 'df'},\n", - " 'provenance': {'creator': 'Psi4',\n", - " 'version': '1.2.1',\n", - " 'routine': 'psi4.json.run_json',\n", - " 'nthreads': 2,\n", - " 'memory': 2.363,\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.5595040321350098,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [1.9894085953542391e-29,\n", - " 0.0,\n", - " 0.00177377357460623,\n", - " 5.930424637099778e-19,\n", - " -0.019370236843236188,\n", - " -0.0008868867872527386,\n", - " -5.930424637297979e-19,\n", - " 0.019370236843236188,\n", - " -0.0008868867872527386],\n", - " 'properties': {'calcinfo_nbasis': 7,\n", - " 'calcinfo_nmo': 7,\n", - " 'calcinfo_nalpha': 5,\n", - " 'calcinfo_nbeta': 5,\n", - " 'calcinfo_natom': 3,\n", - " 'nuclear_repulsion_energy': 8.80146205625184,\n", - " 'return_energy': -74.96475169985098,\n", - " 'scf_one_electron_energy': -121.6764697280917,\n", - " 'scf_two_electron_energy': 37.91025597198889,\n", - " 'scf_dipole_moment': [0.0, 0.0, 1.668684982939947],\n", - " 'scf_total_energy': -74.96475169985098,\n", - " 'scf_iterations': 8},\n", - " 'error': None,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'psi4:qcvars': {'CURRENT DIPOLE X': 0.0,\n", - " 'CURRENT DIPOLE Y': 0.0,\n", - " 'CURRENT DIPOLE Z': 1.668684982939947,\n", - " 'CURRENT ENERGY': -74.96475169985098,\n", - " 'CURRENT REFERENCE ENERGY': -74.96475169985098,\n", - " 'HF TOTAL ENERGY': -74.96475169985098,\n", - " 'NUCLEAR REPULSION ENERGY': 8.80146205625184,\n", - " 'ONE-ELECTRON ENERGY': -121.6764697280917,\n", - " 'PCM POLARIZATION ENERGY': 0.0,\n", - " 'SCF DIPOLE X': 0.0,\n", - " 'SCF DIPOLE Y': 0.0,\n", - " 'SCF DIPOLE Z': 1.668684982939947,\n", - " 'SCF ITERATION ENERGY': -74.96475169985098,\n", - " 'SCF ITERATIONS': 8.0,\n", - " 'SCF N ITERS': 8.0,\n", - " 'SCF TOTAL ENERGY': -74.96475169985098,\n", - " 'TWO-ELECTRON ENERGY': 37.91025597198889},\n", - " 'stdout': '\\n Memory set to 2.363 GiB by Python driver.\\ngradient() will perform analytic gradient computation.\\n\\n*** tstart() called on Daniels-MacBook-Pro.local\\n*** at Thu Feb 21 14:26:30 2019\\n\\n => Loading Basis Set <=\\n\\n Name: STO-3G\\n Role: ORBITAL\\n Keyword: BASIS\\n atoms 1 entry O line 81 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/sto-3g.gbs \\n atoms 2-3 entry H line 19 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/sto-3g.gbs \\n\\n\\n ---------------------------------------------------------\\n SCF\\n by Justin Turney, Rob Parrish, Andy Simmonett\\n and Daniel Smith\\n RHF Reference\\n 2 Threads, 2419 MiB Core\\n ---------------------------------------------------------\\n\\n ==> Geometry <==\\n\\n Molecular point group: c2v\\n Full point group: C2v\\n\\n Geometry (in Bohr), charge = 0, multiplicity = 1:\\n\\n Center X Y Z Mass \\n ------------ ----------------- ----------------- ----------------- -----------------\\n O 0.000000000000 0.000000000000 -0.129476941311 15.994914619570\\n H 0.000000000000 -1.494187340000 1.027446508689 1.007825032230\\n H -0.000000000000 1.494187340000 1.027446508689 1.007825032230\\n\\n Running in c2v symmetry.\\n\\n Rotational constants: A = 25.12553 B = 13.37732 C = 8.72954 [cm^-1]\\n Rotational constants: A = 753244.47078 B = 401041.84756 C = 261705.04616 [MHz]\\n Nuclear repulsion = 8.801462056251840\\n\\n Charge = 0\\n Multiplicity = 1\\n Electrons = 10\\n Nalpha = 5\\n Nbeta = 5\\n\\n ==> Algorithm <==\\n\\n SCF Algorithm Type is DF.\\n DIIS enabled.\\n MOM disabled.\\n Fractional occupation disabled.\\n Guess Type is SAD.\\n Energy threshold = 1.00e-08\\n Density threshold = 1.00e-08\\n Integral threshold = 0.00e+00\\n\\n ==> Primary Basis <==\\n\\n Basis Set: STO-3G\\n Blend: STO-3G\\n Number of shells: 5\\n Number of basis function: 7\\n Number of Cartesian functions: 7\\n Spherical Harmonics?: true\\n Max angular momentum: 1\\n\\n => Loading Basis Set <=\\n\\n Name: (STO-3G AUX)\\n Role: JKFIT\\n Keyword: DF_BASIS_SCF\\n atoms 1 entry O line 323 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/def2-svp-jkfit.gbs \\n atoms 2-3 entry H line 23 file /Users/daniel/anaconda3/envs/qcf/share/psi4/basis/def2-svp-jkfit.gbs \\n\\n ==> Pre-Iterations <==\\n\\n -------------------------------------------------------\\n Irrep Nso Nmo Nalpha Nbeta Ndocc Nsocc\\n -------------------------------------------------------\\n A1 4 4 0 0 0 0\\n A2 0 0 0 0 0 0\\n B1 1 1 0 0 0 0\\n B2 2 2 0 0 0 0\\n -------------------------------------------------------\\n Total 7 7 5 5 5 0\\n -------------------------------------------------------\\n\\n ==> Integral Setup <==\\n\\n DFHelper Memory: AOs need 0.000 [GiB]; user supplied 1.772 [GiB]. Using in-core AOs.\\n\\n ==> MemDFJK: Density-Fitted J/K Matrices <==\\n\\n J tasked: Yes\\n K tasked: Yes\\n wK tasked: No\\n OpenMP threads: 2\\n Memory (MB): 1814\\n Algorithm: Core\\n Schwarz Cutoff: 1E-12\\n Mask sparsity (%): 0.0000\\n Fitting Condition: 1E-12\\n\\n => Auxiliary Basis Set <=\\n\\n Basis Set: (STO-3G AUX)\\n Blend: DEF2-SVP-JKFIT\\n Number of shells: 37\\n Number of basis function: 113\\n Number of Cartesian functions: 133\\n Spherical Harmonics?: true\\n Max angular momentum: 4\\n\\n Minimum eigenvalue in the overlap matrix is 3.7028426405E-01.\\n Using Symmetric Orthogonalization.\\n\\n SCF Guess: Superposition of Atomic Densities via on-the-fly atomic UHF.\\n\\n ==> Iterations <==\\n\\n Total Energy Delta E RMS |[F,P]|\\n\\n @DF-RHF iter 0: -74.69402902983985 -7.46940e+01 2.97675e-01 \\n @DF-RHF iter 1: -74.91405389559664 -2.20025e-01 5.19907e-02 \\n @DF-RHF iter 2: -74.96251196514109 -4.84581e-02 9.52327e-03 DIIS\\n @DF-RHF iter 3: -74.96429870282233 -1.78674e-03 3.64833e-03 DIIS\\n @DF-RHF iter 4: -74.96474887540941 -4.50173e-04 3.39468e-04 DIIS\\n @DF-RHF iter 5: -74.96475167030833 -2.79490e-06 3.89206e-05 DIIS\\n @DF-RHF iter 6: -74.96475169983995 -2.95316e-08 8.30820e-07 DIIS\\n @DF-RHF iter 7: -74.96475169985094 -1.09850e-11 2.29675e-08 DIIS\\n @DF-RHF iter 8: -74.96475169985098 -4.26326e-14 1.44914e-10 DIIS\\n\\n ==> Post-Iterations <==\\n\\n Orbital Energies [Eh]\\n ---------------------\\n\\n Doubly Occupied: \\n\\n 1A1 -20.247493 2A1 -1.247793 1B2 -0.595851 \\n 3A1 -0.447885 1B1 -0.388958 \\n\\n Virtual: \\n\\n 4A1 0.564173 2B2 0.693043 \\n\\n Final Occupation by Irrep:\\n A1 A2 B1 B2 \\n DOCC [ 3, 0, 1, 1 ]\\n\\n Energy converged.\\n\\n @DF-RHF Final Energy: -74.96475169985098\\n\\n => Energetics <=\\n\\n Nuclear Repulsion Energy = 8.8014620562518395\\n One-Electron Energy = -121.6764697280917034\\n Two-Electron Energy = 37.9102559719888887\\n Total Energy = -74.9647516998509786\\n\\n\\n\\nProperties will be evaluated at 0.000000, 0.000000, 0.000000 [a0]\\n\\nProperties computed using the SCF density matrix\\n\\n Nuclear Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: 1.0191\\n\\n Electronic Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: -0.3626\\n\\n Dipole Moment: [e a0]\\n X: 0.0000 Y: 0.0000 Z: 0.6565 Total: 0.6565\\n\\n Dipole Moment: [D]\\n X: 0.0000 Y: 0.0000 Z: 1.6687 Total: 1.6687\\n\\n\\n*** tstop() called on Daniels-MacBook-Pro.local at Thu Feb 21 14:26:30 2019\\nModule time:\\n\\tuser time = 0.51 seconds = 0.01 minutes\\n\\tsystem time = 0.03 seconds = 0.00 minutes\\n\\ttotal time = 0 seconds = 0.00 minutes\\nTotal time:\\n\\tuser time = 1.78 seconds = 0.03 minutes\\n\\tsystem time = 0.20 seconds = 0.00 minutes\\n\\ttotal time = 262 seconds = 4.37 minutes\\n\\n*** tstart() called on Daniels-MacBook-Pro.local\\n*** at Thu Feb 21 14:26:30 2019\\n\\n\\n ------------------------------------------------------------\\n SCF GRAD \\n Rob Parrish, Justin Turney, \\n Andy Simmonett, and Alex Sokolov \\n ------------------------------------------------------------\\n\\n ==> Geometry <==\\n\\n Molecular point group: c2v\\n Full point group: C2v\\n\\n Geometry (in Bohr), charge = 0, multiplicity = 1:\\n\\n Center X Y Z Mass \\n ------------ ----------------- ----------------- ----------------- -----------------\\n O 0.000000000000 0.000000000000 -0.129476941311 15.994914619570\\n H 0.000000000000 -1.494187340000 1.027446508689 1.007825032230\\n H -0.000000000000 1.494187340000 1.027446508689 1.007825032230\\n\\n Nuclear repulsion = 8.801462056251840\\n\\n ==> Basis Set <==\\n\\n Basis Set: STO-3G\\n Blend: STO-3G\\n Number of shells: 5\\n Number of basis function: 7\\n Number of Cartesian functions: 7\\n Spherical Harmonics?: true\\n Max angular momentum: 1\\n\\n ==> DFJKGrad: Density-Fitted SCF Gradients <==\\n\\n Gradient: 1\\n J tasked: Yes\\n K tasked: Yes\\n wK tasked: No\\n OpenMP threads: 2\\n Integrals threads: 2\\n Memory (MB): 1814\\n Schwarz Cutoff: 0E+00\\n Fitting Condition: 1E-12\\n\\n => Auxiliary Basis Set <=\\n\\n Basis Set: (STO-3G AUX)\\n Blend: DEF2-SVP-JKFIT\\n Number of shells: 37\\n Number of basis function: 113\\n Number of Cartesian functions: 133\\n Spherical Harmonics?: true\\n Max angular momentum: 4\\n\\n\\n -Total Gradient:\\n Atom X Y Z\\n ------ ----------------- ----------------- -----------------\\n 1 0.000000000000 0.000000000000 0.001773773575\\n 2 0.000000000000 -0.019370236843 -0.000886886787\\n 3 -0.000000000000 0.019370236843 -0.000886886787\\n\\n\\n*** tstop() called on Daniels-MacBook-Pro.local at Thu Feb 21 14:26:30 2019\\nModule time:\\n\\tuser time = 0.17 seconds = 0.00 minutes\\n\\tsystem time = 0.01 seconds = 0.00 minutes\\n\\ttotal time = 0 seconds = 0.00 minutes\\nTotal time:\\n\\tuser time = 1.95 seconds = 0.03 minutes\\n\\tsystem time = 0.21 seconds = 0.00 minutes\\n\\ttotal time = 262 seconds = 4.37 minutes\\n'}" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "psi4_task[\"driver\"] = \"gradient\"\n", - "qcengine.compute(psi4_task, \"psi4\")" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]]},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'UFF', 'basis': None},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {'scf_type': 'df'},\n", - " 'provenance': {'creator': 'rdkit',\n", - " 'version': '2018.03.4',\n", - " 'routine': 'rdkit.Chem.AllChem.UFFGetMoleculeForceField',\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.5387258529663086,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [0.0,\n", - " 0.0,\n", - " -0.009643868612805792,\n", - " 0.0,\n", - " -0.006202887227922877,\n", - " 0.004821934306402896,\n", - " 0.0,\n", - " 0.006202887227922877,\n", - " 0.004821934306402896],\n", - " 'properties': {'return_energy': 4.05209848561957e-05},\n", - " 'error': None,\n", - " 'return_output': False,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': 'No stdout recieved.'}" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rdkit_task = {\n", - " \"schema_name\": \"qcschema_input\",\n", - " \"schema_version\": 1,\n", - " \"molecule\": qcengine.get_molecule(\"water\"),\n", - " \"driver\": \"gradient\",\n", - " \"model\": {\"method\": \"UFF\", \"basis\": None},\n", - " \"keywords\": {\"scf_type\": \"df\"},\n", - " \"return_output\": False}\n", - "qcengine.compute(rdkit_task, \"rdkit\")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]]},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'ani1', 'basis': None},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {'scf_type': 'df'},\n", - " 'provenance': {'creator': 'torchani',\n", - " 'version': 'unknown',\n", - " 'routine': 'torchani.builtin.aev_computer',\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 6.07636284828186,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [0.0,\n", - " 0.0,\n", - " -0.1136966422200203,\n", - " 0.0,\n", - " -0.09969407320022583,\n", - " 0.056848324835300446,\n", - " 0.0,\n", - " 0.09969407320022583,\n", - " 0.056848324835300446],\n", - " 'properties': {'return_energy': -76.38631439208984},\n", - " 'error': None,\n", - " 'return_output': False,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': 'No stdout recieved.'}" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "torch_task = {\n", - " \"schema_name\": \"qcschema_input\",\n", - " \"schema_version\": 1,\n", - " \"molecule\": qcengine.get_molecule(\"water\"),\n", - " \"driver\": \"gradient\",\n", - " \"model\": {\"method\": \"ani1\", \"basis\": None},\n", - " \"keywords\": {\"scf_type\": \"df\"},\n", - " \"return_output\": False}\n", - "qcengine.compute(torch_task, \"torchani\")" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'input_specification': {'driver': 'gradient',\n", - " 'model': {'method': 'UFF', 'basis': None},\n", - " 'schema_name': 'qcschema_input',\n", - " 'schema_version': 1,\n", - " 'keywords': {}},\n", - " 'initial_molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]]},\n", - " 'schema_name': 'qcschema_optimization_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {'coordsys': 'tric', 'maxiter': 100, 'program': 'rdkit'},\n", - " 'final_molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.1218741,\n", - " 0.0,\n", - " -1.47972431,\n", - " 1.02364509,\n", - " 0.0,\n", - " 1.47972431,\n", - " 1.02364509],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]],\n", - " 'fix_com': True,\n", - " 'fix_orientation': True},\n", - " 'success': True,\n", - " 'id': None,\n", - " 'trajectory': [{'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12947694,\n", - " 0.0,\n", - " -1.49418734,\n", - " 1.02744651,\n", - " 0.0,\n", - " 1.49418734,\n", - " 1.02744651],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]],\n", - " 'fix_com': True,\n", - " 'fix_orientation': True},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'UFF', 'basis': None},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {},\n", - " 'provenance': {'creator': 'rdkit',\n", - " 'version': '2018.03.4',\n", - " 'routine': 'rdkit.Chem.AllChem.UFFGetMoleculeForceField',\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.003949165344238281,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [0.0,\n", - " 0.0,\n", - " -0.009643868612805792,\n", - " 0.0,\n", - " -0.006202887227922877,\n", - " 0.004821934306402896,\n", - " 0.0,\n", - " 0.006202887227922877,\n", - " 0.004821934306402896],\n", - " 'properties': {'return_energy': 4.05209848561957e-05},\n", - " 'error': None,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': 'No stdout recieved.'},\n", - " {'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.12266448,\n", - " 0.0,\n", - " -1.48126327,\n", - " 1.02404028,\n", - " 0.0,\n", - " 1.48126327,\n", - " 1.02404028],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]],\n", - " 'fix_com': True,\n", - " 'fix_orientation': True},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'UFF', 'basis': None},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {},\n", - " 'provenance': {'creator': 'rdkit',\n", - " 'version': '2018.03.4',\n", - " 'routine': 'rdkit.Chem.AllChem.UFFGetMoleculeForceField',\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.0019562244415283203,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [0.0,\n", - " 0.0,\n", - " -0.0010139850382173455,\n", - " 0.0,\n", - " -0.0006555212879753362,\n", - " 0.0005069925191086728,\n", - " 0.0,\n", - " 0.0006555212879753362,\n", - " 0.0005069925191086728],\n", - " 'properties': {'return_energy': 4.508152898762255e-07},\n", - " 'error': None,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': 'No stdout recieved.'},\n", - " {'molecule': {'symbols': ['O', 'H', 'H'],\n", - " 'geometry': [0.0,\n", - " 0.0,\n", - " -0.1218741,\n", - " 0.0,\n", - " -1.47972431,\n", - " 1.02364509,\n", - " 0.0,\n", - " 1.47972431,\n", - " 1.02364509],\n", - " 'connectivity': [[0, 1, 1.0], [0, 2, 1.0]],\n", - " 'fix_com': True,\n", - " 'fix_orientation': True},\n", - " 'driver': 'gradient',\n", - " 'model': {'method': 'UFF', 'basis': None},\n", - " 'id': None,\n", - " 'schema_name': 'qcschema_output',\n", - " 'schema_version': 1,\n", - " 'keywords': {},\n", - " 'provenance': {'creator': 'rdkit',\n", - " 'version': '2018.03.4',\n", - " 'routine': 'rdkit.Chem.AllChem.UFFGetMoleculeForceField',\n", - " 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'wall_time': 0.0016818046569824219,\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'username': 'daniel',\n", - " 'hostname': 'Daniels-MacBook-Pro.local'},\n", - " 'success': True,\n", - " 'return_result': [0.0,\n", - " 0.0,\n", - " -8.404315685295723e-08,\n", - " 0.0,\n", - " 3.598898529188213e-08,\n", - " 4.2021578426478616e-08,\n", - " 0.0,\n", - " -3.598898529188213e-08,\n", - " 4.2021578426478616e-08],\n", - " 'properties': {'return_energy': 9.14109548708188e-15},\n", - " 'error': None,\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': 'No stdout recieved.'}],\n", - " 'energies': [4.05209848561957e-05,\n", - " 4.508152898762255e-07,\n", - " 9.14109548708188e-15],\n", - " 'error': None,\n", - " 'provenance': {'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'hostname': 'Daniels-MacBook-Pro.local',\n", - " 'username': 'daniel',\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'wall_time': 0.6345179080963135,\n", - " 'creator': 'QCEngine',\n", - " 'version': 'v0.5.2+23.g6c46911'},\n", - " 'stderr': 'No stderr recieved.',\n", - " 'stdout': \"9 internal coordinates being used (instead of 9 Cartesians)\\nInternal coordinate system (atoms numbered from 1):\\nDistance 1-2\\nDistance 1-3\\nAngle 2-1-3\\nTranslation-X 1-3\\nTranslation-Y 1-3\\nTranslation-Z 1-3\\nRotation-A 1-3\\nRotation-B 1-3\\nRotation-C 1-3\\n : 2\\n : 1\\n : 1\\n : 1\\n : 1\\n : 1\\n : 1\\n : 1\\nStep 0 : Gradient = 8.494e-03/9.644e-03 (rms/max) Energy = 0.0000405210\\nHessian Eigenvalues: 5.00000e-02 5.00000e-02 5.00000e-02 ... 1.60000e-01 4.76883e-01 4.76883e-01\\nStep 1 : Displace = \\x1b[0m6.138e-03\\x1b[0m/\\x1b[0m7.073e-03\\x1b[0m (rms/max) Trust = 1.000e-01 (=) Grad = \\x1b[0m8.947e-04\\x1b[0m/\\x1b[0m1.014e-03\\x1b[0m (rms/max) E (change) = 0.0000004508 (\\x1b[0m-4.007e-05\\x1b[0m) Quality = \\x1b[0m0.310\\x1b[0m\\nHessian Eigenvalues: 5.00000e-02 5.00000e-02 5.00000e-02 ... 1.60001e-01 4.26582e-01 4.76883e-01\\nStep 2 : Displace = \\x1b[92m7.277e-04\\x1b[0m/\\x1b[92m8.408e-04\\x1b[0m (rms/max) Trust = 1.000e-01 (=) Grad = \\x1b[92m6.630e-08\\x1b[0m/\\x1b[92m8.404e-08\\x1b[0m (rms/max) E (change) = 0.0000000000 (\\x1b[92m-4.508e-07\\x1b[0m) Quality = \\x1b[0m0.280\\x1b[0m\\nConverged! =D\\n\"}" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "geometric_task = {\n", - " \"schema_name\": \"qcschema_optimization_input\",\n", - " \"schema_version\": 1,\n", - " \"keywords\": {\n", - " \"coordsys\": \"tric\",\n", - " \"maxiter\": 100,\n", - " \"program\": \"rdkit\"\n", - " },\n", - " \"input_specification\": {\n", - " \"schema_name\": \"qcschema_input\",\n", - " \"schema_version\": 1,\n", - " \"driver\": \"gradient\",\n", - " \"model\": {\"method\": \"UFF\", \"basis\": None},\n", - " \"keywords\": {},\n", - " },\n", - " \"initial_molecule\": qcengine.get_molecule(\"water\"),\n", - "}\n", - "ret = qcengine.compute_procedure(geometric_task, \"geometric\")\n", - "ret" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "9 internal coordinates being used (instead of 9 Cartesians)\n", - "Internal coordinate system (atoms numbered from 1):\n", - "Distance 1-2\n", - "Distance 1-3\n", - "Angle 2-1-3\n", - "Translation-X 1-3\n", - "Translation-Y 1-3\n", - "Translation-Z 1-3\n", - "Rotation-A 1-3\n", - "Rotation-B 1-3\n", - "Rotation-C 1-3\n", - " : 2\n", - " : 1\n", - " : 1\n", - " : 1\n", - " : 1\n", - " : 1\n", - " : 1\n", - " : 1\n", - "Step 0 : Gradient = 8.494e-03/9.644e-03 (rms/max) Energy = 0.0000405210\n", - "Hessian Eigenvalues: 5.00000e-02 5.00000e-02 5.00000e-02 ... 1.60000e-01 4.76883e-01 4.76883e-01\n", - "Step 1 : Displace = \u001b[0m6.138e-03\u001b[0m/\u001b[0m7.073e-03\u001b[0m (rms/max) Trust = 1.000e-01 (=) Grad = \u001b[0m8.947e-04\u001b[0m/\u001b[0m1.014e-03\u001b[0m (rms/max) E (change) = 0.0000004508 (\u001b[0m-4.007e-05\u001b[0m) Quality = \u001b[0m0.310\u001b[0m\n", - "Hessian Eigenvalues: 5.00000e-02 5.00000e-02 5.00000e-02 ... 1.60001e-01 4.26582e-01 4.76883e-01\n", - "Step 2 : Displace = \u001b[92m7.277e-04\u001b[0m/\u001b[92m8.408e-04\u001b[0m (rms/max) Trust = 1.000e-01 (=) Grad = \u001b[92m6.630e-08\u001b[0m/\u001b[92m8.404e-08\u001b[0m (rms/max) E (change) = 0.0000000000 (\u001b[92m-4.508e-07\u001b[0m) Quality = \u001b[0m0.280\u001b[0m\n", - "Converged! =D\n", - "\n" - ] - } - ], - "source": [ - "print(ret[\"stdout\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz',\n", - " 'hostname': 'Daniels-MacBook-Pro.local',\n", - " 'username': 'daniel',\n", - " 'qcengine_version': 'v0.5.2+23.g6c46911',\n", - " 'wall_time': 0.6345179080963135,\n", - " 'creator': 'QCEngine',\n", - " 'version': 'v0.5.2+23.g6c46911'}" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ret[\"provenance\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'creator': 'rdkit', 'version': '2018.03.4', 'routine': 'rdkit.Chem.AllChem.UFFGetMoleculeForceField', 'cpu': 'Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz', 'wall_time': 0.003949165344238281, 'qcengine_version': 'v0.5.2+23.g6c46911', 'username': 'daniel', 'hostname': 'Daniels-MacBook-Pro.local'}\n", - "{'return_energy': 4.05209848561957e-05}\n" - ] - } - ], - "source": [ - "print(ret[\"trajectory\"][0][\"provenance\"])\n", - "print(ret[\"trajectory\"][0][\"properties\"])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 35db5675e..000000000 --- a/examples/README.md +++ /dev/null @@ -1,8 +0,0 @@ -Examples -======== - -Work in progress! - -This folder contains a collection of working examples. - - diff --git a/examples/terachem_pbs.py b/examples/terachem_pbs.py deleted file mode 100644 index a2e041d65..000000000 --- a/examples/terachem_pbs.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -import qcengine as qcng -import qcelemental as qcel - -os.environ["TERACHEM_PBS_HOST"] = "127.0.0.1" -os.environ["TERACHEM_PBS_PORT"] = "11111" - -prog = qcng.get_program("terachem_pbs") - - -mol = qcel.models.Molecule.from_data( - """ - O 0.0 0.000 -0.129 - H 0.0 -1.494 1.027 - H 0.0 1.494 1.027 -""" -) - -inp = qcel.models.AtomicInput( - molecule=mol, - driver="energy", - model={"method": "pbe0", "basis": "6-31g"}, -) -ret = prog.compute(inp) -print(ret) \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 000000000..cb0808a25 --- /dev/null +++ b/genindex.html @@ -0,0 +1,483 @@ + + + + + + Index — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ B + | C + | E + | F + | G + | H + | I + | J + | L + | M + | N + | P + | Q + | R + | S + | T + | U + +
+

B

+ + +
+ +

C

+ + + +
+ +

E

+ + + +
+ +

F

+ + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + +
+ +

J

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

P

+ + +
+ +

Q

+ + + +
    +
  • + qcengine + +
  • +
  • + qcengine.config + +
  • +
    +
  • + qcengine.programs + +
  • +
  • + qcengine.util + +
  • +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ + + +
+
+
+ +
+ +
+

© Copyright 2018-2024, The Molecular Sciences Software Institute.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..07657cb5d --- /dev/null +++ b/index.html @@ -0,0 +1,312 @@ + + + + + + + QCEngine — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

QCEngine

+

Quantum chemistry program executor and IO standardizer (QCSchema) for quantum chemistry.

+
+

Program Execution

+

A simple example of QCEngine’s capabilities is as follows:

+
>>> import qcengine as qcng
+>>> import qcelemental as qcel
+
+>>> mol = qcel.models.Molecule.from_data("""
+>>>     O  0.0  0.000  -0.129
+>>>     H  0.0 -1.494  1.027
+>>>     H  0.0  1.494  1.027
+>>> """)
+
+>>> model = qcel.models.AtomicInput(
+>>>     molecule=mol,
+>>>     driver="energy",
+>>>     model={"method": "SCF", "basis": "sto-3g"},
+>>>     keywords={"scf_type": "df"}
+>>> )
+
+
+

These input specifications can be executed with the compute syntax along with a program specifier:

+
>>> ret = qcng.compute(model, "psi4")
+
+
+

The results contain a complete record of the computation:

+
>>> ret.return_result
+-74.45994963230625
+
+>>> ret.properties.scf_dipole_moment
+[0.0, 0.0, 0.6635967188869244]
+
+>>> ret.provenance.cpu
+Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
+
+
+
+
+

Backends

+

Currently available compute backends for single results are as follow:

+ +

In addition, several procedures are available:

+ +
+
+

Configuration Determination

+

In addition, QCEngine can automatically determine the following quantites:

+
    +
  • The number of physical cores on the system and to use.

  • +
  • The amount of physical memory on the system and the amount to use.

  • +
  • The provenance of a computation (hardware, software versions, and compute resources).

  • +
  • Location of scratch disk space.

  • +
  • Location of quantum chemistry programs binaries or Python modules.

  • +
+

Each of these options can be specified by the user as well.

+
>>> qcng.get_config()
+<JobConfig ncores=2 memory=2.506 scratch_directory=None>
+
+>>> qcng.get_config(task_config={"scratch_directory": "/tmp"})
+<JobConfig ncores=2 memory=2.506 scratch_directory='/tmp'>
+
+>>> os.environ["SCRATCH"] = "/my_scratch"
+>>> qcng.get_config(task_config={"scratch_directory": "$SCRATCH"})
+<JobConfig ncores=2 memory=2.506 scratch_directory='/my_scratch'>
+
+
+
+
+

Program and Procedure Information

+

Available programs and procedures may be printed using the CLI:

+
>>> qcengine info
+>> Version information
+QCEngine version:    v0.11.0
+QCElemental version: v0.11.0
+
+>> Program information
+Available programs:
+mopac v2016
+psi4 v1.3.2
+rdkit v2019.03.4
+
+Other supported programs:
+cfour dftd3 entos gamess molpro mp2d nwchem terachem torchani
+...
+
+
+
+
+
+
+
+

Index

+

Getting Started

+ +
+
+

User Interface

+ +
+
+

Programs

+ +
+
+

Developer Documentation

+ +
+

Developer Documentation

+ +
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2018-2024, The Molecular Sciences Software Institute.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/install.html b/install.html new file mode 100644 index 000000000..3ab65205d --- /dev/null +++ b/install.html @@ -0,0 +1,199 @@ + + + + + + + Install QCEngine — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Install QCEngine

+

You can install qcengine with conda or with pip.

+
+

Conda

+

You can install qcengine using conda:

+
>>> conda install qcengine -c conda-forge
+
+
+

This installs QCEngine and its dependencies. The qcengine package is maintained on the +conda-forge channel.

+
+
+

Pip

+

You can also install QCEngine using pip:

+
>>> pip install qcengine
+
+
+
+
+

Test the Installation

+
+

Note

+

QCEngine is a wrapper for other quantum chemistry codes. The tests for QCEngine will only test the wrapper for a +given code if its detected in the $PATH or current Python Environment, otherwise the tests for that package are +skipped. Keep this in mind if you see many skip or s codes output from PyTest.

+
+

You can test to make sure that Engine is installed correctly by first installing pytest.

+

From conda:

+
>>> conda install pytest -c conda-forge
+
+
+

From pip:

+
>>> pip install pytest
+
+
+

Then, run the following command:

+
>>> pytest --pyargs qcengine
+
+
+
+
+

Developing from Source

+

If you are a developer and want to make contributions Engine, you can access the source code from +github.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 000000000..b39ab7302 Binary files /dev/null and b/objects.inv differ diff --git a/program_overview.html b/program_overview.html new file mode 100644 index 000000000..2d8bf0698 --- /dev/null +++ b/program_overview.html @@ -0,0 +1,402 @@ + + + + + + + Program Overview — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Program Overview

+

The general capabilities available through QCEngine for each program can be +found below:

+
+

Quantum Chemistry

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Program

Production

E

G

H

Properties

Wavefunction

adcc

CFOUR

Qcore (Entos)

GAMESS

MRChem

Molpro

NWChem

Psi4

Q-Chem

Terachem

Terachem PBS

Turbomole

+
+
+

Semi-Empirical

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Program

Production

E

G

H

Properties

Wavefunction

MOPAC

xtb

+
+
+

AI Potential

+ + + + + + + + + + + + + + + + + + + +

Program

Production

E

G

H

Properties

TorchANI

+
+
+

Molecular Mechanics

+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Program

Production

E

G

H

Properties

OpenMM

RDKit

+
+
+

Analytical Corrections

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Program

Production

E

G

H

Properties

DFTD3

s-DFTD3

DFTD4

gCP

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/programs_molecular_mechanics.html b/programs_molecular_mechanics.html new file mode 100644 index 000000000..846afdffa --- /dev/null +++ b/programs_molecular_mechanics.html @@ -0,0 +1,246 @@ + + + + + + + Molecular Mechanics — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Molecular Mechanics

+

For Molecular Mechanics (MM) engines to fit the AtomicInput/Result schema +the following convention is used:

+
    +
  • Method: The force field used such as MMFF94, GAFF, OpenFF-1.0.0.

  • +
  • Basis: The typing engine used to find the required paramters.

  • +
+

For all MM computations the input Molecule object must have connectivity and this will not be automatically assigned for you.

+
+

Example

+
>>> mol = qcel.models.Molecule(
+>>>     symbols=["O", "H", "H"],
+>>>     geometry=[[0, 0, 0], [0, 0, 2], [0, 2, 0]],
+>>>     connectivity=[[0, 1, 1], [0, 2, 1]],
+>>> )
+
+>>> model = qcel.models.AtomicInput(
+>>>     molecule=mol,
+>>>     driver="energy",
+>>>     model={"method": "openff-1.0.0", "basis": "smirnoff"},
+>>> )
+>>> ret = qcng.compute(model, "openmm")
+>>> ret.return_result
+0.011185654397410195
+
+
+
+
+

OpenMM

+

Currently OpenMM only supports the smirnoff typing engine from the +openff-toolkit. Currently available force fields are the following:

+ + + + + + + + + + + + + + + + + +

Method

Basis

smirnoff99Frosst-1.1.0

smirnoff

openff-1.0.0

smirnoff

openff_unconstrained-1.0.0

smirnoff

+

Other forcefields may be available depending on your version of the openff-toolkit, see their docs for more information.

+
+
+

RDKit

+

RDKit force fields currently do not require a typing engine and the basis is omitted in all computations. Currently available force fields are the following:

+ + + + + + + + + + + + + + + + + +

Method

Basis

UFF

None

MMFF94

None

MMFF94s

None

+
+
+

xtb

+

Experimental access to force fields are available with the xtb engine. +Note that the xtb engine will not require nor use a topology information provided in the input schema.

+ + + + + + + + + + + + + +

Method

Basis

Reference

GFN-FF

None

10.1002/anie.202004239

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/programs_semiempirical.html b/programs_semiempirical.html new file mode 100644 index 000000000..7a5ca8b03 --- /dev/null +++ b/programs_semiempirical.html @@ -0,0 +1,271 @@ + + + + + + + Semiempirical Quantum Mechanics — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Semiempirical Quantum Mechanics

+

For semiempirical quantum mechanics (SQM) engines to fit the AtomicInput/Result schema the following convention is used:

+
    +
  • Method: The unique method name (PM7 or GFN2-xTB) including the parametrisation information is provided, no basis is needed

  • +
+

As for quantum mechanical methods a minimal Molecule object is sufficient as input.

+
+

Note

+

Semiemprical engines might not handle the concept of ghost atoms correctly, check carefully how the used engine handles ghost atoms. +To be sure remove ghost atoms from input to semiempirical engines beforehand.

+
+
+

Example

+

For example, running a calculation with the GFN2-xTB method using the xtb engine would work like any other QM engine with

+
>>> import qcelemental as qcel
+>>> mol = qcel.models.Molecule(
+...     symbols=["O", "H", "H"],
+...     geometry=[
+...         [ 0.00000000000000, 0.00000000000000,-0.73578586109551],
+...         [ 1.44183152868459, 0.00000000000000, 0.36789293054775],
+...         [-1.44183152868459, 0.00000000000000, 0.36789293054775],
+...     ],
+... )
+...
+>>> model = qcel.models.AtomicInput(
+...     molecule=mol,
+...     driver="energy",
+...     model={"method": "GFN2-xTB"},
+... )
+...
+>>> import qcengine as qcng
+>>> ret = qcng.compute(model, "xtb")
+>>> ret.return_result
+-5.070451354836705
+
+
+
+
+

MOPAC

+

The following semiempirical Hamiltonians are supported with the MOPAC engine.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Method

Basis

mndo

None

am1

None

pm3

None

rm1

None

mndod

None

pm6

None

pm6-d3

None

pm6-dh+

None

pm6-dh2

None

pm6-dh2x

None

pm6-d3h4

None

pm6-3dh4x

None

pm7

None

pm7-ts

None

+
+
+

xtb

+

The following extended tight binding Hamiltonians are available with the xtb engine.

+ + + + + + + + + + + + + + + + + + + + + +

Method

Basis

Reference

GFN2-xTB

None

10.1021/acs.jctc.8b01176

GFN1-xTB

None

10.1021/acs.jctc.7b00118

GFN0-xTB

None

10.26434/chemrxiv.8326202.v1

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 000000000..3ddbd10f9 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,175 @@ + + + + + + Python Module Index — QCEngine 0+untagged.1.g3b9ed2a documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ q +
+ + + + + + + + + + + + + + + + +
 
+ q
+ qcengine +
    + qcengine.config +
    + qcengine.programs +
    + qcengine.util +
+ + +
+
+
+ +
+ +
+

© Copyright 2018-2024, The Molecular Sciences Software Institute.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 2fa981a2d..000000000 --- a/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[tool.black] -line-length = 120 -target-version = ['py37', 'py38'] diff --git a/qcengine/__init__.py b/qcengine/__init__.py deleted file mode 100644 index 69af126aa..000000000 --- a/qcengine/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Base file for the dqm_compute module. -""" - -from . import config, exceptions -from .compute import compute, compute_procedure -from .config import get_config -from .extras import get_information -from .mdi_server import MDIServer -from .procedures import get_procedure, list_all_procedures, list_available_procedures -from .programs import get_program, list_all_programs, list_available_programs, register_program, unregister_program -from .stock_mols import get_molecule - -# Handle versioneer -__version__ = get_information("version") -__git_revision__ = get_information("git_revision") -del get_information diff --git a/qcengine/__main__.py b/qcengine/__main__.py deleted file mode 100644 index 5a13729a8..000000000 --- a/qcengine/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -import qcengine.cli - -qcengine.cli.main() diff --git a/qcengine/_version.py b/qcengine/_version.py deleted file mode 100644 index 8321564da..000000000 --- a/qcengine/_version.py +++ /dev/null @@ -1,533 +0,0 @@ -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (built by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.18 (https://github.com/warner/python-versioneer) -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - git_date = "$Format:%ci$" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440" - cfg.tag_prefix = "" - cfg.parentdir_prefix = "None" - cfg.versionfile_source = "qcengine/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -def register_vcs_handler(vcs, method): # decorator - """Decorator to mark a method as the handler for a particular VCS.""" - - def decorate(f): - """Store f in HANDLERS[vcs][method].""" - if vcs not in HANDLERS: - HANDLERS[vcs] = {} - HANDLERS[vcs][method] = f - return f - - return decorate - - -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - try: - dispcmd = str([c] + args) - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen( - [c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None) - ) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = p.communicate()[0].strip() - if sys.version_info[0] >= 3: - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, p.returncode - return stdout, p.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - for i in range(3): - dirname = os.path.basename(root) - if dirname.startswith(parentdir_prefix): - return { - "version": dirname[len(parentdir_prefix) :], - "full-revisionid": None, - "dirty": False, - "error": None, - "date": None, - } - else: - rootdirs.append(root) - root = os.path.dirname(root) # up a level - - if verbose: - print("Tried directories %s but none started with prefix %s" % (str(rootdirs), parentdir_prefix)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r"\d", r)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix) :] - if verbose: - print("picking %s" % r) - return { - "version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": None, - "date": date, - } - # no suitable tags, so version is "0+unknown", but full hex is still there - if verbose: - print("no suitable tags, using unknown + full revision id") - return { - "version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, - "error": "no suitable tags", - "date": None, - } - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) - if rc != 0: - if verbose: - print("Directory %s not under git control" % root) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] - # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command( - GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root - ) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if dirty: - git_describe = git_describe[: git_describe.rindex("-dirty")] - - # now we have TAG-NUM-gHEX or HEX - - if "-" in git_describe: - # TAG-NUM-gHEX - mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - if verbose: - fmt = "tag '%s' doesn't start with prefix '%s'" - print(fmt % (full_tag, tag_prefix)) - pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix) :] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) - pieces["distance"] = int(count_out) # total number of commits - - # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Eexceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render(pieces, style): - """Render the given version pieces into the requested style.""" - if pieces["error"]: - return { - "version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None, - } - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return { - "version": rendered, - "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], - "error": None, - "date": pieces.get("date"), - } - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for i in cfg.versionfile_source.split("/"): - root = os.path.dirname(root) - except NameError: - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None, - } - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return { - "version": "0+unknown", - "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", - "date": None, - } diff --git a/qcengine/cli.py b/qcengine/cli.py deleted file mode 100644 index 70e0e39f3..000000000 --- a/qcengine/cli.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -Provides a CLI for QCEngine -""" - -import argparse -import json -import os.path -import sys -from typing import Any, Dict - -from . import get_program # run and run-procedure; info -from . import ( - __version__, - compute, - compute_procedure, - get_procedure, - list_all_procedures, - list_all_programs, - list_available_procedures, - list_available_programs, -) -from .config import global_repr # info - -__all__ = ["main"] - -info_choices = frozenset(["version", "programs", "procedures", "config", "all"]) - - -def parse_args(): - parser = argparse.ArgumentParser(description="A CLI for the QCEngine.") - parser.add_argument("--version", action="version", version=f"{__version__}") - - parent_parser = argparse.ArgumentParser(add_help=False) - task_group = parent_parser.add_argument_group( - "Task Configuration", "Extra configuration related to running the computation" - ) - task_group.add_argument("--ncores", type=int, help="The number of cores to use for the task") - task_group.add_argument("--nnodes", type=int, help="The number of nodes to use") - task_group.add_argument("--memory", type=float, help="The amount of memory (in GiB) to use") - task_group.add_argument("--scratch-directory", type=str, help="Where to store temporary files") - task_group.add_argument("--retries", type=int, help="Number of retries for random failures") - task_group.add_argument("--mpiexec-command", type=str, help="Command used to launch MPI tasks") - task_group.add_argument( - "--use-mpiexec", - action="store_true", - default=None, - help="Whether it is necessary to use MPI to run an executable", - ) - task_group.add_argument("--cores-per-rank", type=int, help="Number of cores per MPI rank") - task_group.add_argument( - "--scratch-messy", - action="store_true", - default=None, - help="Leave the scratch directory and contents on disk after completion", - ) - - subparsers = parser.add_subparsers(dest="command") - - info = subparsers.add_parser("info", help="Print information about QCEngine setup, version, and environment.") - info.add_argument( - "category", nargs="*", default="all", choices=info_choices, help="The information categories to show." - ) - - run = subparsers.add_parser( - "run", parents=[parent_parser], help="Run a program on a given task. Output is printed as a JSON blob." - ) - run.add_argument("program", type=str, help="The program to run.") - run.add_argument( - "data", - type=str, - help="Data describing the task to run. " - "One of: (i) A JSON blob, " - "(ii) A file name, " - "(iii) '-', indicating data will be read from STDIN.", - ) - - run_procedure = subparsers.add_parser( - "run-procedure", - parents=[parent_parser], - help="Run a procedure on a given task. Output is printed as a JSON blob.", - ) - run_procedure.add_argument("procedure", type=str, help="The procedure to run.") - run_procedure.add_argument( - "data", - type=str, - help="Data describing the task to run. " - "One of: (i) A JSON blob, " - "(ii) A file name, " - "(iii) '-', indicating data will be read from STDIN.", - ) - - args = vars(parser.parse_args()) - if args["command"] is None: - parser.print_help(sys.stderr) - exit(1) - - return args - - -def info_cli(args): - def info_version(): - import qcelemental - - print(">>> Version information") - print(f"QCEngine: {__version__}") - print(f"QCElemental: {qcelemental.__version__}") - print() - - def info_programs(): # lgtm: [py/similar-function] - print(">>> Program information") - all_progs = list_all_programs() - avail_progs = list_available_programs() - print("Available programs:") - for prog_name in sorted(avail_progs): - program = get_program(prog_name) - version = program.get_version() - for loc, ver in program.version_cache.items(): - if ver == version: - which = loc - break - else: - which = "???" - if version is None: - version = "???" - print(f"{prog_name + ':':12} v{version:20} {which}") - - print() - print("Other supported programs:") - print(" ".join(sorted(all_progs - avail_progs))) - print() - print( - """If you think available programs are missing, query for details: `python -c "import qcengine as qcng; qcng.get_program('')"`""" - ) - print() - - def info_procedures(): # lgtm: [py/similar-function] - print(">>> Procedure information") - all_procs = list_all_procedures() - avail_procs = list_available_procedures() - print("Available procedures:") - for proc_name in sorted(avail_procs): - version = get_procedure(proc_name).get_version() - if version is None: - version = "???" - print(f"{proc_name} v{version}") - - print() - print("Other supported procedures:") - print(" ".join(sorted(all_procs - avail_procs))) - print() - - # default=["all"] does is not allowed by argparse - if not isinstance(args["category"], list): - args["category"] = [args["category"]] - cat = set(args["category"]) - - if "version" in cat or "all" in cat: - info_version() - if "programs" in cat or "all" in cat: - info_programs() - if "procedures" in cat or "all" in cat: - info_procedures() - if "config" in cat or "all" in cat: - print(">>> Configuration information") - print() - print(global_repr()) - - -def data_arg_helper(data_arg: str) -> Dict[str, Any]: - """ - Converts the data argument of run and run-procedure commands to a dict for compute or compute_procedure - - Parameters - ---------- - data_arg: str - Either a data blob or file name or '-' for STDIN - - Returns - ------- - Dict[str, Any] - An input for compute or compute_procedure. - """ - if data_arg == "-": - return json.load(sys.stdin) - elif os.path.isfile(data_arg): - return json.load(open(data_arg)) - else: - return json.loads(data_arg) - - -def main(args=None): - # Grab CLI args if not present - if args is None: - args = parse_args() - - # Break out a task config - task_config = { - "ncores": args.pop("ncores", None), - "memory": args.pop("memory", None), - "nnodes": args.pop("nnodes", None), - "scratch_directory": args.pop("scratch_directory", None), - "retries": args.pop("retries", None), - "mpiexec_command": args.pop("mpiexec_command", None), - "use_mpiexec": args.pop("use_mpiexec", None), - "cores_per_rank": args.pop("cores_per_rank", None), - "scratch_messy": args.pop("scratch_messy", None), - } - - # Prune None values and let other config functions handle defaults - task_config = {k: v for k, v in task_config.items() if v is not None} - - command = args.pop("command") - if command == "info": - info_cli(args) - elif command == "run": - ret = compute(data_arg_helper(args["data"]), args["program"], task_config=task_config) - print(ret.json()) - elif command == "run-procedure": - ret = compute_procedure(data_arg_helper(args["data"]), args["procedure"], task_config=task_config) - print(ret.json()) diff --git a/qcengine/compute.py b/qcengine/compute.py deleted file mode 100644 index 00e94a329..000000000 --- a/qcengine/compute.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Integrates the computes together -""" -import warnings -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from qcelemental.models import AtomicInput, AtomicResult, FailedOperation, OptimizationResult - -from .config import get_config -from .exceptions import InputError, RandomError -from .procedures import get_procedure -from .programs import get_program -from .util import compute_wrapper, environ_context, handle_output_metadata, model_wrapper - -if TYPE_CHECKING: - try: - from pydantic.v1.main import BaseModel - except ImportError: - from pydantic.main import BaseModel - from qcelemental.models import AtomicResult - - -__all__ = ["compute", "compute_procedure"] - - -def _process_failure_and_return(model, return_dict, raise_error): - if isinstance(model, FailedOperation): - if raise_error: - raise InputError(model.error.error_message) - elif return_dict: - return model.dict() - else: - return model - else: - return False - - -def compute( - input_data: Union[Dict[str, Any], "AtomicInput"], - program: str, - raise_error: bool = False, - task_config: Optional[Dict[str, Any]] = None, - local_options: Optional[Dict[str, Any]] = None, - return_dict: bool = False, -) -> Union["AtomicResult", "FailedOperation", Dict[str, Any]]: - """Executes a single CMS program given a QCSchema input. - - The full specification can be found at: - http://molssi-qc-schema.readthedocs.io/en/latest/index.html# - - Parameters - ---------- - input_data - A QCSchema input specification in dictionary or model from QCElemental.models - program - The CMS program with which to execute the input. - raise_error - Determines if compute should raise an error or not. - retries : int, optional - The number of random tries to retry for. - task_config - A dictionary of local configuration options corresponding to a TaskConfig object. - local_options - Deprecated parameter, renamed to ``task_config`` - return_dict - Returns a dict instead of qcelemental.models.AtomicResult - - Returns - ------- - result - AtomicResult, FailedOperation, or Dict representation of either object type - A QCSchema representation of the requested output, type depends on return_dict key. - """ - - output_data = input_data.copy() # lgtm [py/multiple-definition] - with compute_wrapper(capture_output=False, raise_error=raise_error) as metadata: - - # Grab the executor and build the input model - executor = get_program(program) - - # Build the model and validate - input_data = model_wrapper(input_data, AtomicInput) - - # Build out task_config - if task_config is None: - task_config = {} - - if local_options: - warnings.warn( - "Using the `local_options` keyword argument is deprecated in favor of using `task_config`, " - "in version 0.30.0 it will stop working.", - category=FutureWarning, - stacklevel=2, - ) - task_config = {**local_options, **task_config} - - input_engine_options = input_data.extras.pop("_qcengine_local_config", {}) - - task_config = {**task_config, **input_engine_options} - config = get_config(task_config=task_config) - - # Set environment parameters and execute - with environ_context(config=config): - - # Handle optional retries - for x in range(config.retries + 1): - try: - output_data = executor.compute(input_data, config) - break - except RandomError as e: - - if x == config.retries: - raise e - else: - metadata["retries"] += 1 - except: - raise - - return handle_output_metadata(output_data, metadata, raise_error=raise_error, return_dict=return_dict) - - -def compute_procedure( - input_data: Union[Dict[str, Any], "BaseModel"], - procedure: str, - raise_error: bool = False, - task_config: Optional[Dict[str, str]] = None, - local_options: Optional[Dict[str, str]] = None, - return_dict: bool = False, -) -> Union["OptimizationResult", "FailedOperation", Dict[str, Any]]: - """Runs a procedure (a collection of the quantum chemistry executions) - - Parameters - ---------- - input_data : dict or qcelemental.models.OptimizationInput - A JSON input specific to the procedure executed in dictionary or model from QCElemental.models - procedure : {"geometric", "berny"} - The name of the procedure to run - raise_error : bool, option - Determines if compute should raise an error or not. - task_config - A dictionary of local configuration options corresponding to a TaskConfig object. - local_options - Deprecated parameter, renamed to ``task_config`` - return_dict : bool, optional, default True - Returns a dict instead of qcelemental.models.AtomicInput - - Returns - ------ - dict, OptimizationResult, FailedOperation - A QC Schema representation of the requested output, type depends on return_dict key. - """ - # Build out task_config - if task_config is None: - task_config = {} - - if local_options: - warnings.warn( - "Using the `local_options` keyword argument is depreciated in favor of using `task_config`, " - "in version 0.30.0 it will stop working.", - category=FutureWarning, - stacklevel=2, - ) - task_config = {**local_options, **task_config} - - output_data = input_data.copy() # lgtm [py/multiple-definition] - with compute_wrapper(capture_output=False, raise_error=raise_error) as metadata: - - # Grab the executor and build the input model - executor = get_procedure(procedure) - - config = get_config(task_config=task_config) - input_data = executor.build_input_model(input_data) - - # Create a base output data in case of errors - output_data = input_data.copy() # lgtm [py/multiple-definition] - - # Set environment parameters and execute - with environ_context(config=config): - output_data = executor.compute(input_data, config) - - return handle_output_metadata(output_data, metadata, raise_error=raise_error, return_dict=return_dict) diff --git a/qcengine/config.py b/qcengine/config.py deleted file mode 100644 index a836ddbe9..000000000 --- a/qcengine/config.py +++ /dev/null @@ -1,362 +0,0 @@ -""" -Creates globals for the qcengine module -""" - -import fnmatch -import getpass -import logging -import os -import socket -from typing import Any, Dict, Optional, Union - -try: - import pydantic.v1 as pydantic -except ImportError: - import pydantic - -from .extras import get_information - -__all__ = ["get_config", "get_provenance_augments", "global_repr", "NodeDescriptor"] - -# Start a globals dictionary with small starting values -_global_values = None -NODE_DESCRIPTORS = {} -LOGGER = logging.getLogger("QCEngine") -LOGGER.setLevel(logging.CRITICAL) - - -# Generic globals -def get_global(key: Optional[str] = None) -> Union[str, Dict[str, Any]]: - import cpuinfo - import psutil - - # TODO (wardlt): Implement a means of getting CPU information from compute nodes on clusters for MPI tasks - # The QC code runs on a different node than the node running this Python function, which may have different info - - global _global_values - if _global_values is None: - _global_values = {} - _global_values["hostname"] = socket.gethostname() - _global_values["memory"] = round(psutil.virtual_memory().available / (1024**3), 3) - _global_values["username"] = getpass.getuser() - - # Work through VMs and logical cores. - if hasattr(psutil.Process(), "cpu_affinity"): - cpu_cnt = len(psutil.Process().cpu_affinity()) - full_physical_cnt = psutil.cpu_count(logical=False) - full_logical_cnt = psutil.cpu_count(logical=True) - if (cpu_cnt == full_logical_cnt) and (full_physical_cnt is not None): - # cpu_affinity isn't capturing deliberate setting but just VMs, so use physical - cpu_cnt = full_physical_cnt - else: - cpu_cnt = psutil.cpu_count(logical=False) - if cpu_cnt is None: - cpu_cnt = psutil.cpu_count(logical=True) - - _global_values["ncores"] = cpu_cnt - _global_values["nnodes"] = 1 - - _global_values["cpuinfo"] = cpuinfo.get_cpu_info() - try: - _global_values["cpu_brand"] = _global_values["cpuinfo"]["brand_raw"] - except KeyError: - # Remove this if py-cpuinfo is pinned to >=6.0.0 - _global_values["cpu_brand"] = _global_values["cpuinfo"].get("brand", "(unknown)") - - if key is None: - return _global_values.copy() - else: - return _global_values[key] - - -class NodeDescriptor(pydantic.BaseModel): - """ - Description of an individual node - """ - - # Host data - hostname_pattern: str - name: str - scratch_directory: Optional[str] = None # What location to use as scratch - - memory: Optional[float] = None - memory_safety_factor: int = 10 # Percentage of memory as a safety factor - - # Specifications - ncores: Optional[int] = pydantic.Field( - None, - description="""Number of cores accessible to each task on this node - - The default value, ``None``, will allow QCEngine to autodetect the number of cores.""", - ) - jobs_per_node: int = 1 - retries: int = 0 - - # Cluster options - is_batch_node: bool = pydantic.Field( - False, - help="""Whether the node running QCEngine is a batch node - - Some clusters are configured such that tasks are launched from a special "batch" or "MOM" onto the compute nodes. - The compute nodes on such clusters often have a different CPU architecture than the batch nodes and - often are unable to launch MPI tasks, which has two implications: - 1) QCEngine must make *all* calls to an executable via ``mpirun`` because the executables might not - be able to run on the batch node. - 2) QCEngine must run on the batch node to be able to launch tasks on the more than one compute nodes - - ``is_batch_node`` is used when creating the task configuration as a means of determining whether - ``mpiexec_command`` must always be used even for serial jobs (e.g., getting the version number) - """, - ) - mpiexec_command: Optional[str] = pydantic.Field( - None, - description="""Invocation for launching node-parallel tasks with MPI - - The invocation need not specify the number of nodes, tasks, or cores per node. - Information about the task configuration will be added to the command by use of - Python's string formatting. The configuration will be supplied as the following variables: - - {nnodes} - Number of nodes - {ranks_per_node} - Number of MPI ranks per node - {cores_per_rank} - Number of cores to use for each MPI rank - {total_ranks} - Total number of MPI ranks - - As examples, the ``aprun`` command on Cray systems should be similar to - ``aprun -n {total_ranks} -N {ranks_per_node}`` and ``mpirun`` from OpenMPI should - be similar to ``mpirun -np {total_ranks} -N {ranks_per_node}``. - - Programs where each MPI rank can use multiple threads (e.g., QC programs with MPI+OpenMP) can - use the {cores_per_rank} option to control the hybrid parallelism. - As an example, the Cray ``aprun`` command using this figure could be: - ``aprun -n {total_ranks} -N {ranks_per_node} -d {cores_per_rank} -j 1``. - The appropriate number of ranks per node will be determined based on the number of - cores per node and the number of cores per rank. - """, - ) - - def __init__(self, **data: Dict[str, Any]): - data = parse_environment(data) - super().__init__(**data) - - if self.mpiexec_command is not None: - # Ensure that the mpiexec_command contains necessary information - if not ("{total_ranks}" in self.mpiexec_command or "{nnodes}" in self.mpiexec_command): - raise ValueError("mpiexec_command must contain either {total_ranks} or {nnodes}") - if "{ranks_per_node}" not in self.mpiexec_command: - raise ValueError("mpiexec_command must explicitly state the number of ranks per node") - - class Config: - extra = "forbid" - - -class TaskConfig(pydantic.BaseSettings): - """Description of the configuration used to launch a task.""" - - # Specifications - ncores: int = pydantic.Field(None, description="Number cores per task on each node") - nnodes: int = pydantic.Field(None, description="Number of nodes per task") - memory: float = pydantic.Field( - None, description="Amount of memory in GiB (2^30 bytes; not GB = 10^9 bytes) per node." - ) - scratch_directory: Optional[str] # What location to use as scratch - retries: int # Number of retries on random failures - mpiexec_command: Optional[str] # Command used to launch MPI tasks, see NodeDescriptor - use_mpiexec: bool = False # Whether it is necessary to use MPI to run an executable - cores_per_rank: int = pydantic.Field(1, description="Number of cores per MPI rank") - scratch_messy: bool = pydantic.Field( - False, description="Leave scratch directory and contents on disk after completion." - ) - - class Config(pydantic.BaseSettings.Config): - extra = "forbid" - env_prefix = "QCENGINE_" - - -def _load_defaults() -> None: - """ - Pulls the defaults from the QCA folder - """ - - # Find the config - load_path = None - test_paths = [os.getcwd(), os.path.join(os.path.expanduser("~"), ".qcarchive")] - - if "DQM_CONFIG_PATH" in os.environ: - test_paths.insert(0, os.environ["DQM_CONFIG_PATH"]) - - for path in test_paths: - path = os.path.join(path, "qcengine.yaml") - if os.path.exists(path): - load_path = path - break - - if load_path is None: - LOGGER.info("Could not find 'qcengine.yaml'. Searched the following paths: {}".format(", ".join(test_paths))) - LOGGER.info("Using default options...") - - else: - import yaml - - LOGGER.info("Found 'qcengine.yaml' at path: {}".format(load_path)) - with open(load_path, "r") as stream: - user_config = yaml.load(stream, Loader=yaml.SafeLoader) - - for k, v in user_config.items(): - NODE_DESCRIPTORS[k] = NodeDescriptor(name=k, **v) - - -def global_repr() -> str: - """ - A representation of the current global configuration. - """ - - ret = "" - ret += "Host information:\n" - ret += "-" * 80 + "\n" - - prov = get_provenance_augments() - for k in ["username", "hostname", "cpu"]: - ret += "{:<30} {:<30}\n".format(k, prov[k]) - - ret += "\nNode information:\n" - ret += "-" * 80 + "\n" - for k, v in get_node_descriptor(): - ret += " {:<28} {}\n".format(k, v) - - if k in ["scratch_directory", "memory_per_job"]: - ret += "\n" - - ret += "\nJob information:\n" - ret += "-" * 80 + "\n" - for k, v in get_config(): - ret += " {:<28} {}\n".format(k, v) - - ret += "-" * 80 + "\n" - - return ret - - -def get_node_descriptor(hostname: Optional[str] = None) -> NodeDescriptor: - """ - Find the correct NodeDescriptor based off current hostname - """ - if isinstance(hostname, NodeDescriptor): - return hostname - - if hostname is None: - hostname = get_global("hostname") - - # Find a match - for name, node in NODE_DESCRIPTORS.items(): - - if fnmatch.fnmatch(hostname, node.hostname_pattern): - config = node - break - else: - config = NodeDescriptor( - name="default", hostname_pattern="*", memory=get_global("memory"), ncores=get_global("ncores") - ) - - return config - - -def parse_environment(data: Dict[str, Any]) -> Dict[str, Any]: - """Collects local environment variable values into ``data`` for any keys with RHS starting with ``$``.""" - ret = {} - for k, var in data.items(): - if isinstance(var, str): - var = os.path.expanduser(os.path.expandvars(var)) - if var.startswith("$"): - var = None - - ret[k] = var - - return ret - - -def read_qcengine_task_environment() -> Dict[str, Any]: - """ - Reads the qcengine task-related environment variables and returns a dictionary of the values. - """ - - ret = {} - for k, v in os.environ.items(): - if k.startswith("QCENGINE_"): - ret[k[9:].lower()] = v - - return ret - - -def get_config(*, hostname: Optional[str] = None, task_config: Dict[str, Any] = None) -> TaskConfig: - """ - Returns the configuration key for qcengine. - """ - - if task_config is None: - task_config = {} - - task_config_env = read_qcengine_task_environment() - task_config = {**task_config_env, **parse_environment(task_config)} - - config = {} - - # Node data - node = get_node_descriptor(hostname) - ncores = node.ncores or get_global("ncores") - config["scratch_directory"] = task_config.pop("scratch_directory", node.scratch_directory) - config["retries"] = task_config.pop("retries", node.retries) - - # Jobs per node - jobs_per_node = int(task_config.pop("jobs_per_node", None) or node.jobs_per_node) - - # Handle memory - memory = task_config.pop("memory", None) - if memory is None: - memory = node.memory or get_global("memory") - memory_coeff = 1 - node.memory_safety_factor / 100 - memory = round(memory * memory_coeff / jobs_per_node, 3) - - config["memory"] = memory - - # Get the number of cores available to each task - ncores = int(task_config.pop("ncores", int(ncores / jobs_per_node))) - if ncores < 1: - raise KeyError("Number of jobs per node exceeds the number of available cores.") - - config["ncores"] = ncores - config["nnodes"] = int(task_config.pop("nnodes", 1)) - - # Add in the MPI launch command template - config["mpiexec_command"] = node.mpiexec_command - config["use_mpiexec"] = node.is_batch_node or config["nnodes"] > 1 - config["cores_per_rank"] = task_config.get("cores_per_rank", 1) - - # Override any settings - if task_config is not None: - config.update(task_config) - - # Make sure mpirun command is defined if needed - if config["use_mpiexec"] and config["mpiexec_command"] is None: - raise ValueError( - "You need to define the mpiexec command for this node. " - "See: https://qcengine.readthedocs.io/en/stable/environment.html" - ) - - return TaskConfig(**config) - - -def get_provenance_augments() -> Dict[str, str]: - return { - "cpu": get_global("cpu_brand"), - "hostname": get_global("hostname"), - "username": get_global("username"), - "qcengine_version": get_information("version"), - } - - -def get_logger() -> "Logger": - return LOGGER - - -# Pull in the local variables -_load_defaults() diff --git a/qcengine/exceptions.py b/qcengine/exceptions.py deleted file mode 100644 index 1ac56b098..000000000 --- a/qcengine/exceptions.py +++ /dev/null @@ -1,137 +0,0 @@ -import traceback -from typing import Any, Dict, Optional - -from qcelemental.models import AtomicInput - - -class QCEngineException(Exception): - """ - Base QCEngine exception, should never be called explicitly. - """ - - error_type = "base_error" - header = "QCEngine Base Error" - - def __init__(self, message: str): - - # Call the base class constructor with the parameters it needs - super().__init__(message) - - # Now for your custom code... - self.raw_message = message - self.traceback = traceback.format_exc() - - @property - def error_message(self) -> str: - return f"{self.header}: {self.raw_message}" - - -class UnknownError(QCEngineException): - """ - Unknown QCEngine error, the type was not able to be specified. - """ - - error_type = "unknown_error" - header = "QCEngine Unknown Error" - - -class InputError(QCEngineException): - """ - Incorrect user parameters, not recoverable. Also may indicate a version issue. - """ - - error_type = "input_error" - header = "QCEngine Input Error" - - -class ResourceError(QCEngineException): - """ - Not enough resources for computation such as not enough memory, cores, or disk was not available. - Recoverable on different compute hardware. - """ - - error_type = "resource_error" - header = "QCEngine Resource Error" - - -class ConvergenceError(QCEngineException): - """ - Failed iteration convergence error, likely recoverable with tweaked parameters. - """ - - error_type = "convergence_error" - header = "QCEngine Convergence Error" - - -class RandomError(QCEngineException): - """ - Likely recoverable errors such as segmentation faults or disk io problems. - """ - - error_type = "random_error" - header = "QCEngine Random Error" - - -class KnownErrorException(QCEngineException): - """Base class for detecting errors in the output of a ProgramHarness - - Subclasses for this class must define `error_name` and `description`, which provide - a short name for the error used in the error correction code and a human-readable - description of the error and how it is corrected. - - Subclasses for this exception must fulfill the `detect_error` class method, - which detects if an error has occurred and raises an exception if one was detected. - When raising the exception, the `detect_error` routine can optionally provide details - about the error that are needed to correction. - """ - - error_type = "known_error" - header = "QCEngine Known Error" - - error_name: str # Unique name for this error. Used in autodetection logic - description: str # Human-readable description of the error. - details: Optional[Dict[str, Any]] # Any details for this error. Used for user feedback and autocorrection logic - - def __init__(self, details: Optional[dict] = None): - super().__init__(self.description) - self.details = details - - @classmethod - def detect_error(cls, outputs: Dict[str, str]): - """Detect whether this operation throws an error - Args: - outputs (dict): Output files needed to check for errors - Raises: - KnownErrorException: Error, if one was detected - """ - raise NotImplementedError() - - def create_keyword_update(self, input_data: AtomicInput) -> Dict[str, Any]: - """Create an keyword used to the update the dictionary given observed error - - Parameters - ---------- - input_data - Input specification used to perform the calculation that failed - - Returns - ------- - Dictionary used to update the keywords field of ``input_data`` - """ - raise NotImplementedError() - - -class SimpleKnownErrorException(KnownErrorException): - """Subclass for errors with simple detection logic. - - Most useful for error types that do not need any additional details to correct.""" - - @classmethod - def detect_error(cls, outputs: Dict[str, str]): - if cls._detect(outputs): - raise cls() - - @classmethod - def _detect(cls, outputs: Dict[str, str]) -> bool: - """Detect whether an error is present""" - raise NotImplementedError() diff --git a/qcengine/extras.py b/qcengine/extras.py deleted file mode 100644 index b55130aee..000000000 --- a/qcengine/extras.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Misc information and runtime information. -""" - -import re - -from . import _version - -__all__ = ["get_information", "provenance_stamp"] - -versions = _version.get_versions() - -__info = {"version": versions["version"], "git_revision": versions["full-revisionid"]} - - -def get_information(key): - """ - Obtains a variety of runtime information about QCEngine. - """ - key = key.lower() - if key not in __info: - raise KeyError("Information key '{}' not understood.".format(key)) - - return __info[key] - - -def provenance_stamp(routine): - """Return dictionary satisfying QCSchema, - https://github.com/MolSSI/QCSchema/blob/master/qcschema/dev/definitions.py#L23-L41 - with QCEngine's credentials for creator and version. The - generating routine's name is passed in through `routine`. - - """ - return {"creator": "QCEngine", "version": get_information("version"), "routine": routine} - - -_yes = re.compile(r"^(yes|true|on|1)", re.IGNORECASE) -_no = re.compile(r"^(no|false|off|0)", re.IGNORECASE) -_der0th = re.compile(r"^(0|none|energy)", re.IGNORECASE) -_der1st = re.compile(r"^(1|first|gradient)", re.IGNORECASE) -_der2nd = re.compile(r"^(2|second|hessian)", re.IGNORECASE) -_der3rd = re.compile(r"^(3|third)", re.IGNORECASE) -_der4th = re.compile(r"^(4|fourth)", re.IGNORECASE) -_der5th = re.compile(r"^(5|fifth)", re.IGNORECASE) diff --git a/qcengine/mdi_server.py b/qcengine/mdi_server.py deleted file mode 100644 index 90fff9e32..000000000 --- a/qcengine/mdi_server.py +++ /dev/null @@ -1,487 +0,0 @@ -"""Support for using QCEngine as an MDI engine. -For details regarding MDI, see https://molssi.github.io/MDI_Library/html/index.html. - -""" -from typing import Any, Dict, List, Optional - -import numpy as np -import qcelemental as qcel -from qcelemental.util import which_import - -from .compute import compute - -try: - from mdi import ( - MDI_CHAR, - MDI_COMMAND_LENGTH, - MDI_DOUBLE, - MDI_INT, - MDI_MAJOR_VERSION, - MDI_Accept_Communicator, - MDI_Init, - MDI_MPI_get_world_comm, - MDI_Recv, - MDI_Recv_Command, - MDI_Register_Command, - MDI_Register_Node, - MDI_Send, - ) - - use_mdi = True -except ImportError: - use_mdi = False - - -class MDIServer: - def __init__( - self, - mdi_options: str, - program: str, - molecule, - model, - keywords, - raise_error: bool = False, - local_options: Optional[Dict[str, Any]] = None, - ): - """Initialize an MDIServer object for communication with MDI - - Parameters - ---------- - mdi_options: str - Options used during MDI initialization. - program : str - The program to execute the input with. - molecule - The initial state of the molecule. - model - The simulation model to use. - keywords - Program-specific keywords. - raise_error : bool, optional - Determines if compute should raise an error or not. - local_options : Optional[Dict[str, Any]], optional - A dictionary of local configuration options - """ - - if not use_mdi: - raise Exception("Trying to run as an MDI engine, but the MDI Library was not found") - - if MDI_MAJOR_VERSION < 1: - raise Exception("QCEngine requires version 1.0.0 or higher of the MDI Library") - - # Confirm that the MDI library has been located - which_import("mdi", raise_error=True, raise_msg="Please install via 'conda install pymdi -c conda-forge'") - - # Initialize MDI - MDI_Init(mdi_options) - - # Input variables - self.molecule = molecule - self.model = model - self.keywords = keywords - self.program = program - self.raise_error = raise_error - self.local_options = local_options - - # The MDI interface does not currently support multiple fragments - if len(self.molecule.fragments) != 1: - raise Exception("The MDI interface does not support multiple fragments") - - # Molecule charge and multiplicity - self.total_charge = self.molecule.molecular_charge - self.multiplicity = self.molecule.molecular_multiplicity - - # Flag to track whether the latest molecule specification has been validated - self.molecule_validated = True - - # Output of most recent compute call - self.compute_return = None - - # Set whether the current energy is valid - self.energy_is_current = False - - # MPI variables - self.mpi_world = None - self.world_rank = 0 - - # Flag to stop listening for MDI commands - self.stop_listening = False - - # Dictionary of all supported MDI commands - self.commands = { - "<@": self.send_node, - "NATOMS": self.recv_natoms, - "COORDS": self.recv_coords, - "SCF": self.run_energy, - "ELEMENTS": self.recv_elements, - "MASSES": self.recv_masses, - "TOTCHARGE": self.recv_total_charge, - "ELEC_MULT": self.recv_multiplicity, - "EXIT": self.stop, - } - - # Register the @DEFAULT node - MDI_Register_Node("@DEFAULT") - - # Register all supported commands - for c in self.commands.keys(): - MDI_Register_Command("@DEFAULT", c) - - # Set the current node - self.current_node = "@DEFAULT" - - # Accept a communicator to the driver code - self.comm = MDI_Accept_Communicator() - - def update_molecule(self, key: str, value): - """Update the molecule - - Parameters - ---------- - key : str - Key of the molecular element to update - value - Update value - """ - if key == "molecular_charge" or key == "molecular_multiplicity": - # In order to validate correctly, the charges and multiplicities must be set simultaneously - try: - self.molecule = qcel.models.Molecule( - **{ - **self.molecule.dict(), - **{ - "molecular_charge": self.total_charge, - "fragment_charges": [self.total_charge], - "molecular_multiplicity": self.multiplicity, - "fragment_multiplicities": [self.multiplicity], - }, - } - ) - self.molecule_validated = True - except qcel.exceptions.ValidationError: - # The molecule didn't validate correctly, but a future >TOTCHARGE or >ELEC_MULT command might fix it - self.molecule_validated = False - else: - try: - self.molecule = qcel.models.Molecule(**{**self.molecule.dict(), **{key: value}}) - self.molecule_validated = True - except qcel.exceptions.ValidationError: - if self.molecule_validated: - # This update caused the validation error - raise Exception("MDI command caused a validation error") - - # Respond to the <@ command - def send_node(self) -> str: - """Send the name of the current node through MDI - - Returns - ------- - node : str - Name of the current node - """ - node = self.current_node - MDI_Send(node, MDI_COMMAND_LENGTH, MDI_CHAR, self.comm) - return node - - # Respond to the int: - """Send the number of atoms through MDI - - Returns - ------- - natom : int - Number of atoms - """ - natom = len(self.molecule.geometry) - MDI_Send(natom, 1, MDI_INT, self.comm) - return natom - - # Respond to the >NATOMS command - def recv_natoms(self, natoms: Optional[int] = None) -> None: - """Receive the number of atoms in the system through MDI and create a new molecule with them - - Parameters - ---------- - natoms : int, optional - New number of atoms. If None, receive through MDI. - """ - natom = len(self.molecule.geometry) - if natoms is None: - natoms = MDI_Recv(1, MDI_INT, self.comm) - - mol_string = "" - for iatom in range(natoms): - mol_string += "He " + str(1.0 * iatom) + " 0.0 0.0\n" - self.molecule = qcel.models.Molecule.from_data(mol_string) - - self.energy_is_current = False - - # Respond to the np.ndarray: - """Send the nuclear coordinates through MDI - - Returns - ------- - coords : np.ndarray - Nuclear coordinates - """ - natom = len(self.molecule.geometry) - - coords = np.reshape(self.molecule.geometry, (3 * natom)) - MDI_Send(coords, 3 * natom, MDI_DOUBLE, self.comm) - - return coords - - # Respond to the >COORDS command - def recv_coords(self, coords: Optional[np.ndarray] = None) -> None: - """Receive a set of nuclear coordinates through MDI and assign them to the atoms in the current molecule - - Parameters - ---------- - coords : np.ndarray, optional - New nuclear coordinates. If None, receive through MDI. - """ - natom = len(self.molecule.geometry) - if coords is None: - coords = np.zeros(3 * natom) - MDI_Recv(3 * natom, MDI_DOUBLE, self.comm, buf=coords) - new_geometry = np.reshape(coords, (-1, 3)) - self.molecule = qcel.models.Molecule(**{**self.molecule.dict(), **{"geometry": new_geometry}}) - self.energy_is_current = False - - # Respond to the float: - """Send the total energy through MDI - - Returns - ------- - energy : float - Energy of the system - """ - # Ensure that the molecule currently passes validation - if not self.molecule_validated: - raise Exception("MDI attempting to compute energy on an unvalidated molecule") - self.run_energy() - - # Confirm that the calculation completed successfully - if not hasattr(self.compute_return, "properties"): - raise Exception("MDI Calculation failed: \n\n" + str(self.compute_return.error.error_message)) - - properties = self.compute_return.properties.dict() - energy = properties["return_energy"] - MDI_Send(energy, 1, MDI_DOUBLE, self.comm) - return energy - - # Respond to the np.ndarray: - """Send the nuclear forces through MDI - - Returns - ------- - forces : np.ndarray - Forces on the nuclei - """ - # Ensure that the molecule currently passes validation - if not self.molecule_validated: - raise Exception("MDI attempting to compute gradients on an unvalidated molecule") - self.run_energy() - properties = self.compute_return.properties.dict() - - forces = np.reshape(-1.0 * properties["return_gradient"], (-1,)) - - if len(forces) != 3 * len(self.molecule.geometry): - raise Exception( - "MDI: The length of the forces is not what was expected. Expected: " - + str(3 * len(self.molecule.geometry)) - + " Actual: " - + str(len(forces)) - ) - - MDI_Send(forces, len(forces), MDI_DOUBLE, self.comm) - return forces - - # Respond to the SCF command - def run_energy(self) -> None: - if not self.energy_is_current: - """Ensure that the orientation of the molecule remains fixed""" - self.update_molecule("fix_com", True) - self.update_molecule("fix_orientation", True) - - """Run an energy calculation""" - input = qcel.models.AtomicInput( - molecule=self.molecule, driver="gradient", model=self.model, keywords=self.keywords - ) - self.compute_return = compute( - input_data=input, program=self.program, raise_error=self.raise_error, local_options=self.local_options - ) - - # If there is an error message, print it out - if hasattr(self.compute_return, "error"): - if self.compute_return.error is not None: - print("---------------- QCEngine Compute Error ----------------\n\n") - print(str(self.compute_return.error.error_message)) - print("\n\n-------------- End QCEngine Compute Error --------------", flush=True) - - self.energy_is_current = True - - # Respond to the ELEMENTS command - def recv_elements(self, elements: Optional[List[int]] = None): - """Receive a set of atomic numbers through MDI and assign them to the atoms in the current molecule - - Parameters - ---------- - elements : :obj:`list` of :obj:`int`, optional - New element numbers. If None, receive through MDI. - """ - natom = len(self.molecule.geometry) - if elements is None: - elements = MDI_Recv(natom, MDI_INT, self.comm) - - for iatom in range(natom): - self.molecule.symbols[iatom] = qcel.periodictable.to_symbol(elements[iatom]) - - return elements - - # Respond to the np.ndarray: - """Send the nuclear masses through MDI - - Returns - ------- - masses : np.ndarray - Atomic masses - """ - natom = len(self.molecule.geometry) - masses = self.molecule.masses - MDI_Send(masses, natom, MDI_DOUBLE, self.comm) - return masses - - # Respond to the >MASSES command - def recv_masses(self, masses: Optional[List[float]] = None) -> None: - """Receive a set of nuclear masses through MDI and assign them to the atoms in the current molecule - - Parameters - ---------- - masses : :obj:`list` of :obj:`float`, optional - New nuclear masses. If None, receive through MDI. - """ - natom = len(self.molecule.geometry) - if masses is None: - masses = MDI_Recv(natom, MDI_DOUBLE, self.comm) - self.update_molecule("masses", masses) - self.energy_is_current = False - - # Respond to the float: - """Send the total system charge through MDI - - Returns - ------- - charge : float - Total charge of the system - """ - charge = self.molecule.molecular_charge - MDI_Send(charge, 1, MDI_DOUBLE, self.comm) - return charge - - # Respond to the >TOTCHARGE command - def recv_total_charge(self, charge: Optional[float] = None) -> None: - """Receive the total system charge through MDI - - Parameters - ---------- - charge : float, optional - New charge of the system. If None, receive through MDI. - """ - if charge is None: - charge = MDI_Recv(1, MDI_DOUBLE, self.comm) - self.total_charge = charge - self.energy_is_current = False - - # Allow a validation error here, because a future >ELEC_MULT command might resolve it - try: - self.update_molecule("molecular_charge", self.total_charge) - except qcel.exceptions.ValidationError: - pass - - # Respond to the int: - """Send the electronic multiplicity through MDI - - Returns - ------- - multiplicity : int - Multiplicity of the system - """ - multiplicity = self.molecule.molecular_multiplicity - MDI_Send(multiplicity, 1, MDI_INT, self.comm) - return multiplicity - - # Respond to the >ELEC_MULT command - def recv_multiplicity(self, multiplicity: Optional[int] = None) -> None: - """Receive the electronic multiplicity through MDI - - Parameters - ---------- - multiplicity : int, optional - New multiplicity of the system. If None, receive through MDI. - """ - if multiplicity is None: - multiplicity = MDI_Recv(1, MDI_INT, self.comm) - self.multiplicity = multiplicity - self.energy_is_current = False - - # Allow a validation error here, because a future >TOTCHARGE command might resolve it - try: - self.update_molecule("molecular_multiplicity", self.multiplicity) - except qcel.exceptions.ValidationError: - pass - - # Respond to the EXIT command - def stop(self) -> None: - """Stop listening for MDI commands""" - self.stop_listening = True - - # Enter server mode, listening for commands from the driver - def start(self) -> None: - """Receive commands through MDI and respond to them as defined by the MDI Standard""" - - while not self.stop_listening: - if self.world_rank == 0: - command = MDI_Recv_Command(self.comm) - else: - command = None - if self.world_rank == 0: - print("MDI command received: " + str(command)) - - # Search for this command in self.commands - found_command = False - for supported_command in self.commands: - if not found_command and command == supported_command: - # Run the function corresponding to this command - self.commands[supported_command]() - found_command = True - if not found_command: - raise Exception("Unrecognized command: " + str(command)) diff --git a/qcengine/procedures/__init__.py b/qcengine/procedures/__init__.py deleted file mode 100644 index b5e0e0266..000000000 --- a/qcengine/procedures/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .base import get_procedure, list_all_procedures, list_available_procedures, register_procedure diff --git a/qcengine/procedures/base.py b/qcengine/procedures/base.py deleted file mode 100644 index 2e9979d4a..000000000 --- a/qcengine/procedures/base.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Imports the various procedure backends -""" - -from typing import Set - -from ..exceptions import InputError, ResourceError -from .berny import BernyProcedure -from .geometric import GeometricProcedure -from .nwchem_opt import NWChemDriverProcedure -from .optking import OptKingProcedure -from .torsiondrive import TorsionDriveProcedure -from .model import ProcedureHarness - -__all__ = ["register_procedure", "get_procedure", "list_all_procedures", "list_available_procedures"] - -procedures = {} - - -def register_procedure(entry_point: ProcedureHarness) -> None: - """ - Register a new ProcedureHarness with QCEngine - """ - - name = entry_point.name - if name.lower() in procedures.keys(): - raise ValueError("{} is already a registered procedure.".format(name)) - - procedures[name.lower()] = entry_point - - -def get_procedure(name: str) -> ProcedureHarness: - """ - Returns a procedures executor class - """ - - name = name.lower() - - if name not in procedures: - raise InputError(f"Procedure {name} is not registered to QCEngine.") - - ret = procedures[name] - if not ret.found(): - raise ResourceError(f"Procedure {name} is registered with QCEngine, but cannot be found.") - - return ret - - -def list_all_procedures() -> Set[str]: - """ - List all procedures registered by QCEngine. - """ - return set(procedures.keys()) - - -def list_available_procedures() -> Set[str]: - """ - List all procedures that can be exectued (found) by QCEngine. - """ - - ret = set() - for k, p in procedures.items(): - if p.found(): - ret.add(k) - - return ret - - -register_procedure(GeometricProcedure()) -register_procedure(OptKingProcedure()) -register_procedure(BernyProcedure()) -register_procedure(NWChemDriverProcedure()) -register_procedure(TorsionDriveProcedure()) diff --git a/qcengine/procedures/berny.py b/qcengine/procedures/berny.py deleted file mode 100644 index 84345f203..000000000 --- a/qcengine/procedures/berny.py +++ /dev/null @@ -1,96 +0,0 @@ -import logging -from qcengine.exceptions import UnknownError -import sys -import traceback -from io import StringIO -from typing import Any, Dict, Union - -import numpy as np -from qcelemental.models import OptimizationInput, OptimizationResult, FailedOperation -from qcelemental.util import which_import - -import qcengine - -from ..config import TaskConfig -from .model import ProcedureHarness - - -class BernyProcedure(ProcedureHarness): - _defaults = {"name": "Berny", "procedure": "optimization"} - - def found(self, raise_error: bool = False) -> bool: - return which_import( - "berny", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `pip install pyberny`.", - ) - - def build_input_model(self, data: Union[Dict[str, Any], "OptimizationInput"]) -> "OptimizationInput": - return self._build_model(data, OptimizationInput) - - def compute( - self, input_data: "OptimizationInput", config: "TaskConfig" - ) -> Union["OptimizationResult", "FailedOperation"]: - try: - import berny - except ModuleNotFoundError: - raise ModuleNotFoundError("Could not find Berny in the Python path.") - - # Get berny version from the installed package, use setuptools' - # pkg_resources for python < 3.8 - if sys.version_info >= (3, 8): - from importlib.metadata import distribution - else: - from pkg_resources import get_distribution as distribution - berny_version = distribution("pyberny").version - - # Berny uses the stdlib logging module and by default uses per-module - # loggers. For QCEngine, we create one logger per BernyProcedure - # instance, by using the instance's id(), and send all logging messages - # to a string stream - log_stream = StringIO() - log = logging.getLogger(f"{__name__}.{id(self)}") - log.addHandler(logging.StreamHandler(log_stream)) - log.setLevel("INFO") - - input_data = input_data.dict() - geom_qcng = input_data["initial_molecule"] - comput = {**input_data["input_specification"], "molecule": geom_qcng} - program = input_data["keywords"].pop("program") - task_config = config.dict() - trajectory = [] - output_data = input_data.copy() - try: - # Pyberny uses angstroms for the Cartesian geometry, but atomic - # units for everything else, including the gradients (hartree/bohr). - geom_berny = berny.Geometry(geom_qcng["symbols"], geom_qcng["geometry"] / berny.angstrom) - opt = berny.Berny(geom_berny, logger=log, **input_data["keywords"]) - for geom_berny in opt: - geom_qcng["geometry"] = np.stack(geom_berny.coords * berny.angstrom) - ret = qcengine.compute(comput, program, task_config=task_config) - if ret.success: - trajectory.append(ret.dict()) - opt.send((ret.properties.return_energy, ret.return_result)) - else: - # qcengine.compute returned FailedOperation - raise UnknownError("Gradient computation failed") - - except UnknownError: - error = ret.error.dict() # ComputeError - except Exception: - error = {"error_type": "unknown", "error_message": f"Berny error:\n{traceback.format_exc()}"} - else: - output_data["success"] = True - output_data.update( - { - "schema_name": "qcschema_optimization_output", - "final_molecule": trajectory[-1]["molecule"], - "energies": [r["properties"]["return_energy"] for r in trajectory], - "trajectory": trajectory, - "provenance": {"creator": "Berny", "routine": "berny.Berny", "version": berny_version}, - "stdout": log_stream.getvalue(), # collect logged messages - } - ) - return OptimizationResult(**output_data) - return FailedOperation(input_data=input_data, error=error) diff --git a/qcengine/procedures/geometric.py b/qcengine/procedures/geometric.py deleted file mode 100644 index 59bc26c86..000000000 --- a/qcengine/procedures/geometric.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Any, Dict, Union - -from qcelemental.models import OptimizationInput, OptimizationResult -from qcelemental.util import safe_version, which_import - -from .model import ProcedureHarness - - -class GeometricProcedure(ProcedureHarness): - - _defaults = {"name": "geomeTRIC", "procedure": "optimization"} - - version_cache: Dict[str, str] = {} - - class Config(ProcedureHarness.Config): - pass - - def found(self, raise_error: bool = False) -> bool: - return which_import( - "geometric", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install geometric -c conda-forge`.", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which_import("geometric") - if which_prog not in self.version_cache: - import geometric - - self.version_cache[which_prog] = safe_version(geometric.__version__) - - return self.version_cache[which_prog] - - def build_input_model(self, data: Union[Dict[str, Any], "OptimizationInput"]) -> "OptimizationInput": - return self._build_model(data, OptimizationInput) - - def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "OptimizationResult": - try: - import geometric - except ModuleNotFoundError: - raise ModuleNotFoundError("Could not find geomeTRIC in the Python path.") - - input_data = input_model.dict() - - # Temporary patch for geomeTRIC - input_data["initial_molecule"]["symbols"] = list(input_data["initial_molecule"]["symbols"]) - - # Set retries to two if zero while respecting local_config - local_config = config.dict() - local_config["retries"] = local_config.get("retries", 2) or 2 - input_data["input_specification"]["extras"]["_qcengine_local_config"] = local_config - - # Run the program - output_data = geometric.run_json.geometric_run_json(input_data) - - output_data["provenance"] = { - "creator": "geomeTRIC", - "routine": "geometric.run_json.geometric_run_json", - "version": geometric.__version__, - } - - output_data["schema_name"] = "qcschema_optimization_output" - output_data["input_specification"]["extras"].pop("_qcengine_local_config", None) - if output_data["success"]: - output_data = OptimizationResult(**output_data) - - return output_data diff --git a/qcengine/procedures/model.py b/qcengine/procedures/model.py deleted file mode 100644 index 0e540114d..000000000 --- a/qcengine/procedures/model.py +++ /dev/null @@ -1,70 +0,0 @@ -import abc -from typing import Any, Dict, Union - -try: - from pydantic.v1 import BaseModel -except ImportError: - from pydantic import BaseModel - -from ..util import model_wrapper - - -class ProcedureHarness(BaseModel, abc.ABC): - - name: str - procedure: str - - class Config: - allow_mutation: False - extra: "forbid" - - def __init__(self, **kwargs): - super().__init__(**{**self._defaults, **kwargs}) - - @abc.abstractmethod - def build_input_model(self, data: Union[Dict[str, Any], "BaseModel"], raise_error: bool = True) -> "BaseModel": - """ - Build and validate the input model, passes if the data was a normal BaseModel input. - - Parameters - ---------- - data : Union[Dict[str, Any], 'BaseModel'] - A data blob to construct the model from or the input model itself - raise_error : bool, optional - Raise an error or not if the operation failed. - - Returns - ------- - BaseModel - The input model for the procedure. - """ - - @abc.abstractmethod - def compute(self, input_data: "BaseModel", config: "TaskConfig") -> "BaseModel": - pass - - @abc.abstractmethod - def found(self, raise_error: bool = False) -> bool: - """ - Checks if the program can be found. - - Returns - ------- - bool - If the proceudre was found or not. - """ - - def _build_model(self, data: Dict[str, Any], model: "BaseModel") -> "BaseModel": - """ - Quick wrapper around util.model_wrapper for inherited classes - """ - - return model_wrapper(data, model) - - def get_version(self) -> str: - """Finds procedure, extracts version, returns normalized version string. - Returns - ------- - str - Return a valid, safe python version string. - """ diff --git a/qcengine/procedures/nwchem_opt/__init__.py b/qcengine/procedures/nwchem_opt/__init__.py deleted file mode 100644 index 08baabc50..000000000 --- a/qcengine/procedures/nwchem_opt/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -from typing import Union, Dict, Any - -from qcelemental.models import OptimizationInput, AtomicInput, OptimizationResult, Provenance - -from qcengine.config import TaskConfig -from qcengine.exceptions import UnknownError, InputError -from qcengine.procedures.nwchem_opt.harvester import harvest_as_atomic_result -from qcengine.programs.nwchem.runner import NWChemHarness -from qcengine.procedures.model import ProcedureHarness - - -class NWChemDriverProcedure(ProcedureHarness): - """Structural relaxation using NWChem's optimizer""" - - _defaults = {"name": "NWChemDriver", "procedure": "optimization"} - - class Config(ProcedureHarness.Config): - pass - - def found(self, raise_error: bool = False) -> bool: - nwc_harness = NWChemHarness() - return nwc_harness.found(raise_error) - - def get_version(self) -> str: - nwc_harness = NWChemHarness() - return nwc_harness.get_version() - - def build_input_model(self, data: Union[Dict[str, Any], "OptimizationInput"]) -> OptimizationInput: - return self._build_model(data, OptimizationInput) - - def compute(self, input_data: OptimizationInput, config: TaskConfig) -> "BaseModel": - nwc_harness = NWChemHarness() - self.found(raise_error=True) - - # Unify the keywords from the OptimizationInput and QCInputSpecification - # Optimization input will override, but don't tell users this as it seems unnecessary - keywords = input_data.keywords.copy() - keywords.update(input_data.input_specification.keywords) - if keywords.get("program", "nwchem").lower() != "nwchem": - raise InputError("NWChemDriver procedure only works with NWChem") - - # Add a flag to the atomic input that tells the NWChemHarness we are calling it from driver - # This is needed for the NWCHarness to make some changes to the input file - input_data.input_specification.extras["is_driver"] = True - - # Make an atomic input - atomic_input = AtomicInput( - molecule=input_data.initial_molecule, - driver="energy", - keywords=keywords, - **input_data.input_specification.dict(exclude={"driver", "keywords"}), - ) - - # Build the inputs for the job - job_inputs = nwc_harness.build_input(atomic_input, config) - - # Replace the last line with a "task {} optimize" - input_file: str = job_inputs["infiles"]["nwchem.nw"].strip() - beginning, last_line = input_file.rsplit("\n", 1) - assert last_line.startswith("task") - last_line = f"task {last_line.split(' ')[1]} optimize" - job_inputs["infiles"]["nwchem.nw"] = f"{beginning}\n{last_line}" - - # Run it! - success, dexe = nwc_harness.execute(job_inputs) - - # Check for common errors - if "There is an error in the input file" in dexe["stdout"]: - raise InputError(dexe["stdout"]) - if "not compiled" in dexe["stdout"]: - # recoverable with a different compilation with optional modules - raise InputError(dexe["stdout"]) - - # Parse it - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - return self.parse_output(dexe["outfiles"], input_data) - else: - raise UnknownError(dexe["stdout"]) - - def parse_output(self, outfiles: Dict[str, str], input_model: OptimizationInput) -> OptimizationResult: - - # Get the stdout from the calculation (required) - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - # Parse out the atomic results from the file - atomic_results = harvest_as_atomic_result(input_model, stdout) - - # Isolate the converged result - final_step = atomic_results[-1] - - return OptimizationResult( - initial_molecule=input_model.initial_molecule, - input_specification=input_model.input_specification, - final_molecule=final_step.molecule, - trajectory=atomic_results, - energies=[float(r.extras["qcvars"]["CURRENT ENERGY"]) for r in atomic_results], - stdout=stdout, - stderr=stderr, - success=True, - provenance=Provenance(creator="NWChemRelax", version=self.get_version(), routine="nwchem_opt"), - ) diff --git a/qcengine/procedures/nwchem_opt/harvester.py b/qcengine/procedures/nwchem_opt/harvester.py deleted file mode 100644 index 76ba2abb2..000000000 --- a/qcengine/procedures/nwchem_opt/harvester.py +++ /dev/null @@ -1,98 +0,0 @@ -import re -from decimal import Decimal -from typing import List, Tuple - -from qcelemental.models import AtomicResult, Molecule, OptimizationInput, Provenance -from qcelemental.util import unnp - -from qcengine.programs.nwchem.harvester import harvest_outfile_pass -from qcengine.programs.qcvar_identities_resources import build_atomicproperties, build_out -from qcengine.programs.util import PreservingDict - - -def harvest_output(outtext: str) -> Tuple[List[PreservingDict], List[Molecule], List[list], str, str]: - """Function to read an entire NWChem output file. - - Reads all of the different "line search" segments of a file and returns - values from up to last segment for which a geometry was written. - - Args: - outtext (str): Output written to stdout - Returns: - - Variables extracted from the output file in each optimization step - - Molecules from the each complete step - - Gradients from the each complete step - - (str): Version string - - (str): Error message, if any - """ - - # Loop over all steps - # TODO (wardlt): Is it only necessary to read the last two steps? - pass_psivar = [] - pass_coord = [] - pass_grad = [] - version = error = None - for outpass in re.split(r"Step +\d", outtext, re.MULTILINE)[1:]: - psivar, nwcoord, nwgrad, version, module, error = harvest_outfile_pass(outpass) - pass_psivar.append(psivar) - pass_coord.append(nwcoord) - pass_grad.append(nwgrad) - - # Ensure last segment contains the last geometry - if not pass_coord[-1]: - pass_psivar.pop() - pass_coord.pop() - pass_grad.pop() - - return pass_psivar, pass_coord, pass_grad, version, module, error - - -def harvest_as_atomic_result(input_model: OptimizationInput, nwout: str) -> List[AtomicResult]: - """Parse each step in the geometry relaxation as a separate AtomicResult - - Args: - input_model: Input specification for the relaxation - nwout: Standard out from the NWChem simulation - Returns: - A list of the results at each step - """ - # Parse the files - out_psivars, out_mols, out_grads, version, module, error = harvest_output(nwout) - - # Make atomic results - results = [] - for qcvars, nwgrad, out_mol in zip(out_psivars, out_grads, out_mols): - if nwgrad is not None: - qcvars[f"{input_model.input_specification.model.method.upper()[4:]} TOTAL GRADIENT"] = nwgrad - qcvars["CURRENT GRADIENT"] = nwgrad - - # Get the formatted properties - build_out(qcvars) - atprop = build_atomicproperties(qcvars) - - provenance = Provenance(creator="NWChem", version=version, routine="nwchem_opt").dict() - if module is not None: - provenance["module"] = module - - # Format them inout an output - output_data = { - "schema_version": 1, - "molecule": out_mol, - "driver": "gradient", - "extras": input_model.extras.copy(), - "model": input_model.input_specification.model, - "keywords": input_model.input_specification.keywords, - "properties": atprop, - "provenance": provenance, - "return_result": nwgrad, - "success": True, - } - - # got to even out who needs plump/flat/Decimal/float/ndarray/list - # Decimal --> str preserves precision - output_data["extras"]["qcvars"] = { - k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in unnp(qcvars, flat=True).items() - } - - results.append(AtomicResult(**output_data)) - return results diff --git a/qcengine/procedures/optking.py b/qcengine/procedures/optking.py deleted file mode 100644 index 34139a3f3..000000000 --- a/qcengine/procedures/optking.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Any, Dict, Union - -from qcelemental.models import OptimizationInput, OptimizationResult -from qcelemental.util import safe_version, which_import - -from .model import ProcedureHarness - - -class OptKingProcedure(ProcedureHarness): - - _defaults = {"name": "OptKing", "procedure": "optimization"} - - version_cache: Dict[str, str] = {} - - class Config(ProcedureHarness.Config): - pass - - def found(self, raise_error: bool = False) -> bool: - return which_import( - "optking", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install optking -c conda-forge`.", - ) - - def build_input_model(self, data: Union[Dict[str, Any], "OptimizationInput"]) -> "OptimizationInput": - return self._build_model(data, OptimizationInput) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which_import("optking") - if which_prog not in self.version_cache: - import optking - - self.version_cache[which_prog] = safe_version(optking.__version__) - - return self.version_cache[which_prog] - - def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "Optimization": - if self.found(raise_error=True): - import optking - - input_data = input_model.dict() - - # Set retries to two if zero while respecting local_config - local_config = config.dict() - local_config["retries"] = local_config.get("retries", 2) or 2 - input_data["input_specification"]["extras"]["_qcengine_local_config"] = local_config - - # Run the program - output_data = optking.optwrapper.optimize_qcengine(input_data) - - output_data["schema_name"] = "qcschema_optimization_output" - output_data["input_specification"]["extras"].pop("_qcengine_local_config", None) - if output_data["success"]: - output_data = OptimizationResult(**output_data) - - return output_data diff --git a/qcengine/procedures/torsiondrive.py b/qcengine/procedures/torsiondrive.py deleted file mode 100644 index 0fc1327d7..000000000 --- a/qcengine/procedures/torsiondrive.py +++ /dev/null @@ -1,232 +0,0 @@ -import io -from collections import defaultdict -from contextlib import redirect_stderr, redirect_stdout -from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Union - -import numpy as np -from qcelemental.models import FailedOperation, Molecule -from qcelemental.models.procedures import OptimizationInput, OptimizationResult, TorsionDriveInput, TorsionDriveResult -from qcelemental.util import which_import - -from .model import ProcedureHarness - -if TYPE_CHECKING: - from ..config import TaskConfig - - -class TorsionDriveProcedure(ProcedureHarness): - - _defaults = {"name": "TorsionDrive", "procedure": "torsiondrive"} - - class Config(ProcedureHarness.Config): - pass - - def found(self, raise_error: bool = False) -> bool: - return which_import( - "torsiondrive", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install torsiondrive -c conda-forge`.", - ) - - def build_input_model(self, data: Union[Dict[str, Any], "TorsionDriveInput"]) -> "TorsionDriveInput": - return self._build_model(data, TorsionDriveInput) - - def _compute(self, input_model: "TorsionDriveInput", config: "TaskConfig"): - - self.found(raise_error=True) - - import torsiondrive.td_api - - dihedrals = input_model.keywords.dihedrals - grid_spacing = input_model.keywords.grid_spacing - - dihedral_ranges = input_model.keywords.dihedral_ranges - - energy_decrease_thresh = input_model.keywords.energy_decrease_thresh - energy_upper_limit = input_model.keywords.energy_upper_limit - - state = torsiondrive.td_api.create_initial_state( - dihedrals=dihedrals, - grid_spacing=grid_spacing, - elements=input_model.initial_molecule[0].symbols, - init_coords=[molecule.geometry.flatten().tolist() for molecule in input_model.initial_molecule], - dihedral_ranges=dihedral_ranges, - energy_upper_limit=energy_upper_limit, - energy_decrease_thresh=energy_decrease_thresh, - ) - - optimization_results = defaultdict(list) - error = None - - # Spawn new optimizations at each grid points until convergence / an error. - while True: - - next_jobs = torsiondrive.td_api.next_jobs_from_state(state, verbose=False) - - if len(next_jobs) == 0: - break - - grid_point_results = self._spawn_optimizations(next_jobs=next_jobs, input_model=input_model, config=config) - - for grid_point, results in grid_point_results.items(): - - failed_results = [result for result in results if not result.success] - - if len(failed_results) > 0: - - error_message = failed_results[0].error.error_message - error = { - "error_type": "unknown", - "error_message": f"TorsionDrive error at {grid_point}:\n{error_message}", - } - break - - optimization_results[grid_point].extend(results) - - if error is not None: - break - - task_results = { - grid_point: [ - ( - result.initial_molecule.geometry.flatten().tolist(), - result.final_molecule.geometry.flatten().tolist(), - result.energies[-1], - ) - for result in results - ] - for grid_point, results in grid_point_results.items() - } - - torsiondrive.td_api.update_state(state, {**task_results}) - - output_data = input_model.dict() - output_data["provenance"] = { - "creator": "TorsionDrive", - "routine": "torsiondrive.td_api.next_jobs_from_state", - "version": torsiondrive.__version__, - } - output_data["success"] = error is None - - # even if we hit an error during the torsiondrive, we output what we can - output_data["final_energies"], output_data["final_molecules"] = {}, {} - - for grid_point, results in optimization_results.items(): - - final_energy, final_molecule = self._find_final_results(results) - - output_data["final_energies"][grid_point] = final_energy - output_data["final_molecules"][grid_point] = final_molecule - - output_data["optimization_history"] = optimization_results - - if error is not None: - output_data["error"] = error - - return output_data - - def compute(self, input_model: "TorsionDriveInput", config: "TaskConfig") -> "TorsionDriveResult": - - # Capture the stdout and err here to avoid too much nesting in the _compute function - with io.StringIO() as stdout: - with io.StringIO() as stderr: - - with redirect_stdout(stdout): - with redirect_stderr(stderr): - - output_data = self._compute(input_model, config) - - output_data["stdout"] = str(stdout.getvalue()) - output_data["stderr"] = str(stderr.getvalue()) - - # these will get populated by the model below - output_data.pop("schema_name", None) - output_data.pop("schema_version", None) - - output_data = TorsionDriveResult(**output_data) - - return output_data - - @staticmethod - def _spawn_optimization( - grid_point: str, job: List[float], input_model: "TorsionDriveInput", config: "TaskConfig" - ) -> Union[FailedOperation, OptimizationResult]: - """Spawns an optimization at a particular grid point and returns the result. - - Parameters - ---------- - grid_point - A string of the form 'dihedral_1_angle ... dihedral_n_angle' that encodes - the current dihedrals angles to optimize at. - job - The flattened conformer of the molecule to start the optimization at with - length=(n_atoms * 3) - input_model - The input model containing the relevant settings for how to optimize the - structure. - config - The configuration to launch the task using. - - Returns - ------- - The result of the optimization if successful, otherwise an error containing - object. - """ - - from qcengine import compute_procedure - - input_molecule = input_model.initial_molecule[0].copy(deep=True).dict() - input_molecule["geometry"] = np.array(job).reshape(len(input_molecule["symbols"]), 3) - input_molecule = Molecule.from_data(input_molecule) - - dihedrals = input_model.keywords.dihedrals - angles = grid_point.split() - - keywords = { - **input_model.optimization_spec.keywords, - "constraints": { - "set": [ - { - "type": "dihedral", - "indices": dihedral, - "value": int(angle), - } - for dihedral, angle in zip(dihedrals, angles) - ] - }, - } - - input_data = OptimizationInput( - keywords=keywords, - extras={}, - protocols=input_model.optimization_spec.protocols, - input_specification=input_model.input_specification, - initial_molecule=input_molecule, - ) - - return compute_procedure( - input_data, procedure=input_model.optimization_spec.procedure, task_config=config.dict() - ) - - @staticmethod - def _find_final_results( - optimization_results: List[OptimizationResult], - ) -> Tuple[float, Molecule]: - """Returns the energy and final molecule of the lowest energy optimization - in a set.""" - - final_energies = np.array([result.energies[-1] for result in optimization_results]) - lowest_energy_idx = final_energies.argmin() - - return float(final_energies[lowest_energy_idx]), optimization_results[lowest_energy_idx].final_molecule - - def _spawn_optimizations( - self, next_jobs: Dict[str, List[float]], input_model: "TorsionDriveInput", config: "TaskConfig" - ) -> Dict[str, List[Union[FailedOperation, OptimizationResult]]]: - - grid_point_results = { - grid_point: [self._spawn_optimization(grid_point, job, input_model, config) for job in jobs] - for grid_point, jobs in next_jobs.items() - } - return grid_point_results diff --git a/qcengine/programs/__init__.py b/qcengine/programs/__init__.py deleted file mode 100644 index e2b830ff6..000000000 --- a/qcengine/programs/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .base import get_program, list_all_programs, list_available_programs, register_program, unregister_program -from .model import ProgramHarness diff --git a/qcengine/programs/adcc.py b/qcengine/programs/adcc.py deleted file mode 100644 index ff641c5ef..000000000 --- a/qcengine/programs/adcc.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -Calls adcc -""" -from typing import TYPE_CHECKING, Dict - -from qcelemental.models import AtomicResult, BasisSet, Provenance -from qcelemental.util import safe_version, which_import - -from ..exceptions import InputError, UnknownError -from .model import ProgramHarness -from .qcvar_identities_resources import build_atomicproperties - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput - - from ..config import TaskConfig - -# - - -class AdccHarness(ProgramHarness): - _defaults = { - "name": "adcc", - "scratch": False, - "thread_safe": False, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - """Whether adcc harness is ready for operation. - Parameters - ---------- - raise_error: bool - Passed on to control negative return between False and ModuleNotFoundError raised. - Returns - ------- - bool - If adcc and psi4 are found, returns True. - If raise_error is False and adcc or psi4 is missing, returns False. - If raise_error is True and adcc or psi4 are missing, the error message is raised. - """ - found_adcc = which_import( - "adcc", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install adcc -c conda-forge`.", - ) - found_psi4 = which_import( - "psi4", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install psi4 for adcc harness via `conda install psi4 -c conda-forge/label/libint_dev -c conda-forge`.", - ) - return found_adcc and found_psi4 - - def get_version(self) -> str: - """Return the currently used version of adcc""" - self.found(raise_error=True) - - which_prog = which_import("adcc") - if which_prog not in self.version_cache: - import adcc - - self.version_cache[which_prog] = safe_version(adcc.__version__) - return self.version_cache[which_prog] - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - """ - Runs adcc - """ - self.found(raise_error=True) - - import adcc - import psi4 - - mol = input_model.molecule - model = input_model.model - conv_tol = input_model.keywords.get("conv_tol", 1e-6) - - if input_model.driver not in ["energy", "properties"]: - raise InputError(f"Driver {input_model.driver} not implemented for ADCC.") - - if isinstance(input_model.model.basis, BasisSet): - raise InputError("QCSchema BasisSet for model.basis not implemented. Use string basis name.") - if not input_model.model.basis: - raise InputError("Model must contain a basis set.") - - psi4_molecule = psi4.core.Molecule.from_schema(dict(mol.dict(), fix_symmetry="c1")) - psi4.core.clean() - psi4.core.be_quiet() - psi4.set_options( - { - "basis": model.basis, - "scf_type": "pk", - "e_convergence": conv_tol / 100, - "d_convergence": conv_tol / 10, - # 'maxiter': max_iter, - "reference": "RHF" if mol.molecular_multiplicity == 1 else "UHF", - } - ) - _, wfn = psi4.energy("HF", return_wfn=True, molecule=psi4_molecule) - adcc.set_n_threads(config.ncores) - compute_success = False - try: - adcc_state = adcc.run_adc(wfn, method=model.method, **input_model.keywords) - compute_success = adcc_state.converged - except adcc.InputError as e: - raise InputError(str(e)) - except Exception as e: - raise UnknownError(str(e)) - - input_data = input_model.dict(encoding="json") - output_data = input_data.copy() - output_data["success"] = compute_success - - if compute_success: - output_data["return_result"] = adcc_state.excitation_energy[0] - - extract_props = input_model.driver == "properties" - qcvars = adcc_state.to_qcvars(recurse=True, properties=extract_props) - atprop = build_atomicproperties(qcvars) - output_data["extras"]["qcvars"] = qcvars - output_data["properties"] = atprop - - provenance = Provenance(creator="adcc", version=self.get_version(), routine="adcc").dict() - provenance["nthreads"] = adcc.get_n_threads() - output_data["provenance"] = provenance - - return AtomicResult(**output_data) diff --git a/qcengine/programs/aimnet2.py b/qcengine/programs/aimnet2.py deleted file mode 100644 index 4f91b5951..000000000 --- a/qcengine/programs/aimnet2.py +++ /dev/null @@ -1,122 +0,0 @@ -from typing import TYPE_CHECKING, Dict -from qcengine.programs.model import ProgramHarness -from qcelemental.util import safe_version, which_import -from qcelemental.models import AtomicResult, Provenance -from qcengine.exceptions import InputError - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput - from qcengine.config import TaskConfig - - -class AIMNET2Harness(ProgramHarness): - """A harness to run AIMNET2 models """ - - _CACHE = {} - - _defaults = { - "name": "AIMNET2", - "scratch": False, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - - version_cache: Dict[str, str] = {} - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which_import( - "pyaimnet2", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `pip install git+https://github.com/jthorton/AIMNet2.git@main`", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which_import("pyaimnet2") - if which_prog not in self.version_cache: - import pyaimnet2 - - self.version_cache[which_prog] = safe_version(pyaimnet2.__version__) - - return self.version_cache[which_prog] - - def load_model(self, name: str): - model_name = name.lower() - if model_name in self._CACHE: - return self._CACHE[model_name] - - from pyaimnet2 import load_model - - model = load_model(model_name=model_name) - self._CACHE[model_name] = model - return self._CACHE[model_name] - - def compute(self, input_data: "AtomicInput", config: "TaskConfig"): - self.found(raise_error=True) - import torch - from qcengine.units import ureg - - # check we can run on the set of elements - known_elements = {"H", "B", "C", "N", "O", "F", "Si", "P", "S", "Cl", "As", "Se", "Br", "I"} - target_elements = set(input_data.molecule.symbols) - - unknown_elements = target_elements - known_elements - if unknown_elements: - raise InputError(f"AIMNET2 model {input_data.model.method} does not support elements {unknown_elements}.") - - method = input_data.model.method - # load the model using the method as the file name - model = self.load_model(name=method) - - # build the required input data - aimnet_input = { - "coord": torch.tensor( - [input_data.molecule.geometry * ureg.conversion_factor("bohr", "angstrom")], - dtype=torch.float64, - device="cpu", - ), - "numbers": torch.tensor([input_data.molecule.atomic_numbers], dtype=torch.long, device="cpu"), - "charge": torch.tensor([input_data.molecule.molecular_charge], dtype=torch.float64, device="cpu"), - } - - if input_data.driver == "gradient": - aimnet_input["coord"].requires_grad_(True) - out = model(aimnet_input) - - ret_data = { - "success": False, - "properties": { - "return_energy": out["energy"].item() * ureg.conversion_factor("eV", "hartree"), - "return_gradient": ( - -1.0 * out["forces"][0].detach().numpy() * ureg.conversion_factor("eV / angstrom", "hartree / bohr") - ), - "calcinfo_natom": len(input_data.molecule.atomic_numbers), - }, - "extras": input_data.extras.copy(), - } - # update with calculated extras - ret_data["extras"]["aimnet2"] = { - "charges": out["charges"].detach()[0].cpu().numpy(), - "ensemble_charges_std": out["charges_std"].detach()[0].cpu().numpy(), - "ensemble_energy_std": out["energy_std"].item(), - "ensemble_forces_std": out["forces_std"].detach()[0].cpu().numpy(), - } - if input_data.driver == "energy": - ret_data["return_result"] = ret_data["properties"]["return_energy"] - elif input_data.driver == "gradient": - ret_data["return_result"] = ret_data["properties"]["return_gradient"] - else: - raise InputError( - f"AIMNET2 can only compute energy and gradients driver methods. Requested {input_data.driver} not supported." - ) - - ret_data["provenance"] = Provenance(creator="pyaimnet2", version=self.get_version(), routine="load_model") - - ret_data["success"] = True - - return AtomicResult(**{**input_data.dict(), **ret_data}) diff --git a/qcengine/programs/base.py b/qcengine/programs/base.py deleted file mode 100644 index c72687f4c..000000000 --- a/qcengine/programs/base.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Imports the various compute backends -""" - -from typing import Set - -from ..exceptions import InputError, ResourceError -from .model import ProgramHarness -from .adcc import AdccHarness -from .cfour import CFOURHarness -from .dftd3 import DFTD3Harness -from .dftd_ng import DFTD4Harness, SDFTD3Harness -from .gamess import GAMESSHarness -from .gcp import GCPHarness, MCTCGCPHarness -from .molpro import MolproHarness -from .mopac import MopacHarness -from .mp2d import MP2DHarness -from .mrchem import MRChemHarness -from .nwchem import NWChemHarness -from .openmm import OpenMMHarness -from .psi4 import Psi4Harness -from .qchem import QChemHarness -from .qcore import EntosHarness, QcoreHarness -from .rdkit import RDKitHarness -from .terachem import TeraChemHarness -from .terachem_frontend import TeraChemFrontEndHarness -from .terachem_pbs import TeraChemPBSHarness -from .torchani import TorchANIHarness -from .turbomole import TurbomoleHarness -from .xtb import XTBHarness -from .mace import MACEHarness -from .aimnet2 import AIMNET2Harness - -__all__ = ["register_program", "get_program", "list_all_programs", "list_available_programs"] - -programs = {} - - -def register_program(entry_point: ProgramHarness) -> None: - """ - Register a new ProgramHarness with QCEngine. - """ - - name = entry_point.name - if name.lower() in programs.keys(): - raise ValueError("{} is already a registered program.".format(name)) - - programs[name.lower()] = entry_point - - -def unregister_program(name: str) -> None: - """ - Unregisters a given program. - """ - - ret = programs.pop(name.lower(), None) - if ret is None: - raise KeyError(f"Program {name} is not registered with QCEngine") - - -def get_program(name: str, check: bool = True) -> ProgramHarness: - """ - Returns a program's executor class - - Parameters - ---------- - check - ``True`` Do raise error if program not found. ``False`` is handy for - the specialized case of calling non-execution methods (like parsing for testing) - on the returned ``Harness``. - - """ - name = name.lower() - - if name not in programs: - raise InputError(f"Program {name} is not registered to QCEngine.") - - ret = programs[name] - if check: - try: - ret.found(raise_error=True) - except ModuleNotFoundError as err: - raise ResourceError(f"Program {name} is registered with QCEngine, but cannot be found.") from err - - return ret - - -def list_all_programs() -> Set[str]: - """ - List all programs registered by QCEngine. - """ - return set(programs.keys()) - - -def list_available_programs() -> Set[str]: - """ - List all programs that can be exectued (found) by QCEngine. - """ - - ret = set() - for k, p in programs.items(): - if p.found(): - ret.add(k) - - return ret - - -# Quantum -register_program(AdccHarness()) -register_program(CFOURHarness()) -register_program(EntosHarness()) # Duplicate of Qcore harness to transition the namespace, to be deprecated -register_program(GAMESSHarness()) -register_program(MRChemHarness()) -register_program(MolproHarness()) -register_program(NWChemHarness()) -register_program(Psi4Harness()) -register_program(QChemHarness()) -register_program(QcoreHarness()) -register_program(TeraChemHarness()) -register_program(TurbomoleHarness()) -register_program(TeraChemFrontEndHarness()) -register_program(TeraChemPBSHarness()) - -# Semi-empirical -register_program(MopacHarness()) -register_program(XTBHarness()) - -# AI -register_program(TorchANIHarness()) -register_program(MACEHarness()) -register_program(AIMNET2Harness()) - -# Molecular Mechanics -register_program(RDKitHarness()) -register_program(OpenMMHarness()) - -# Analytical Corrections -register_program(DFTD3Harness()) -register_program(DFTD4Harness()) -register_program(SDFTD3Harness()) -register_program(GCPHarness()) -register_program(MCTCGCPHarness()) -register_program(MP2DHarness()) diff --git a/qcengine/programs/cfour/__init__.py b/qcengine/programs/cfour/__init__.py deleted file mode 100644 index d4a48dbb4..000000000 --- a/qcengine/programs/cfour/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .runner import CFOURHarness diff --git a/qcengine/programs/cfour/germinate.py b/qcengine/programs/cfour/germinate.py deleted file mode 100644 index 72d8917a5..000000000 --- a/qcengine/programs/cfour/germinate.py +++ /dev/null @@ -1,72 +0,0 @@ -from typing import Any, Dict - -from qcengine.exceptions import InputError - - -def muster_modelchem(method: str, driver: "DriverEnum") -> Dict[str, Any]: - """Converts the QC method into CFOUR keywords.""" - - method = method.lower() - opts = {} - - if driver == "properties": - pass - else: - derint = driver.derivative_int() - - if derint == 0: - if method == "cfour": - pass # permit clean operation of sandwich mode - else: - opts["deriv_level"] = "zero" - - elif derint == 1: - opts["deriv_level"] = "first" - - elif derint == 2: - opts["vibration"] = "exact" - - if method == "cfour": - pass - - elif method in ["scf", "hf"]: - opts["calc_level"] = "scf" - - elif method == "mp2": - opts["calc_level"] = "mp2" - - elif method == "mp3": - opts["calc_level"] = "mp3" - - elif method == "mp4(sdq)": - opts["calc_level"] = "sdq-mp4" - - elif method == "mp4": - opts["calc_level"] = "mp4" - - elif method == "bccd": - opts["calc_level"] = "b-ccd" - - elif method == "cc2": - opts["calc_level"] = "cc2" - - elif method == "ccsd": - opts["calc_level"] = "ccsd" - - elif method == "cc3": - opts["calc_level"] = "cc3" - - elif method == "ccsd(t)": - # Can't use (T) b/c bug in xsymcor lops it off - opts["calc_level"] = "ccsd[t]" - - elif method == "bccd(t)": - opts["calc_level"] = "b-ccd(t)" - - elif method == "ccsdt": - opts["calc_level"] = "ccsdt" - - else: - raise InputError(f"Method not recognized: {method}") - - return opts diff --git a/qcengine/programs/cfour/harvester.py b/qcengine/programs/cfour/harvester.py deleted file mode 100644 index 773d6d3d3..000000000 --- a/qcengine/programs/cfour/harvester.py +++ /dev/null @@ -1,1389 +0,0 @@ -import logging -import re -from decimal import Decimal - -import numpy as np -import qcelemental as qcel -from qcelemental.models import Molecule -from qcelemental.molparse import regex - -from ..util import PreservingDict, load_hessian - -logger = logging.getLogger(__name__) - - -def harvest_output(outtext): - """Function to separate portions of a CFOUR output file *outtest*, - divided by xjoda. - - """ - pass_psivar = [] - pass_coord = [] - pass_grad = [] - - # for outpass in re.split(r'--invoking executable xjoda', outtext, re.MULTILINE): - for outpass in re.split(r"JODA beginning optimization cycle", outtext, re.MULTILINE): - psivar, c4coord, c4grad, version, module, error = harvest_outfile_pass(outpass) - pass_psivar.append(psivar) - pass_coord.append(c4coord) - pass_grad.append(c4grad) - - # print('\n\nXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n') - # print(outpass) - # print(psivar, c4coord, c4grad, version, error) - # print('\n\nxxxxxxxxxxxxxxxxxxxxxxxxxx\n\n') - - retindx = -1 if pass_coord[-1] else -2 - - # print ' <<< C4 PSIVAR >>>' - # for item in pass_psivar[retindx]: - # print(' %30s %16.8f' % (item, pass_psivar[retindx][item])) - # print ' <<< C4 COORD >>>' - # for item in pass_coord[retindx]: - # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) - # print ' <<< C4 GRAD >>>' - # for item in pass_grad[retindx]: - # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) - - return pass_psivar[retindx], pass_coord[retindx], pass_grad[retindx], version, module, error - - -def harvest_outfile_pass(outtext): - """Function to read CFOUR output file *outtext* and parse important - quantum chemical information from it in - - """ - psivar = PreservingDict() - psivar_coord = None - psivar_grad = None - version = "" - module = None - error = "" - - # TODO: CI - # other ROHF tests - - NUMBER = r"(?x:" + regex.NUMBER + ")" - DECIMAL = r"(?x:" + regex.DECIMAL + ")" - - # Process version - mobj = re.search(r"^\s*" + r"Version" + r"\s+" + r"(?P[\w.]+)" + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched version") - version = mobj.group("version") - - # Process NRE - mobj = re.search( - r"^\s+" + r"(?:Nuclear repulsion energy :)" + r"\s+" + NUMBER + r"\s+a\.u\.\s*$", - outtext, - re.MULTILINE | re.IGNORECASE, - ) - if mobj: - logger.debug("matched nre") - psivar["NUCLEAR REPULSION ENERGY"] = mobj.group(1) - - # Process calcinfo - mobj = re.search( - r"^\s*" + r"There are" + r"\s+" + r"(?P\d+)" + r"\s+" + r"functions in the AO basis." + r"\s*$", - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched nbf", mobj.groups()) - psivar["N BASIS FUNCTIONS"] = mobj.group("nbf") - psivar["N MOLECULAR ORBITALS"] = mobj.group("nbf") # TODO BAD - - mobj = re.search( - # fmt: off - r"^\s*" + "Alpha population by irrep:" + r"(?P[\d\s]+)" + r"\s*" + - r"^\s*" + "Beta population by irrep:" + r"(?P[\d\s]+)" + r"\s*", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched occupied", mobj.groups()) - psivar["N ALPHA ELECTRONS"] = sum([int(d) for d in mobj.group("aocc").split()]) - psivar["N BETA ELECTRONS"] = sum([int(d) for d in mobj.group("bocc").split()]) - - # Process SCF - mobj = re.search(r"^\s+" + r"(?:E\(SCF\))" + r"\s+=\s+" + NUMBER + r"\s+a\.u\.\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched scf1") - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - - mobj = re.search(r"^\s+" + r"(?:E\(SCF\)=)" + r"\s+" + NUMBER + r"\s+" + NUMBER + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched scf2") - mobj5 = re.search(r"BRUECKNER\s+IBRKNR\s+ON", outtext) - if not mobj5: - # interferes with ROHF BCCD - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - - if "SCF TOTAL ENERGY" not in psivar: - # can be too greedy and match across scf cycles - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:SCF has converged.)' + r'\s*$' + - r'(?:.*?)' + - r'^\s+' + r'(?:\d+)' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched scf3") - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - - mobj = re.search(r"^\s+" + r"(?:E\(ROHF\)=)" + r"\s+" + NUMBER + r"\s+" + NUMBER + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched scf4") - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - - # Process MP2 - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:E2\(AA\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(AB\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(TOT\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:Total MP2 energy)' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2r") - psivar["MP2 SAME-SPIN CORRELATION ENERGY"] = 2 * Decimal(mobj.group(1)) - psivar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(2) - psivar["MP2 CORRELATION ENERGY"] = 2 * Decimal(mobj.group(1)) + Decimal(mobj.group(2)) - psivar["MP2 TOTAL ENERGY"] = mobj.group(4) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:E2\(AA\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(BB\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(AB\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(TOT\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:Total MP2 energy)' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2u") - psivar["MP2 SAME-SPIN CORRELATION ENERGY"] = Decimal(mobj.group(1)) + Decimal(mobj.group(2)) - psivar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(3) - psivar["MP2 CORRELATION ENERGY"] = Decimal(mobj.group(1)) + Decimal(mobj.group(2)) + Decimal(mobj.group(3)) - psivar["MP2 TOTAL ENERGY"] = mobj.group(5) - - mobj = re.search( - # particularly, want to avoid capture when following line present: - # "MP2 energies are correct only for semicanonical orbitals." - # fmt: off - r'Singles contribution will be calculated.' + r'\s*' + - r'^\s+' + r'-*' + r'\s*' + - r'^\s+' + r'(?:E\(SCF\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(AA\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(BB\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(AB\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(SINGLE\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:E2\(TOT\))' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'(?:Total MP2 energy)' + r'\s+=\s+' + NUMBER + r'\s+a.u.\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2ro") - psivar["MP2 SAME-SPIN CORRELATION ENERGY"] = Decimal(mobj.group(2)) + Decimal(mobj.group(3)) - psivar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(4) - psivar["MP2 SINGLES ENERGY"] = mobj.group(5) - psivar["MP2 CORRELATION ENERGY"] = ( - Decimal(mobj.group(5)) + Decimal(mobj.group(2)) + Decimal(mobj.group(3)) + Decimal(mobj.group(4)) - ) - psivar["MP2 TOTAL ENERGY"] = mobj.group(7) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:S-MBPT\(2\))' + r'\s+' + r'(?P' + NUMBER + r')' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(2\))' + r'\s+' + r'(?P' + NUMBER + r')' + r'\s+' + - r'(?P' + NUMBER + r')' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2ro2") - # psivar['MP2 SAME-SPIN CORRELATION ENERGY'] = Decimal(mobj.group(1)) + Decimal(mobj.group(2)) - # psivar['MP2 OPPOSITE-SPIN CORRELATION ENERGY'] = mobj.group(3) - psivar["MP2 SINGLES ENERGY"] = mobj.group("sgl") - psivar["MP2 CORRELATION ENERGY"] = Decimal(mobj.group("sgl")) + Decimal(mobj.group("dbl")) - psivar["MP2 TOTAL ENERGY"] = mobj.group("mp2tot") - - # Process MP3 - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:D-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp3r") - dmp2 = Decimal(mobj.group(1)) - dmp3 = Decimal(mobj.group(3)) - psivar["MP2 CORRELATION ENERGY"] = dmp2 - psivar["MP2 TOTAL ENERGY"] = mobj.group(2) - psivar["MP3 CORRELATION ENERGY"] = dmp2 + dmp3 - psivar["MP3 TOTAL ENERGY"] = mobj.group(4) - psivar["MP2.5 CORRELATION ENERGY"] = dmp2 + Decimal("0.500000000000") * dmp3 - psivar["MP2.5 TOTAL ENERGY"] = psivar["MP2.5 CORRELATION ENERGY"] + psivar["SCF TOTAL ENERGY"] - psivar["MP3 SINGLES ENERGY"] = Decimal("0.0") - psivar["MP3 DOUBLES ENERGY"] = dmp2 + dmp3 - module = "vcc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:S-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:S-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp3ro") - dmp2 = Decimal(mobj.group(1)) + Decimal(mobj.group(3)) - dmp3 = Decimal(mobj.group(5)) + Decimal(mobj.group(7)) - psivar["MP3 CORRELATION ENERGY"] = dmp2 + dmp3 - psivar["MP3 TOTAL ENERGY"] = mobj.group(8) - psivar["MP2.5 CORRELATION ENERGY"] = dmp2 + Decimal("0.500000000000") * dmp3 - psivar["MP2.5 TOTAL ENERGY"] = psivar["MP2.5 CORRELATION ENERGY"] + psivar["SCF TOTAL ENERGY"] - psivar["MP3 SINGLES ENERGY"] = Decimal(mobj.group(1)) + Decimal(mobj.group(5)) - psivar["MP3 DOUBLES ENERGY"] = Decimal(mobj.group(3)) + Decimal(mobj.group(7)) - module = "vcc" - - mobj = re.search( - # fmt: off - r"^\s*" + r"(?:MP2 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:MP3 correction:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:MP3 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"(?:.*?)" + - r"^\s*" + r"(?:Non-iterative calculation of MP3)" + r".*" + - r"(?:.*?)" + - r"^\s*" + r"(?:Total MP3 energy:)" + r"\s+" + r"(?P" + NUMBER + ")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp3 ncc") - # psivar["MP2 CORRELATION ENERGY"] = mobj.group("mp2corl") - psivar["MP3 CORRELATION ENERGY"] = mobj.group("mp3corl") - psivar["MP3 CORRECTION ENERGY"] = mobj.group("mp3corr") - psivar["MP3 TOTAL ENERGY"] = mobj.group("mp3tot") - # looks like ncc is rhf-only - # psivar["MP2 DOUBLES ENERGY"] = mobj.group("mp2corl") - psivar["MP3 DOUBLES ENERGY"] = mobj.group("mp3corl") - module = "ncc" - - # Process MP4 - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:D-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:Q-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:S-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp4r") - dmp2 = Decimal(mobj.group(1)) - dmp3 = Decimal(mobj.group(3)) - dmp4sdq = Decimal(mobj.group(5)) + Decimal(mobj.group(7)) + Decimal(mobj.group(9)) - psivar["MP2 CORRELATION ENERGY"] = dmp2 - psivar["MP2 TOTAL ENERGY"] = mobj.group(2) - psivar["MP3 CORRELATION ENERGY"] = dmp2 + dmp3 - psivar["MP3 TOTAL ENERGY"] = mobj.group(4) - psivar["MP2.5 CORRELATION ENERGY"] = dmp2 + Decimal("0.500000000000") * dmp3 - psivar["MP2.5 TOTAL ENERGY"] = psivar["MP2.5 CORRELATION ENERGY"] + psivar["SCF TOTAL ENERGY"] - psivar["MP4(SDQ) CORRELATION ENERGY"] = dmp2 + dmp3 + dmp4sdq - psivar["MP4(SDQ) TOTAL ENERGY"] = mobj.group(10) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:S-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(2\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:S-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:D-MBPT\(3\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:L-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:NL-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp4ro") - module = "vcc" - dmp2 = Decimal(mobj.group(1)) + Decimal(mobj.group(3)) - dmp3 = Decimal(mobj.group(5)) + Decimal(mobj.group(7)) - dmp4sdq = Decimal(mobj.group(9)) + Decimal(mobj.group(11)) - psivar["MP2 CORRELATION ENERGY"] = dmp2 - psivar["MP2 TOTAL ENERGY"] = mobj.group(4) - psivar["MP3 CORRELATION ENERGY"] = dmp2 + dmp3 - psivar["MP3 TOTAL ENERGY"] = mobj.group(8) - psivar["MP2.5 CORRELATION ENERGY"] = dmp2 + Decimal("0.500000000000") * dmp3 - psivar["MP2.5 TOTAL ENERGY"] = psivar["MP2.5 CORRELATION ENERGY"] + psivar["SCF TOTAL ENERGY"] - psivar["MP4(SDQ) CORRELATION ENERGY"] = dmp2 + dmp3 + dmp4sdq - psivar["MP4(SDQ) TOTAL ENERGY"] = mobj.group(12) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:D-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:Q-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:S-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:T-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp4tr") - dmp4sdq = Decimal(mobj.group(1)) + Decimal(mobj.group(3)) + Decimal(mobj.group(5)) - dmp4t = Decimal(mobj.group(7)) - psivar["MP4(SDQ) CORRELATION ENERGY"] = psivar["MP3 CORRELATION ENERGY"] + dmp4sdq - psivar["MP4(SDQ) TOTAL ENERGY"] = mobj.group(6) - psivar["MP4(T) CORRECTION ENERGY"] = dmp4t - psivar["MP4 CORRELATION ENERGY"] = psivar["MP3 CORRELATION ENERGY"] + dmp4sdq + dmp4t - psivar["MP4 TOTAL ENERGY"] = mobj.group(8) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:L-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:NL-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:WT12-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'(?:T-MBPT\(4\))' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp4tro") - module = "vcc" - dmp4sdq = Decimal(mobj.group(1)) + Decimal(mobj.group(3)) - dmp4t = Decimal(mobj.group(5)) + Decimal(mobj.group(7)) # WT12 with T, not SDQ - psivar["MP4(SDQ) CORRELATION ENERGY"] = psivar["MP3 CORRELATION ENERGY"] + dmp4sdq - psivar["MP4(SDQ) TOTAL ENERGY"] = mobj.group(4) - psivar["MP4(T) CORRECTION ENERGY"] = dmp4t - psivar["MP4 CORRELATION ENERGY"] = psivar["MP3 CORRELATION ENERGY"] + dmp4sdq + dmp4t - psivar["MP4 TOTAL ENERGY"] = mobj.group(8) - - mobj = re.search( - # fmt: off - r"^\s*" + r"(?:MP2 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:MP3 correction:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:MP3 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:SDQ-MP4 correction:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:SDQ-MP4 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"(" + - r"^\s*" + r"(?:T-MP4 correction:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:Total MP4 correction:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:MP4 correlation energy:)\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r")?" + - r"(?:.*?)" + - r"^\s*" + r"(?:Non-iterative calculation of (MP4|SDQ-MP4))" + r".*" + - r"(?:.*?)" + - r"^\s*" + r"(?:Total (?P(MP4|SDQ-MP4)) energy:)" + r"\s+" + r"(?P" + NUMBER + ")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mp4 ncc") - # psivar["MP2 CORRELATION ENERGY"] = mobj.group("mp2corl") - module = "ncc" - mtd = {"MP4": "MP4", "SDQ-MP4": "MP4(SDQ)"}[mobj.group("mp4flavor")] - psivar["MP3 CORRELATION ENERGY"] = mobj.group("mp3corl") - psivar["MP3 CORRECTION ENERGY"] = mobj.group("mp3corr") - psivar["MP4(SDQ) CORRELATION ENERGY"] = mobj.group("mp4sdqcorl") - # looks like ncc is rhf-only - # psivar["MP2 DOUBLES ENERGY"] = mobj.group("mp2corl") - psivar["MP3 DOUBLES ENERGY"] = mobj.group("mp3corl") - if mtd == "MP4(SDQ)": - psivar["MP4(SDQ) TOTAL ENERGY"] = mobj.group("mp4flavortot") - elif mtd == "MP4": - psivar["MP4(T) CORRECTION ENERGY"] = mobj.group("mp4tcorr") - psivar["MP4 CORRECTION ENERGY"] = mobj.group("mp4corr") - psivar["MP4 TOTAL ENERGY"] = mobj.group("mp4flavortot") - psivar["MP4 CORRELATION ENERGY"] = mobj.group("mp4sdtqcorl") - - # Process CI Iterations - mobj = re.search( - # fmt: off - r'^\s+' + r'(?P(?PQ?CI(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:\d+)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s+' + r"(?P" + NUMBER + r")" + r'\s+DIIS\s*' + - r'^\s*(?:-+)\s*' + - # CI iterations for CISD, CC iterations for QCISD - r'^\s*(?:A miracle (?Phas come|come) to pass. The (CI|CC) iterations have converged.)\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ci with full %s iterating %s" % (mobj.group("fullCI"), mobj.group("iterCI"))) - module = {"has come": "vcc", "come": "ecc"}[mobj.group("ccprog")] - - mtd = mobj.group("iterCI").upper() - psivar[f"{mtd} CORRELATION ENERGY"] = mobj.group("corl") - psivar[f"{mtd} TOTAL ENERGY"] = mobj.group("tot") - - mobj2 = re.search( - # fmt: off - r"^\s+" + r"E\(QCISD\)\s+=\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s+" + r"E\(QCISD\(T\)\)\s+=\s+" + r"(?P" + NUMBER + r")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj2 and mobj.group("fullCI") == "QCISD(T)": - logger.debug("matched qcisd(t)") - psivar["QCISD(T) TOTAL ENERGY"] = mobj2.group("qcisdt") - psivar["QCISD(T) CORRECTION ENERGY"] = Decimal(mobj2.group("qcisdt")) - Decimal(mobj2.group("qcisd")) - psivar["QCISD(T) CORRELATION ENERGY"] = psivar["QCISD(T) TOTAL ENERGY"] - psivar["SCF TOTAL ENERGY"] - - # Process CC Iterations - cciterations = re.compile( - # fmt: off - r'^\s+' + r'(?P(?PL?CC(?:\w+(?:-(?:1|1b|2|3))?))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:\d+)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s+' + r"(?P" + NUMBER + r")" + r'\s+DIIS\s*' + - r'^\s*(?:-+)\s*' + - r'^\s*(?:A miracle (?Phas come|come) to pass. The CC iterations have converged.)\s*$', - # fmt: on - re.MULTILINE - | re.DOTALL, - ) - mobj_list = [m.groupdict() for m in cciterations.finditer(outtext)] - if mobj_list: - first_match = mobj_list[0] - last_match = mobj_list[-1] - - logger.debug("matched cc with full %s iterating %s" % (last_match["fullCC"], last_match["iterCC"])) - module = {"has come": "vcc", "come": "ecc"}[last_match["ccprog"]] - - mobj4 = re.search(r"CALCLEVEL\s+ICLLVL\s+CCSDT-1b", outtext) - mtd = last_match["iterCC"].upper() - if mtd == "CCSDT-1": - if mobj4 and module == "vcc": - mtd = "CCSDT-1B" - else: - mtd = "CCSDT-1A" - elif mtd == "CCSDT-1b": - mtd = "CCSDT-1B" - - mobj5 = re.search(r"BRUECKNER\s+IBRKNR\s+ON", outtext) - if mobj5 and mtd == "CCSD": - # BCCD corl = last Brueckner iter total E - first Brueckner iter HF E - psivar[f"BCCD CORRELATION ENERGY"] = Decimal(last_match["tot"]) - psivar["SCF TOTAL ENERGY"] - psivar[f"BCCD TOTAL ENERGY"] = last_match["tot"] - logger.debug("matched Brueckner iter") - else: - psivar[f"{mtd} CORRELATION ENERGY"] = last_match["corl"] - psivar[f"{mtd} TOTAL ENERGY"] = last_match["tot"] - - mobj3 = re.search(r"SCF reference function: RHF", outtext) - if mobj3 and mtd not in ["CCSDT-1A", "CCSDT-1B", "CCSDT-2", "CCSDT-3", "CCSDT"]: - psivar[f"{mtd} DOUBLES ENERGY"] = last_match["corl"] - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:\d+)' + r'\s+' + r'(?P' + NUMBER + r')\s+' + - NUMBER + r'\s+' + NUMBER + r'\s+' + - NUMBER + r'\s+' + NUMBER + r'\s+' + - r'(' + NUMBER + r')?' + r'(' + r'\s+' + NUMBER + r')?' + r'\s*' + - r'^\s*' + - r'^\s*' + r'(?:\w+(?:-(1a|1b|2|3))? iterations converged .*?)' + - r'^\s*' + - r'^\s*' + r'(?:Total (?P\w+(?:-(1a|1b|2|3))?) energy:)' + r'\s+' + r'(?P' + NUMBER + r')\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ncc cc iter") - # looks like ncc is rhf-only - mtd = mobj.group("iterCC").upper() - psivar[f"{mtd} CORRELATION ENERGY"] = mobj.group("corl") - if mtd not in ["CCSDT-1A", "CCSDT-1B", "CCSDT-2", "CCSDT-3", "CCSDT"]: - psivar[f"{mtd} DOUBLES ENERGY"] = mobj.group("corl") - psivar[f"{mtd} TOTAL ENERGY"] = mobj.group("tot") - module = "ncc" - - mobj = re.search( - # fmt: off - r'^\s+' + r"Beginning iterative solution of (?P\w+(?:-\d)?) equations" + r"\s*" + - r'(?P.*)' + - r'^\s*' + r"It\." + r"\s+" + "Correlation Energy" + r".*" + - r'^\s*(?:-+)\s*' + - r'^\s*' + - r'^\s*' + r'(?:\w+(?:-\d)? iterations converged .*?)' + - r'^\s*' + - r'^\s*' + r'(?:Total \1 energy:)' + r'\s+' + r'(?P' + NUMBER + r')\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - mobj2 = re.findall( - # fmt: off - r"(\d+)" + r"\s+" + r"(?P" + DECIMAL + r")\s+" + - DECIMAL + r"\s+" + DECIMAL + r"\s+" + DECIMAL + r"\s+" + DECIMAL + r"\s+" + r"(" + DECIMAL + r")?" + r"(" + r"\s+" + DECIMAL + r")?", - # fmt: on - mobj.group("iterations"), - ) - if mobj2: - logger.debug("matched ncc cc iter mod5", mobj.groupdict(), mobj2[-1]) - mtd = mobj.group("iterCC").upper() - psivar[f"{mtd} CORRELATION ENERGY"] = mobj2[-1][2] - if mtd not in ["CCSDT-1A", "CCSDT-1B", "CCSDT-2", "CCSDT-3", "CCSDT"]: - psivar[f"{mtd} DOUBLES ENERGY"] = mobj2[-1][2] - psivar[f"{mtd} TOTAL ENERGY"] = mobj.group("tot") - module = "ncc" - - # Process CC(T) - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:E\(SCF\))' + r'\s+=\s+' + NUMBER + r'\s+a\.u\.\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:E\(CCSD\))' + r'\s+=\s+' + NUMBER + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:E\(CCSD\(T\)\))' + r'\s+=\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) vcc") - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - mobj5 = re.search(r"BRUECKNER\s+IBRKNR\s+ON", outtext) - if not mobj5: - psivar["CCSD TOTAL ENERGY"] = mobj.group(2) - psivar["(T) CORRECTION ENERGY"] = Decimal(mobj.group(3)) - Decimal(mobj.group(2)) - psivar["CCSD(T) CORRELATION ENERGY"] = Decimal(mobj.group(3)) - Decimal(mobj.group(1)) - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group(3) - module = "vcc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:E\(CCSD\))' + r'\s+=\s+' + NUMBER + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:E\(CCSD\(T\)\))' + r'\s+=\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) vcc v2") - mobj5 = re.search(r"BRUECKNER\s+IBRKNR\s+ON", outtext) - if mobj5: - psivar["B(T) CORRECTION ENERGY"] = Decimal(mobj.group(2)) - Decimal(mobj.group(1)) - psivar["BCCD(T) TOTAL ENERGY"] = mobj.group(2) - psivar["BCCD(T) CORRELATION ENERGY"] = psivar["BCCD CORRELATION ENERGY"] + psivar["B(T) CORRECTION ENERGY"] - else: - psivar["CCSD TOTAL ENERGY"] = mobj.group(1) - psivar["(T) CORRECTION ENERGY"] = Decimal(mobj.group(2)) - Decimal(mobj.group(1)) - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group(2) - module = "vcc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:E\(SCF\))' + r'\s+=\s*' + NUMBER + r'\s+a\.u\.\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:CCSD energy)' + r'\s+' + NUMBER + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:Total perturbative triples energy:)' + r'\s+' + NUMBER + r'\s*' + - r'^\s*(?:-+)\s*' + - r'^\s+' + r'(?:CCSD\(T\) energy)' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) ecc") - psivar["SCF TOTAL ENERGY"] = mobj.group(1) - psivar["CCSD TOTAL ENERGY"] = mobj.group(2) - psivar["(T) CORRECTION ENERGY"] = mobj.group(3) - psivar["CCSD(T) CORRELATION ENERGY"] = Decimal(mobj.group(4)) - Decimal(mobj.group(1)) - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group(4) - module = "ecc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:HF-SCF energy)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:CCSD energy)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:E4T to CCSD\(T\))' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:E4T \+ E5ST)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'(?:.*?)' + - r'^\s*(?:-+)\s*' + - r'^\s+' + r'(?:CCSD\(T\) energy)' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) ecc v2") - psivar["SCF TOTAL ENERGY"] = mobj.group("hf") - psivar["CCSD TOTAL ENERGY"] = mobj.group("ccsd") - psivar["T(CCSD) CORRECTION ENERGY"] = mobj.group("e4t") - psivar["(T) CORRECTION ENERGY"] = mobj.group("e4te5st") - psivar["CCSD(T) CORRELATION ENERGY"] = Decimal(mobj.group("ccsd_t_")) - Decimal(mobj.group("hf")) - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group("ccsd_t_") - psivar["CCSD+T(CCSD) CORRELATION ENERGY"] = ( - psivar["CCSD CORRELATION ENERGY"] + psivar["T(CCSD) CORRECTION ENERGY"] - ) - psivar["CCSD+T(CCSD) TOTAL ENERGY"] = psivar["CCSD TOTAL ENERGY"] + psivar["T(CCSD) CORRECTION ENERGY"] - module = "ecc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:CCSD energy)' + r'\s+' + NUMBER + r'\s*' + - r'^\s*(?:-+)\s*' + - r'^\s+' + r'(?:CCSD\(T\) energy)' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) lamb") - psivar["CCSD TOTAL ENERGY"] = mobj.group(1) - psivar["(T) CORRECTION ENERGY"] = Decimal(mobj.group(2)) - Decimal(mobj.group(1)) - psivar["CCSD(T) CORRELATION ENERGY"] = Decimal(mobj.group(2)) - psivar["SCF TOTAL ENERGY"] - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group(2) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?:CCSD\(T\) contribution:)\s+' + r'(?P' + NUMBER + ')' + r'\s*' - r'^\s*' + r'(?:CCSD\[T\] contribution:)\s+' + r'(?P' + NUMBER + ')' + r'\s*' - r'^\s*' + r'(?:Total CCSD\(T\) energy:)\s+' + r'(?P' + NUMBER + ')' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) ncc") - psivar["(T) CORRECTION ENERGY"] = mobj.group("tcorr") - psivar["T(CCSD) CORRECTION ENERGY"] = mobj.group("bkttcorr") - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group("ttot") - module = "ncc" - - mobj = re.search( - # fmt: off - r'^\s*' + r'(?:CCSD\[T\] correlation energy:)\s+' + r'(?P' + NUMBER + ')' + r'\s*' - r'^\s*' + r'(?:CCSD\(T\) correlation energy:)\s+' + r'(?P' + NUMBER + ')' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd(t) ncc v2") - psivar["(T) CORRECTION ENERGY"] = mobj.group("tcorr") - psivar["T(CCSD) CORRECTION ENERGY"] = mobj.group("bkttcorr") - psivar["CCSD+T(CCSD) TOTAL ENERGY"] = psivar["T(CCSD) CORRECTION ENERGY"] + psivar["CCSD TOTAL ENERGY"] - psivar["CCSD+T(CCSD) CORRELATION ENERGY"] = ( - psivar["T(CCSD) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - ) - psivar["CCSD(T) TOTAL ENERGY"] = psivar["(T) CORRECTION ENERGY"] + psivar["CCSD TOTAL ENERGY"] - psivar["CCSD(T) CORRELATION ENERGY"] = psivar["(T) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - module = "ncc" - - mobj = re.search( - # fmt: off - r"^\s*" + r"(?:Lambda-CCSD iterations converged .*)" + - r'(?:.*?)' + - r"^\s*" + r"(?:CCSD\[T\] correlation energy:)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\(T\) correlation energy:)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\[T\]_L correlation energy:)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\(T\)_L correlation energy:)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r'(?:.*?)' + - r"^\s*" + r"(?:Non-iterative calculation of CCSD\(T\)_L .*)" + - r'(?:.*?)' + - r"^\s*" + r"(?:Total CCSD\(T\)_L energy:)\s+" + r"(?P" + NUMBER + r")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched a-ccsd(t) ncc") - psivar["(T) CORRECTION ENERGY"] = mobj.group("tcorr") - psivar["T(CCSD) CORRECTION ENERGY"] = mobj.group("bkttcorr") - psivar["A-(T) CORRECTION ENERGY"] = mobj.group("atcorr") - psivar["CCSD+T(CCSD) TOTAL ENERGY"] = psivar["T(CCSD) CORRECTION ENERGY"] + psivar["CCSD TOTAL ENERGY"] - psivar["CCSD+T(CCSD) CORRELATION ENERGY"] = ( - psivar["T(CCSD) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - ) - psivar["CCSD(T) TOTAL ENERGY"] = psivar["(T) CORRECTION ENERGY"] + psivar["CCSD TOTAL ENERGY"] - psivar["CCSD(T) CORRELATION ENERGY"] = psivar["(T) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - psivar["A-CCSD(T) TOTAL ENERGY"] = mobj.group("accsdttot") - psivar["A-CCSD(T) CORRELATION ENERGY"] = psivar["A-(T) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - module = "ncc" - - mobj = re.search( - # fmt: off - r"^\s*" + r"(?:A miracle come to pass. The CC iterations have converged.)" + r"\s*" + - r'(?:.*?)' + - r"^\s*" + r"(?:HF-SCF energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:MP2 correlation energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:MP2 energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD correlation energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:Delta ET to CCSD\[T\]_L)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\[T\]_L energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:Delta ET to CCSD\(T\)_L)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\(T\)_L energy )\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r'(?:.*?)' + - r"^\s*" + r"(?:Non-iterative perturbative treatment of triple)" + r"\s*" + - r"^\s*" + r"(?:excitations using the CCSD\(T\)_L method:)" + r"\s*" + - r'(?:.*?)' + - r"^\s*" + r"(?:HF-SCF energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:MP2 correlation energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:MP2 energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD correlation energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:Delta ET to CCSD\(T\)_L)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"^\s*" + r"(?:CCSD\(T\)_L energy)\s+" + r"(?P" + NUMBER + r")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched a-ccsd(t) ecc") - psivar["HF TOTAL ENERGY"] = mobj.group("hf") - psivar["MP2 CORRELATION ENERGY"] = mobj.group("mp2corl") - psivar["CCSD CORRELATION ENERGY"] = mobj.group("ccsdcorl") - psivar["A-(T) CORRECTION ENERGY"] = mobj.group("atcorr") - psivar["A-CCSD(T) TOTAL ENERGY"] = mobj.group("accsdt") - psivar["A-CCSD(T) CORRELATION ENERGY"] = psivar["A-(T) CORRECTION ENERGY"] + psivar["CCSD CORRELATION ENERGY"] - - mobj3 = re.search(r"SCF reference function: (R|U)HF", outtext) - if mobj3: - psivar["CCSD SINGLES ENERGY"] = Decimal("0.0") - module = "ecc" - - mobj = re.search( - # fmt: off - r"^\s*" + r"(?:E\(CCSD\))" + r"\s+=\s+" + r"(?P" + NUMBER + ")" + r"\s*" + - r"^\s*" + r"(?:E\(CCSD \+ T\(CCSD\)\))" + r"\s*=\s*" + r"(?P" + NUMBER + ")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd+t(ccsd) vcc") - psivar["CCSD TOTAL ENERGY"] = mobj.group("ccsdtot") - psivar["CCSD+T(CCSD) TOTAL ENERGY"] = mobj.group("ccsdtccsdtot") - psivar["CCSD+T(CCSD) CORRELATION ENERGY"] = psivar["CCSD+T(CCSD) TOTAL ENERGY"] - psivar["SCF TOTAL ENERGY"] - mobj3 = re.search(r"Reference function is (R|U)HF Hartree-Fock", outtext) - if mobj3: - psivar["CCSD SINGLES ENERGY"] = Decimal("0.0") - module = "vcc" - - mobj = re.search( - # fmt: off - r'^\s*' + r'(?:CCSDT\[Q\] correlation energy:)\s+' + r'(?P' + NUMBER + ')' + r'\s*' - r'^\s*' + r'(?:CCSDT\(Q\) correlation energy:)\s+' + r'(?P' + NUMBER + ')' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsdt(q) ncc") - psivar["(Q) CORRECTION ENERGY"] = mobj.group("tcorr") - psivar["[Q] CORRECTION ENERGY"] = mobj.group("bkttcorr") - psivar["CCSDT(Q) TOTAL ENERGY"] = psivar["(Q) CORRECTION ENERGY"] + psivar["CCSDT TOTAL ENERGY"] - psivar["CCSDT(Q) CORRELATION ENERGY"] = psivar["(Q) CORRECTION ENERGY"] + psivar["CCSDT CORRELATION ENERGY"] - module = "ncc" - - # Process DBOC - mobj = re.search( - # fmt: off - r'^\s*' + r'(?:The total diagonal Born-Oppenheimer correction \(DBOC\) is:)\s+' + - r'(?P' + NUMBER + ')' + r'\s*a.u.\s*', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched dboc ecc") - psivar["CCSD DBOC ENERGY"] = mobj.group("dboc") - module = "ecc" - - # Process SCS-CC - mobj = re.search( - # fmt: off - r'^\s+' + r'(?P(?PCC(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s*' + r'(?:@CCENRG-I, Correlation energies.)' + r'\s+(?:ECCAA)\s+' + NUMBER + r'\s*' + - r'^\s+(?:ECCBB)\s+' + NUMBER + r'\s*' + - r'^\s+(?:ECCAB)\s+' + NUMBER + r'\s*' + - r'^\s+(?:Total)\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: # PRINT=2 to get SCS-CC components - logger.debug("matched scscc") - if float(mobj.group(4)) == 0.0: - ss = 2 * Decimal(mobj.group(3)) - else: - ss = Decimal(mobj.group(3)) + Decimal(mobj.group(4)) - - mobj5 = re.search(r"BRUECKNER\s+IBRKNR\s+ON", outtext) - if not mobj5: - if not ( - re.search(r"executable xvcc finished", outtext) - and re.search(r"The reference state is a ROHF wave function.", outtext) - ): - psivar["%s SAME-SPIN CORRELATION ENERGY" % (mobj.group("iterCC"))] = ss - psivar["%s OPPOSITE-SPIN CORRELATION ENERGY" % (mobj.group("iterCC"))] = mobj.group(5) - psivar["%s CORRELATION ENERGY" % (mobj.group("iterCC"))] = mobj.group(6) - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?P(?PCC(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s+' + r'Amplitude equations converged in' + r'\s*\d+\s*' + r'iterations.\s*' + - r'(?:.*?)' + - r'^\s+' + r'The AA contribution to the correlation energy is:\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'The BB contribution to the correlation energy is:\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'The AB contribution to the correlation energy is:\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'The total correlation energy is\s+' + NUMBER + r'\s+a.u.\s*' + - r'(?:.*?)' + - # r'^\s+' + r'The CC iterations have converged.' + r'\s*$', - r'^\s+' + r'(?:A miracle come to pass. )?' + r'The CC iterations have converged.' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: # PRINT=2 to get SCS components - logger.debug("matched scscc2") - iterCC = mobj.group("iterCC") - mobj3 = re.search(r"The reference state is a ROHF wave function.", outtext) - mobj4 = re.search(r"executable xvcc finished", outtext) - if mobj4: # vcc - if not (iterCC == "CCD" and mobj3): - # uncertain if ROHF CCD correct - psivar[f"{iterCC} OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(5) - if not mobj3: - psivar[f"{iterCC} SAME-SPIN CORRELATION ENERGY"] = Decimal(mobj.group(3)) + Decimal(mobj.group(4)) - else: # ecc - psivar[f"{iterCC} SAME-SPIN CORRELATION ENERGY"] = Decimal(mobj.group(3)) + Decimal(mobj.group(4)) - if not mobj3: - psivar[f"{iterCC} OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(5) - psivar[f"{iterCC} CORRELATION ENERGY"] = mobj.group(6) - - mobj = re.search( - # fmt: off - #r'^\s+' + r'(?P(?PL?CC(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - # better one for LCC and one for CC, right? - r'^\s+' + r'(?P(?PLCC(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s+' + r'Amplitude equations converged in' + r'\s*\d+\s*' + r'iterations.\s*' + - r'(?:.*?)' + - r'^\s+' + r'The AA contribution to the correlation energy is:\s+' + r"(?P" + NUMBER + r")" + r'\s+a.u.\s*' + - r'(^\s+' + r'The BB contribution to the correlation energy is:\s+' + r"(?P" + NUMBER + r")" + r'\s+a.u.\s*' + r")?" + - r'^\s+' + r'The AB contribution to the correlation energy is:\s+' + r"(?P" + NUMBER + r")" + r'\s+a.u.\s*' + - r'^\s+' + r'The total correlation energy is\s+' + r"(?P" + NUMBER + r")" + r'\s+a.u.\s*' + - r'(?:.*?)' + - r'^\s+' + r'(?:A miracle come to pass. )?' + r'The CC iterations have converged.' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: # PRINT=2 to get SCS components - logger.debug("matched scslccd") - mobj3 = re.search(r"The reference state is a ROHF wave function.", outtext) - mobj4 = re.search(r"executable xvcc finished", outtext) - iterCC = mobj.group("iterCC") - if mobj4: # vcc - if mobj.group("BB"): - aabb = Decimal(mobj.group("AA")) + Decimal(mobj.group("BB")) - else: - aabb = Decimal("2") * Decimal(mobj.group("AA")) - psivar[f"{iterCC} OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group("AB") - if not mobj3: - psivar[f"{iterCC} SAME-SPIN CORRELATION ENERGY"] = aabb - psivar["%s CORRELATION ENERGY" % (mobj.group("iterCC"))] = mobj.group("corl") - - mobj = re.search( - # fmt: off - r'^\s+' + r'(?P(?PCC(?:\w+))(?:\(T\))?)' + r'\s+(?:energy will be calculated.)\s*' + - r'(?:.*?)' + - r'^\s+' + r'Amplitude equations converged in' + r'\s*\d+\s*' + r'iterations.\s*' + - r'(?:.*?)' + - r'^\s+' + r'The AA contribution to the correlation energy is:\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'The AB contribution to the correlation energy is:\s+' + NUMBER + r'\s+a.u.\s*' + - r'^\s+' + r'The total correlation energy is\s+' + NUMBER + r'\s+a.u.\s*' + - r'(?:.*?)' + - # r'^\s+' + r'The CC iterations have converged.' + r'\s*$', - r'^\s+' + r'(?:A miracle come to pass. )?' + r'The CC iterations have converged.' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: # PRINT=2 to get SCS components - logger.debug("matched scscc rhf", mobj.groups()) - psivar["%s SAME-SPIN CORRELATION ENERGY" % (mobj.group("iterCC"))] = 2 * Decimal(mobj.group(3)) - psivar["%s OPPOSITE-SPIN CORRELATION ENERGY" % (mobj.group("iterCC"))] = mobj.group(4) - psivar["%s CORRELATION ENERGY" % (mobj.group("iterCC"))] = mobj.group(5) - - # Process gradient - mobj = re.search( - # fmt: off - r'\s+' + r'Molecular gradient' + r'\s*' + - r'\s+' + r'------------------' + r'\s*' + - r'\s+' + r'\n' + - r'(?:(?:\s+[A-Z]+\s*#\d+\s+[xyz]\s+[-+]?\d+\.\d+\s*\n)+)' + # optional, it seems - r'\n\n' + # optional, it seems - r'((?:\s+[A-Z]+\s*#\d+\s+\d?\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s*\n)+)' + - r'\n\n' + - r'\s+' + 'Molecular gradient norm', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched molgrad") - atoms = [] - psivar_grad = [] - for line in mobj.group(1).splitlines(): - lline = line.split() - atoms.append(lline[0]) - # psivar_gradient.append([Decimal(lline[-3]), Decimal(lline[-2]), Decimal(lline[-1])]) - psivar_grad.append([float(lline[-3]), float(lline[-2]), float(lline[-1])]) - - # Process geometry - mobj = re.search( - # fmt: off - # r'\s+(?:-+)\s*' + - # r'^\s+' + r'Z-matrix Atomic Coordinates (in bohr)' + r'\s*' + - r'^\s+' + r'Symbol Number X Y Z' + r'\s*' + - r'^\s+(?:-+)\s*' + - r'((?:\s+[A-Z]+\s+([0-9]+|\*\*\*)\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s*\n)+)' + # allows ghosts - r'^\s+(?:-+)\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched geom") - molxyz = "%d bohr\n\n" % len(mobj.group(1).splitlines()) - for line in mobj.group(1).splitlines(): - lline = line.split() - if lline[1] == "***": - tag = "@Xe" # potentially dangerous bypass - else: - tag = lline[0] - molxyz += "%s %16s %16s %16s\n" % (tag, lline[-3], lline[-2], lline[-1]) - # Rather a dinky Molecule as no ghost, charge, or multiplicity - psivar_coord = Molecule( - validate=False, - **qcel.molparse.to_schema( - qcel.molparse.from_string(molxyz, dtype="xyz+", fix_com=True, fix_orientation=True)["qm"], dtype=2 - ), - ) - - # Process atom geometry - mobj = re.search(r"^\s+" + r"@GETXYZ-I, 1 atoms read from ZMAT." + r"\s*$", outtext, re.MULTILINE) - mobj2 = re.search( - r"^([A-Z]+)#1" + r"\s+" + NUMBER + r"\s+" + NUMBER + r"\s+" + NUMBER + r"\s*$", outtext, re.MULTILINE - ) - if mobj and mobj2: - logger.debug("matched atom2") # unsavory for when atom never printed except for basis file - # Dinky Molecule - molxyz = "1 bohr\n\n%s 0.0 0.0 0.0\n" % (mobj2.group(1)) - psivar_coord = Molecule( - validate=False, - **qcel.molparse.to_schema( - qcel.molparse.from_string(molxyz, dtype="xyz+", fix_com=True, fix_orientation=True)["qm"], dtype=2 - ), - ) - - mobj = re.search( - # fmt: off - r'^\s+' + r'@GETXYZ-I, 1 atoms read from ZMAT.' + r'\s*' + - r'^\s+' + r'[0-9]+\s+([A-Z]+)\s+[0-9]+\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched atom") - # Dinky Molecule - molxyz = "1 bohr\n\n%s 0.0 0.0 0.0\n" % (mobj.group(1)) - psivar_coord = Molecule( - validate=False, - **qcel.molparse.to_schema( - qcel.molparse.from_string(molxyz, dtype="xyz+", fix_com=True, fix_orientation=True)["qm"], dtype=2 - ), - ) - - # Process error codes - mobj = re.search( - # fmt: off - r"^\s*" + r"--executable " + r"(?P\w+)" + r" finished with status" + r"\s+" + r"(?P[1-9][0-9]*)", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched error") - # psivar['CFOUR ERROR CODE'] = mobj.group(2) - c4exe = mobj.group("c4exe") - errcode = int(mobj.group("errcode")) - if errcode != 0: - error += f"--executable {c4exe} finished with status {errcode}" - if c4exe in ["xvcc", "xecc", "xncc"]: - module = c4exe[1:] - - # Process CURRENT energies (TODO: needs better way) - if "SCF TOTAL ENERGY" in psivar: - psivar["CURRENT REFERENCE ENERGY"] = psivar["SCF TOTAL ENERGY"] - psivar["CURRENT ENERGY"] = psivar["SCF TOTAL ENERGY"] - psivar["HF TOTAL ENERGY"] = psivar["SCF TOTAL ENERGY"] - - if "MP2 TOTAL ENERGY" in psivar and "MP2 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["MP2 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["MP2 TOTAL ENERGY"] - - if "MP3 TOTAL ENERGY" in psivar and "MP3 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["MP3 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["MP3 TOTAL ENERGY"] - - if "MP4(SDQ) TOTAL ENERGY" in psivar and "MP4(SDQ) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["MP4(SDQ) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["MP4(SDQ) TOTAL ENERGY"] - - if "MP4 TOTAL ENERGY" in psivar and "MP4 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["MP4 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["MP4 TOTAL ENERGY"] - - if "CISD TOTAL ENERGY" in psivar and "CISD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CISD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CISD TOTAL ENERGY"] - - if "QCISD TOTAL ENERGY" in psivar and "QCISD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["QCISD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["QCISD TOTAL ENERGY"] - - if "QCISD(T) TOTAL ENERGY" in psivar and "QCISD(T) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["QCISD(T) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["QCISD(T) TOTAL ENERGY"] - - if "LCCD TOTAL ENERGY" in psivar and "LCCD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["LCCD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["LCCD TOTAL ENERGY"] - - if "LCCSD TOTAL ENERGY" in psivar and "LCCSD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["LCCSD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["LCCSD TOTAL ENERGY"] - - # if ('%s TOTAL ENERGY' % (mobj.group('fullCC')) in psivar) and \ - # ('%s CORRELATION ENERGY' % (mobj.group('fullCC')) in psivar): - # psivar['CURRENT CORRELATION ENERGY'] = psivar['%s CORRELATION ENERGY' % (mobj.group('fullCC')] - # psivar['CURRENT ENERGY'] = psivar['%s TOTAL ENERGY' % (mobj.group('fullCC')] - - if "CC2 TOTAL ENERGY" in psivar and "CC2 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CC2 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CC2 TOTAL ENERGY"] - - if "CCD TOTAL ENERGY" in psivar and "CCD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCD TOTAL ENERGY"] - - if "BCCD TOTAL ENERGY" in psivar and "BCCD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["BCCD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["BCCD TOTAL ENERGY"] - - if "CCSD TOTAL ENERGY" in psivar and "CCSD CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSD CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSD TOTAL ENERGY"] - - if "CCSD+T(CCSD) TOTAL ENERGY" in psivar and "CCSD+T(CCSD) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSD+T(CCSD) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSD+T(CCSD) TOTAL ENERGY"] - - if "CCSD(T) TOTAL ENERGY" in psivar and "CCSD(T) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSD(T) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSD(T) TOTAL ENERGY"] - - if "A-CCSD(T) TOTAL ENERGY" in psivar and "A-CCSD(T) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["A-CCSD(T) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["A-CCSD(T) TOTAL ENERGY"] - - if "BCCD(T) TOTAL ENERGY" in psivar and "BCCD(T) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["BCCD(T) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["BCCD(T) TOTAL ENERGY"] - - if "CC3 TOTAL ENERGY" in psivar and "CC3 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CC3 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CC3 TOTAL ENERGY"] - - if "CCSDT TOTAL ENERGY" in psivar and "CCSDT CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT TOTAL ENERGY"] - - if "CCSDT-1A TOTAL ENERGY" in psivar and "CCSDT-1A CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT-1A CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT-1A TOTAL ENERGY"] - - if "CCSDT-1B TOTAL ENERGY" in psivar and "CCSDT-1B CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT-1B CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT-1B TOTAL ENERGY"] - - if "CCSDT-2 TOTAL ENERGY" in psivar and "CCSDT-2 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT-2 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT-2 TOTAL ENERGY"] - - if "CCSDT-3 TOTAL ENERGY" in psivar and "CCSDT-3 CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT-3 CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT-3 TOTAL ENERGY"] - - if "CCSDT(Q) TOTAL ENERGY" in psivar and "CCSDT(Q) CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDT(Q) CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDT(Q) TOTAL ENERGY"] - - if "CCSDTQ TOTAL ENERGY" in psivar and "CCSDTQ CORRELATION ENERGY" in psivar: - psivar["CURRENT CORRELATION ENERGY"] = psivar["CCSDTQ CORRELATION ENERGY"] - psivar["CURRENT ENERGY"] = psivar["CCSDTQ TOTAL ENERGY"] - - psivar[f"N ATOMS"] = len(psivar_coord.symbols) - - return psivar, psivar_coord, psivar_grad, version, module, error - - -def harvest(in_mol: Molecule, method: str, c4out, **largs): - """Parses all the pieces of output from Cfour: the stdout in - *c4out* and the contents of various scratch files like GRD stored - in their namesake keys in *largs*. Since all Cfour output uses - its own orientation and atom ordering for the given molecule, - a qcdb.Molecule *in_mol*, if supplied, is used to transform the - Cfour output back into consistency with *in_mol*. - - """ - # Collect results from output file and subsidiary files - qcvars, out_mol, outGrad, version, module, error = harvest_output(c4out) - - if largs.get("GRD"): - grdMol, grdGrad = harvest_GRD(largs["GRD"]) - else: - grdMol, grdGrad = None, None - - if largs.get("FCMFINAL"): - fcmHess = load_hessian(largs["FCMFINAL"], dtype="fcmfinal") - if np.count_nonzero(fcmHess) == 0: - fcmHess = None - else: - fcmHess = None - - if largs.get("DIPOL"): - dipolDip = harvest_DIPOL(largs["DIPOL"]) - else: - dipolDip = None - - # Sometimes the hierarchical setting of CURRENT breaks down - if method == "ccsd+t(ccsd)": - qcvars["CURRENT CORRELATION ENERGY"] = qcvars["CCSD+T(CCSD) CORRELATION ENERGY"] - qcvars["CURRENT ENERGY"] = qcvars["CCSD+T(CCSD) TOTAL ENERGY"] - - if fcmHess is not None and method in ["hf", "scf"]: - # MP2 available in HF Hessian so need to counteract - qcvars.pop("CURRENT CORRELATION ENERGY") - qcvars["CURRENT ENERGY"] = qcvars["HF TOTAL ENERGY"] - - # Reconcile the coordinate information: several cases - # Case p4Mol GRD Check consistency Apply orientation? ReturnMol (1-19-2014) - # sp with mol thru cfour {} None None outMol N.C. outMol - # opt with mol thru cfour {} None grdMol outMol && grdMol N.C. grdMol - # sp with mol thru molecule {} p4Mol None p4Mol && outMol p4Mol <-- outMol p4Mol (same as input arg) - # opt with mol thru molecule {} p4Mol grdMol p4Mol && outMol && grdMol p4Mol <-- grdMol p4Mol (same as input arg) - # Jul 2021: above describes longtime orientation strategy. Now, mol through cfour {} no longer allowed, and fix_* signal whether input (T) or cfour native (F) frames for returned data. - - if out_mol: - if grdMol: - if abs(out_mol.nuclear_repulsion_energy() - grdMol.nuclear_repulsion_energy()) > 1.0e-3: - raise ValueError( - f"""CFOUR outfile (NRE: {out_mol.nuclear_repulsion_energy()} inconsistent with CFOUR GRD (NRE: {grdMol.nuclear_repulsion_energy()}).""" - ) - if in_mol: - if abs(out_mol.nuclear_repulsion_energy() - in_mol.nuclear_repulsion_energy()) > 1.0e-3: - raise ValueError( - f"""CFOUR outfile (NRE: {out_mol.nuclear_repulsion_energy()}) inconsistent with AtomicInput.molecule (NRE: {in_mol.nuclear_repulsion_energy()}).""" - ) - else: - raise ValueError("""No coordinate information extracted from CFOUR output.""") - - # Set up array reorientation object(s) - if in_mol and out_mol and grdMol: - # Jul 2021: apparently GRD and FCMFINAL can have different atom orderings :-) - - _, data = grdMol.align(out_mol, atoms_map=False, mols_align=True, verbose=0) - g2o_mill = data["mill"] - - oriCoord = g2o_mill.align_coordinates(grdMol.geometry) - oriGrad = g2o_mill.align_gradient(np.array(grdGrad)) - - if dipolDip is None: - oriDip = None - else: - oriDip = g2o_mill.align_vector(dipolDip) - - if fcmHess is None: - oriHess = None - else: - oriHess = fcmHess - - # Frame considerations - if in_mol.fix_com and in_mol.fix_orientation: - # Impose input frame if important as signalled by fix_*=T - return_mol = in_mol - _, data = out_mol.align(in_mol, atoms_map=False, mols_align=True, generic_ghosts=True, verbose=0) - mill = data["mill"] - - else: - return_mol, _ = in_mol.align(out_mol, atoms_map=False, mols_align=True, generic_ghosts=True, verbose=0) - mill = qcel.molutil.compute_scramble( - len(in_mol.symbols), do_resort=False, do_shift=False, do_rotate=False, do_mirror=False - ) # identity AlignmentMill - - # _, data = out_mol.align(in_mol, atoms_map=False, mols_align=True, verbose=0) - # o2i_mill = data["mill"] - # - # oriCoord = o2i_mill.align_coordinates(oriCoord) - # oriGrad = o2i_mill.align_gradient(oriGrad) - # if oriDip is not None: - # oriDip = o2i_mill.align_vector(oriDip) - # if oriHess is not None: - # oriHess = o2i_mill.align_hessian(oriHess) - - oriCoord = mill.align_coordinates(oriCoord) - oriGrad = mill.align_gradient(oriGrad) - if oriDip is not None: - oriDip = mill.align_vector(oriDip) - if oriHess is not None: - oriHess = mill.align_hessian(oriHess) - - elif in_mol and out_mol: - # TODO watch out - haven't seen atom_map=False yet - - if in_mol.fix_com and in_mol.fix_orientation: - # Impose input frame if important as signalled by fix_*=T - return_mol = in_mol - _, data = out_mol.align(in_mol, atoms_map=True, mols_align=True, generic_ghosts=True, verbose=0) - mill = data["mill"] - - else: - return_mol, _ = in_mol.align(out_mol, atoms_map=False, mols_align=True, generic_ghosts=True, verbose=0) - mill = qcel.molutil.compute_scramble( - len(in_mol.symbols), do_resort=False, do_shift=False, do_rotate=False, do_mirror=False - ) # identity AlignmentMill - - oriCoord = mill.align_coordinates(out_mol.geometry) # (np_out=True)) - oriGrad = None - oriHess = None # I don't think we ever get FCMFINAL w/o GRAD - if dipolDip is None: - oriDip = None - else: - oriDip = mill.align_vector(np.array(dipolDip)) - # p4c4 = OrientMols(in_mol, out_mol) - # oriCoord = p4c4.transform_coordinates2(out_mol) - # oriGrad = None - # oriDip = None if dipolDip is None else p4c4.transform_vector(dipolDip) - - elif out_mol: - oriGrad = None - oriHess = None - oriDip = None if dipolDip is None else dipolDip - - # not sure of purpose but it interferes now that return_mol overwrites atres.mol - # return_mol = None if in_mol else grdMol - - if oriDip is not None: - qcvars["CURRENT DIPOLE"] = oriDip - oriDip *= qcel.constants.dipmom_au2debye - # outPsivar["CURRENT DIPOLE X"] = oriDip[0] - # outPsivar["CURRENT DIPOLE Y"] = oriDip[1] - # outPsivar["CURRENT DIPOLE Z"] = oriDip[2] - # outPsivar['CURRENT DIPOLE X'] = str(oriDip[0] * psi_dipmom_au2debye) - # outPsivar['CURRENT DIPOLE Y'] = str(oriDip[1] * psi_dipmom_au2debye) - # outPsivar['CURRENT DIPOLE Z'] = str(oriDip[2] * psi_dipmom_au2debye) - - if oriGrad is not None: - return_grad = oriGrad - elif grdGrad is not None: - return_grad = grdGrad - else: - return_grad = None - - if oriHess is not None: - return_hess = oriHess - else: - return_hess = None - - # if oriCoord is not None: - # retCoord = oriCoord - # else: - # retCoord = None - - return qcvars, return_hess, return_grad, return_mol, version, module, error - - -def harvest_GRD(grd): - """Parses the contents *grd* of the Cfour GRD file into the gradient - array and coordinate information. The coordinate info is converted - into a rather dinky Molecule (no charge, multiplicity, or fragment), - but this is these coordinates that govern the reading of molecule - orientation by Cfour. Return qcel.models.Molecule and gradient array. - - """ - grd = grd.splitlines() - Nat = int(grd[0].split()[0]) - molxyz = f"{Nat} bohr\n\n" - - grad = [] - for at in range(Nat): - mline = grd[at + 1].split() - - # "@Xe" is potentially dangerous bypass for ghosts - el = "@Xe" if int(float(mline[0])) == 0 else qcel.periodictable.to_E(int(float(mline[0]))) - molxyz += "%s %16s %16s %16s\n" % (el, mline[-3], mline[-2], mline[-1]) - lline = grd[at + 1 + Nat].split() - grad.append([float(lline[-3]), float(lline[-2]), float(lline[-1])]) - grad = np.array(grad).reshape((-1, 3)) - - mol = Molecule( - validate=False, - **qcel.molparse.to_schema( - qcel.molparse.from_string(molxyz, dtype="xyz+", fix_com=True, fix_orientation=True)["qm"], dtype=2 - ), - ) - - return mol, grad - - -def harvest_DIPOL(dipol): - """Parses the contents *dipol* of the Cfour DIPOL file into a dipol vector.""" - dipol = dipol.splitlines() - lline = dipol[0].split() - dip = np.array([float(lline[0]), float(lline[1]), float(lline[2])]) - - # return None if empty else dip - return dip diff --git a/qcengine/programs/cfour/keywords.py b/qcengine/programs/cfour/keywords.py deleted file mode 100644 index 41f323554..000000000 --- a/qcengine/programs/cfour/keywords.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Any, Dict, Tuple - -from qcengine.exceptions import InputError - - -def format_keywords(keywords: Dict[str, Any]) -> str: - """Form keywords deck from dictionary `keywords` where keys are CFOUR keyword ("__" separating - any nested-module keywords) strings and values are Python formatted. - - """ - text = [] - - keywords = {k.upper(): v for k, v in keywords.items()} - for key, val in sorted(keywords.items()): - text.append("=".join(format_keyword(key, val))) - - text = "\n".join(text) - text = "\n\n*CFOUR(" + text + ")\n\n" - - return text - - -def format_keyword(keyword: str, val: Any) -> Tuple[str, str]: - """Reformat keyword's value from Python into CFOUR-speak. Arrays are the primary target.""" - keyword = keyword.upper() - - # Transform booleans into integers - if val is True: - text = "1" - elif val is False: - text = "0" - - # Transform list from [[3, 0, 1, 1], [2, 0, 1, 0]] --> 3-0-1-1/2-0-1-0 - elif isinstance(val, list): - if type(val[0]).__name__ == "list": - if type(val[0][0]).__name__ == "list": - raise InputError("Option has level of array nesting inconsistent with CFOUR.") - else: - # option is 2D array - text = "/".join("-".join(map(str, no)) for no in val) - else: - # option is plain 1D array - if keyword in ["ESTATE_SYM", "CFOUR_ESTATE_SYM"]: - # [3, 1, 0, 2] --> 3/1/0/2 - text = "/".join(map(str, val)) - else: - # [3, 1, 0, 2] --> 3-1-0-2 - text = "-".join(map(str, val)) - - # Transform the basis sets that *must* be lowercase - elif keyword in ["CFOUR_BASIS", "BASIS"] and val.upper() in [ - "SVP", - "DZP", - "TZP", - "TZP2P", - "QZ2P", - "PZ3D2F", - "13S9P4D3F", - ]: - text = str(val.lower()) - - # Transform the methods that *must* be mixed case - elif keyword in ["CFOUR_CALC_LEVEL", "CALC_LEVEL"] and val.upper() == "CCSDT-1B": - text = "CCSDT-1b" - - # No Transform - else: - text = str(val).upper() - - return keyword, text diff --git a/qcengine/programs/cfour/runner.py b/qcengine/programs/cfour/runner.py deleted file mode 100644 index 6fe67e2b6..000000000 --- a/qcengine/programs/cfour/runner.py +++ /dev/null @@ -1,230 +0,0 @@ -"""Compute quantum chemistry using Mainz-Austin-Budapest-Gainesville's CFOUR executable.""" - -import copy -import pprint -from decimal import Decimal -from pathlib import Path -from typing import Any, Dict, Optional, Tuple - -import numpy as np -from qcelemental.models import AtomicInput, AtomicResult, BasisSet, Provenance -from qcelemental.util import safe_version, which - -from ...exceptions import InputError, UnknownError -from ...util import execute -from ..model import ProgramHarness -from ..qcvar_identities_resources import build_atomicproperties, build_out -from ..util import error_stamp -from .germinate import muster_modelchem -from .harvester import harvest -from .keywords import format_keywords - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) - - -class CFOURHarness(ProgramHarness): - """ - - Notes - ----- - * Looks for basis set file ``../basis/GENBAS`` from ``xcfour`` executable. If this doesn't work, file an issue. - - """ - - _defaults = { - "name": "CFOUR", - "scratch": True, - "thread_safe": False, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "xcfour", return_bool=True, raise_error=raise_error, raise_msg="Please install via http://cfour.de/" - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("xcfour") - if which_prog not in self.version_cache: - success, output = execute([which_prog, "ZMAT"], {"ZMAT": "\nHe\n\n"}) - - if success: - for line in output["stdout"].splitlines(): - if "Version" in line: - branch = " ".join(line.strip().split()[1:]) - self.version_cache[which_prog] = safe_version(branch) - - return self.version_cache[which_prog] - - def compute(self, input_model: AtomicInput, config: "TaskConfig") -> AtomicResult: - self.found(raise_error=True) - - job_inputs = self.build_input(input_model, config) - success, dexe = self.execute(job_inputs) - - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - dexe["outfiles"]["input"] = job_inputs["infiles"]["ZMAT"] - return self.parse_output(dexe["outfiles"], input_model) - - def build_input( - self, input_model: AtomicInput, config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - cfourrec = { - "infiles": {}, - "scratch_directory": config.scratch_directory, - "scratch_messy": config.scratch_messy, - } - - opts = copy.deepcopy(input_model.keywords) - - # Handle memory - # for cfour, [GiB] --> [QW] - opts["memory_size"] = int(config.memory * (1024**3) / 8) - opts["mem_unit"] = "integerwords" - - # Handle molecule - molcmd, moldata = input_model.molecule.to_string(dtype="cfour", units="Bohr", return_data=True) - opts.update(moldata["keywords"]) - - # Handle calc type and quantum chemical method - mdcopts = muster_modelchem(input_model.model.method, input_model.driver) - opts.update(mdcopts) - - # Handle basis set - if isinstance(input_model.model.basis, BasisSet): - raise InputError("QCSchema BasisSet for model.basis not implemented. Use string basis name.") - if input_model.model.basis is None: - raise InputError("None for model.basis is not useable.") - - # * why, yes, this is highly questionable - # * assuming relative file location between xcfour exe and GENBAS file - # * reading a multi MB file into the inputs dict - if all(input_model.molecule.real): - opts["basis"] = input_model.model.basis - bascmd = "" - else: - # * note not getting per-basis casing like if it passed through format_keywords - opts["basis"] = "SPECIAL" - text = [ - ( - f"""H:6-31G""" - if (elem == "H" and input_model.model.basis.upper() == "6-31G*") - else f"""{elem.upper()}:{input_model.model.basis.upper()}""" - ) - for iat, elem in enumerate(input_model.molecule.symbols) - ] - text.append("") - text.append("") - bascmd = "\n".join(text) - - # Handle conversion from schema (flat key/value) keywords into local format - optcmd = format_keywords(opts) - - xcfour = which("xcfour") - genbas = Path(xcfour).parent.parent / "basis" / "GENBAS" - cfourrec["infiles"]["ZMAT"] = molcmd + optcmd + bascmd - cfourrec["infiles"]["GENBAS"] = genbas.read_text() - cfourrec["command"] = [xcfour] - - return cfourrec - - def execute( - self, inputs: Dict[str, Any], *, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None - ) -> Tuple[bool, Dict]: - - # llel works b/c util.environ_context sets OMP_NUM_THREADS = config.ncores - - success, dexe = execute( - inputs["command"], - inputs["infiles"], - ["GRD", "FCMFINAL", "DIPOL"], - # "DIPDER", "POLAR", "POLDER"], - scratch_messy=inputs["scratch_messy"], - scratch_directory=inputs["scratch_directory"], - ) - return success, dexe - - def parse_output( - self, outfiles: Dict[str, str], input_model: AtomicInput - ) -> AtomicResult: # lgtm: [py/similar-function] - - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - method = input_model.model.method.lower() - method = method[3:] if method.startswith("c4-") else method - - # c4mol, if it exists, is dinky, just a clue to geometry of cfour results - try: - # July 2021: c4mol & vector returns now atin/outfile orientation depending on fix_com,orientation=T/F. previously always atin orientation - qcvars, c4hess, c4grad, c4mol, version, module, errorTMP = harvest( - input_model.molecule, method, stdout, **outfiles - ) - except Exception: - raise UnknownError(error_stamp(outfiles["input"], stdout, stderr)) - - if errorTMP != "": - raise UnknownError(error_stamp(outfiles["input"], stdout, stderr)) - - try: - if c4grad is not None: - qcvars["CURRENT GRADIENT"] = c4grad - qcvars[f"{method.upper()} TOTAL GRADIENT"] = c4grad - - if c4hess is not None: - qcvars[f"{method.upper()} TOTAL HESSIAN"] = c4hess - qcvars["CURRENT HESSIAN"] = c4hess - - if input_model.driver.upper() == "PROPERTIES": - retres = qcvars[f"CURRENT ENERGY"] - else: - retres = qcvars[f"CURRENT {input_model.driver.upper()}"] - except KeyError: - raise UnknownError(error_stamp(outfiles["input"], stdout, stderr)) - - # TODO: "xalloc(): memory allocation failed!" - - if isinstance(retres, Decimal): - retres = float(retres) - elif isinstance(retres, np.ndarray): - retres = retres.ravel().tolist() - - build_out(qcvars) - atprop = build_atomicproperties(qcvars) - - provenance = Provenance(creator="CFOUR", version=self.get_version(), routine="xcfour").dict() - if module is not None: - provenance["module"] = module - - output_data = { - "schema_version": 1, - "molecule": c4mol, # overwrites with outfile Cartesians in case fix_*=F - "extras": {**input_model.extras}, - "native_files": {k: v for k, v in outfiles.items() if v is not None}, - "properties": atprop, - "provenance": provenance, - "return_result": retres, - "stderr": stderr, - "stdout": stdout, - "success": True, - } - - # got to even out who needs plump/flat/Decimal/float/ndarray/list - # Decimal --> str preserves precision - # * formerly unnp(qcvars, flat=True).items() - output_data["extras"]["qcvars"] = { - k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in qcvars.items() - } - - return AtomicResult(**{**input_model.dict(), **output_data}) diff --git a/qcengine/programs/dftd3.py b/qcengine/programs/dftd3.py deleted file mode 100644 index 2f67fcfee..000000000 --- a/qcengine/programs/dftd3.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Compute dispersion correction using Grimme's DFTD3 executable.""" - -import os -import pathlib -import pprint -import re -import socket -import sys -from decimal import Decimal -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple - -import numpy as np -import qcelemental as qcel -from qcelemental.models import AtomicResult, FailedOperation, Provenance -from qcelemental.util import safe_version, which - -from ..exceptions import InputError, ResourceError, UnknownError -from ..util import execute -from . import empirical_dispersion_resources -from .model import ProgramHarness - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput - - from ..config import TaskConfig - - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) - - -class DFTD3Harness(ProgramHarness): - - _defaults = { - "name": "DFTD3", - "scratch": True, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "dftd3", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install dftd3 -c psi4`.", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("dftd3") - if which_prog not in self.version_cache: - # Note: anything below v3.2.1 will return the help menu here. but that's fine as version compare evals to False. - command = [which_prog, "-version"] - import subprocess - - proc = subprocess.run(command, stdout=subprocess.PIPE) - self.version_cache[which_prog] = safe_version(proc.stdout.decode("utf-8").strip()) - - return self.version_cache[which_prog] - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - self.found(raise_error=True) - - job_inputs = self.build_input(input_model, config) - - success, dexe = self.execute(job_inputs) - - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - dexe["outfiles"]["input"] = job_inputs["infiles"][".dftd3par.local"] - dexe["outfiles"]["dftd3_geometry.xyz"] = job_inputs["infiles"]["dftd3_geometry.xyz"] - output_model = self.parse_output(dexe["outfiles"], input_model) - - else: - output_model = FailedOperation( - success=False, - error={"error_type": "execution_error", "error_message": dexe["stderr"]}, - input_data=input_model.dict(), - ) - - return output_model - - def execute( - self, inputs: Dict[str, Any], *, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None - ) -> Tuple[bool, Dict]: - - success, dexe = execute( - inputs["command"], - inputs["infiles"], - inputs["outfiles"], - scratch_messy=inputs["scratch_messy"], - # env=inputs["env"], - scratch_directory=inputs["scratch_directory"], - blocking_files=inputs["blocking_files"], - ) - return success, dexe - - def build_input( - self, input_model: "AtomicInput", config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - - # strip engine hint - mtd = input_model.model.method - if mtd.startswith("d3-"): - mtd = mtd[3:] - - if (input_model.driver.derivative_int() > 1) or (input_model.driver == "properties"): - raise InputError(f"Driver {input_model.driver} not implemented for DFTD3.") - - # temp until actual options object - input_model.extras["info"] = empirical_dispersion_resources.from_arrays( - name_hint=mtd, - level_hint=input_model.keywords.get("level_hint", None), - param_tweaks=input_model.keywords.get("params_tweaks", None), - dashcoeff_supplement=input_model.keywords.get("dashcoeff_supplement", None), - ) - - # this is what the dftd3 program needs, not what the job needs - # * form dftd3_parameters string that governs dispersion calc - # * form dftd3_geometry string that supplies geometry to dispersion calc - # * form command and arguments - - # Need 'real' field later and that's only guaranteed for molrec - molrec = qcel.molparse.from_schema(input_model.molecule.dict()) - # jobrec['molecule']['real'] = molrec['real'] - - command = ["dftd3", "dftd3_geometry.xyz"] - if input_model.driver == "gradient": - command.append("-grad") - if input_model.extras["info"]["dashlevel"] == "atmgr": - command.append("-abc") - - # Append `-anal` for pairwise atomic analysis - if input_model.keywords.get("pair_resolved", False): - command.append("-anal") - - infiles = { - ".dftd3par.local": dftd3_coeff_formatter( - input_model.extras["info"]["dashlevel"], input_model.extras["info"]["dashparams"] - ), - "dftd3_geometry.xyz": qcel.molparse.to_string(molrec, dtype="xyz", units="Angstrom", ghost_format=""), - } - - return { - "command": command, - "infiles": infiles, - "outfiles": ["dftd3_gradient", "dftd3_abc_gradient"], - "scratch_messy": config.scratch_messy, - "scratch_directory": config.scratch_directory, - "input_result": input_model.copy(deep=True), - "blocking_files": [os.path.join(pathlib.Path.home(), ".dftd3par." + socket.gethostname())], - } - - # Notes - # ----- - # Central to harvesting is the fact (to the planting, not to the DFTD3 - # program) that 2-body and 3-body are run separately. Because of how - # damping functions work (see GH:psi4/psi4#1407), some 2-body damping - # schemes can give wrong answers for 3-body. And because 3-body is - # set to run with some dummy values, the 2-body values are no good. - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - Grimme_h2kcal = 627.509541 - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - for fl, contents in outfiles.items(): - if contents is not None: - # LOG text += f'\n DFTD3 scratch file {fl} has been read.\n' - pass - - # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) - real = np.array(input_model.molecule.real) - full_nat = real.shape[0] - real_nat = np.sum(real) - for ln in stdout.splitlines(): - if re.match(" Edisp /kcal,au", ln): - ene = Decimal(ln.split()[3]) - elif re.match(r" E6\(ABC\) \" :", ln): # c. v3.2.0 - raise ResourceError("Cannot process ATM results from DFTD3 prior to v3.2.1.") - elif re.match(r""" E6\(ABC\) /kcal,au:""", ln): - atm = Decimal(ln.split()[-1]) - elif re.match(" analysis of pair-wise terms", ln): - D3pairs = np.zeros((full_nat, full_nat)) - # Iterate over block - start = stdout.splitlines().index(ln) + 2 - for l in stdout.splitlines()[start:]: - data = l.replace("-", " -").split() - # print(data) - if len(data) == 0: - break - atom1 = int(data[0]) - 1 - atom2 = int(data[1]) - 1 - Edisp = Decimal(data[-1]) - D3pairs[atom1, atom2] = Decimal(0.5) * Edisp / Decimal(Grimme_h2kcal) - D3pairs[atom2, atom1] = D3pairs[atom1, atom2] - - elif re.match(" normal termination of dftd3", ln): - break - else: - if not ((real_nat == 1) and (input_model.driver == "gradient")): - raise UnknownError( - f"Unsuccessful run. Check input, particularly geometry in [a0]. Model: {input_model.model}" - ) - - # parse gradient output - # * DFTD3 crashes on one-atom gradients. Avoid the error (above) and just force the correct result (below). - if outfiles["dftd3_gradient"] is not None: - srealgrad = outfiles["dftd3_gradient"].replace("D", "E") - realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) - elif real_nat == 1: - realgrad = np.zeros((1, 3)) - - if outfiles["dftd3_abc_gradient"] is not None: - srealgrad = outfiles["dftd3_abc_gradient"].replace("D", "E") - realgradabc = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) - elif real_nat == 1: - realgradabc = np.zeros((1, 3)) - - if input_model.driver == "gradient": - ireal = np.argwhere(real).reshape((-1)) - fullgrad = np.zeros((full_nat, 3)) - rg = realgradabc if (input_model.extras["info"]["dashlevel"] == "atmgr") else realgrad - try: - fullgrad[ireal, :] = rg - except NameError as exc: - raise UnknownError("Unsuccessful gradient collection.") from exc - - qcvkey = input_model.extras["info"]["fctldash"].upper() - - calcinfo = [] - if input_model.extras["info"]["dashlevel"] == "atmgr": - calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", atm)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", atm)) - calcinfo.append(qcel.Datum("3-BODY DISPERSION CORRECTION ENERGY", "Eh", atm)) - calcinfo.append(qcel.Datum("AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY", "Eh", atm)) - - if input_model.driver == "gradient": - calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("3-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append( - qcel.Datum("AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad) - ) - - else: - calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", ene)) - calcinfo.append(qcel.Datum("2-BODY DISPERSION CORRECTION ENERGY", "Eh", ene)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} DISPERSION CORRECTION ENERGY", "Eh", ene)) - - if input_model.driver == "gradient": - calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("2-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - - # LOGtext += qcel.datum.print_variables({info.label: info for info in calcinfo}) - calcinfo = {info.label: info.data for info in calcinfo} - - # Decimal --> str preserves precision - calcinfo = {k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in calcinfo.items()} - - # jobrec['properties'] = {"return_energy": ene} - # jobrec["molecule"]["real"] = list(jobrec["molecule"]["real"]) - - retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] - if isinstance(retres, Decimal): - retres = float(retres) - elif isinstance(retres, np.ndarray): - retres = retres.ravel().tolist() - - output_data = { - "extras": input_model.extras, - "native_files": {k: v for k, v in outfiles.items() if v is not None}, - "properties": { - "return_energy": calcinfo[f"CURRENT ENERGY"], - }, - "provenance": Provenance( - creator="DFTD3", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name - ), - "return_result": retres, - "stderr": stderr, - "stdout": stdout, - } - output_data["extras"]["local_keywords"] = input_model.extras["info"] - output_data["extras"]["qcvars"] = calcinfo - if input_model.keywords.get("pair_resolved", False): - assert ( - abs(D3pairs.sum() - float(retres)) < 1.0e-6 - ), f"pairwise sum {D3pairs.sum()} != energy {float(retres)}" - output_data["extras"]["qcvars"]["2-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] = D3pairs - output_data["success"] = True - - return AtomicResult(**{**input_model.dict(), **output_data}) - - -def dftd3_coeff_formatter(dashlvl: str, dashcoeff: Dict) -> str: - """Return strings for DFTD3 program parameter file. - - s6 rs6 s18 rs8 alpha6 version - ------------------------------------------------------ - d2: s6 sr6 s8=0.0 a2=None alpha6 version=2 - d3zero: s6 sr6 s8 a2=sr8 alpha6 version=3 - d3bj: s6 a1 s8 a2 alpha6=None version=4 - d3mzero: s6 sr6 s8 beta alpha6=14.0 version=5 - d3mbj: s6 a1 s8 a2 alpha6=None version=6 - atmgr: s6=1.0 sr6=None s8=None a2=None alpha6 version=3 (needs -abc, too) - - 2-body variant here. that is, d3zero2b - - Parameters - ---------- - dashlvl : {'d2', 'd3zero', d3bj', 'd3mzero', 'd3mbj', 'atmgr'} - Level of dispersion correction. - dashcoeff : dict - Dictionary fully specifying non-fixed parameters (table above) for `dashlvl` to drive DFTD3. - - Notes - ----- - The `atmgr` dashlvl is intended for use only to get the three-body Axilrod-Teller-Muto - three body dispersion correction. Therefore, dummy parameters are passed for two-body damping - function, and it will give garbage for two-body component of dispersion correction. - - Returns - ------- - str - Suitable for `.dftd3par` file. - - """ - dashformatter = """{:12.6f} {:12.6f} {:12.6f} {:12.6f} {:12.6f} {:6}\n""" - - dashlvl = dashlvl.lower() - if dashlvl == "d2": - return dashformatter.format(dashcoeff["s6"], dashcoeff["sr6"], 0.0, 0.0, dashcoeff["alpha6"], 2) - elif dashlvl == "d3zero2b": - return dashformatter.format( - dashcoeff["s6"], dashcoeff["sr6"], dashcoeff["s8"], dashcoeff["sr8"], dashcoeff["alpha6"], 3 - ) - elif dashlvl == "d3bj2b": - return dashformatter.format(dashcoeff["s6"], dashcoeff["a1"], dashcoeff["s8"], dashcoeff["a2"], 0.0, 4) - elif dashlvl == "d3mzero2b": - return dashformatter.format(dashcoeff["s6"], dashcoeff["sr6"], dashcoeff["s8"], dashcoeff["beta"], 14.0, 5) - elif dashlvl == "d3mbj2b": - return dashformatter.format(dashcoeff["s6"], dashcoeff["a1"], dashcoeff["s8"], dashcoeff["a2"], 0.0, 6) - elif dashlvl == "atmgr": - # need to set first four parameters to something other than None, otherwise DFTD3 gets mad or a bit wrong - return dashformatter.format(1.0, 0.0, 0.0, 0.0, dashcoeff["alpha6"], 3) - else: - raise InputError(f"""-D correction level {dashlvl} is not available. Choose among {dashcoeff.keys()}.""") diff --git a/qcengine/programs/dftd_ng.py b/qcengine/programs/dftd_ng.py deleted file mode 100644 index 8fe0af980..000000000 --- a/qcengine/programs/dftd_ng.py +++ /dev/null @@ -1,313 +0,0 @@ -""" -Harness for the DFT-D dispersion correction. -This implementation interfaces with the dftd3 and dftd4 Python-API, which provides -native support for QCSchema. - -Therefore, this harness only has to provide a thin wrapper to integrate the -respective dispersion correction. -""" - -from typing import Dict - -from qcelemental.models import AtomicInput, AtomicResult -from qcelemental.util import parse_version, safe_version, which_import - -from ..config import TaskConfig -from ..exceptions import InputError -from .empirical_dispersion_resources import from_arrays, get_dispersion_aliases -from .model import ProgramHarness - - -class DFTD4Harness(ProgramHarness): - """Calculation harness for the DFT-D4 dispersion correction.""" - - _defaults = { - "name": "dftd4", - "scratch": False, - "thread_safe": True, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - """Check for the availability of the Python API of dftd4""" - - return which_import( - "dftd4", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install a dftd4 version with enabled Python API" - + " (e.g. conda install dftd4-python -c conda-forge)", - ) - - def get_version(self) -> str: - """Return the currently used version of dftd4""" - self.found(raise_error=True) - - which_prog = which_import("dftd4") - if which_prog not in self.version_cache: - import dftd4 - - self.version_cache[which_prog] = safe_version(dftd4.__version__) - - return self.version_cache[which_prog] - - def compute(self, input_model: AtomicInput, config: TaskConfig) -> AtomicResult: - """ - Actual interface to the dftd4 package. The compute function is just a thin - wrapper around the native QCSchema interface of the dftd4 Python-API. - """ - - self.found(raise_error=True) - - import dftd4 - from dftd4.qcschema import run_qcschema - - # strip engine hint - input_data = input_model.dict() - method = input_model.model.method - if method.startswith("d4-"): - method = method[3:] - input_data["model"]["method"] = method - qcvkey = method.upper() if method is not None else None - - # send `from_arrays` the dftd4 behavior of functional specification overrides explicit parameters specification - # * differs from dftd4 harness behavior where parameters extend or override functional - # * stash the resolved plan in extras or, if errored, leave it for the proper dftd4 api to reject - param_tweaks = None if method else input_model.keywords.get("params_tweaks", None) - try: - planinfo = from_arrays( - verbose=1, - name_hint=method, - level_hint=input_model.keywords.get("level_hint", None), - param_tweaks=param_tweaks, - dashcoeff_supplement=input_model.keywords.get("dashcoeff_supplement", None), - ) - except InputError: - pass - else: - input_data["extras"]["info"] = planinfo - - # strip dispersion level from method - for alias, d4 in get_dispersion_aliases().items(): - if d4 == "d4bjeeqatm" and method.lower().endswith(alias): - method = method[: -(len(alias) + 1)] - input_data["model"]["method"] = method - - # consolidate dispersion level aliases - level_hint = input_model.keywords.get("level_hint", None) - if level_hint and get_dispersion_aliases()[level_hint.lower()] == "d4bjeeqatm": - level_hint = "d4" - input_data["keywords"]["level_hint"] = level_hint - - input_model = AtomicInput(**input_data) - - # Run the Harness - output = run_qcschema(input_model) - - if "info" in output.extras: - qcvkey = output.extras["info"]["fctldash"].upper() - - calcinfo = {} - energy = output.properties.return_energy - calcinfo["CURRENT ENERGY"] = energy - calcinfo["DISPERSION CORRECTION ENERGY"] = energy - if qcvkey: - calcinfo[f"{qcvkey} DISPERSION CORRECTION ENERGY"] = energy - - if output.driver == "gradient": - gradient = output.return_result - calcinfo["CURRENT GRADIENT"] = gradient - calcinfo["DISPERSION CORRECTION GRADIENT"] = gradient - if qcvkey: - calcinfo[f"{qcvkey} DISPERSION CORRECTION GRADIENT"] = gradient - - if output.keywords.get("pair_resolved", False): - pw2 = output.extras["dftd4"]["additive pairwise energy"] - pw3 = output.extras["dftd4"]["non-additive pairwise energy"] - assert abs(pw2.sum() + pw3.sum() - energy) < 1.0e-8, f"{pw2.sum()} + {pw3.sum()} != {energy}" - calcinfo["2-BODY DISPERSION CORRECTION ENERGY"] = pw2.sum() - calcinfo["3-BODY DISPERSION CORRECTION ENERGY"] = pw3.sum() - calcinfo["2-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] = pw2 - calcinfo["3-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] = pw3 - - output.extras["qcvars"] = calcinfo - - return output - - -class SDFTD3Harness(ProgramHarness): - """ - Calculation harness for the DFT-D3 dispersion correction. - - This implementation of DFT-D3 supports the several damping functions, which - are selected via the *level_hint* keyword. Damping parameter can be specified - via the *param_tweaks* dictionary. If no *param_tweaks* are provided the - functional parameters are obtained from the internal database of the library. - - The following damping function are available via *level_hint*: - - - ``d3bj``: - Rational damping function for DFT-D3. The original scheme was proposed by - Becke and Johnson and implemented in a slightly adjusted form using only - the C8/C6 ratio in the critical radius for DFT-D3. - Requires at least three parameters: *s8*, *a1*, and *a2*. - The parameters *s6*, *s9*, and *alpha6* can be adjusted as well. - - ``d3zero``: - Original DFT-D3 damping function, based on a variant proposed by Chai and Head-Gordon. - Requires at least two parameters: *s8* and *sr6*. - The parameters *s6*, *s9*, *sr8*, and *alpha6* can be adjusted as well. - - ``d3mbj``: - Modified version of the rational damping parameters. The functional form of the - damping function is *unmodified* with respect to the original rational damping scheme. - However, for a number of functionals new parameters were introduced. - Requires at least three parameters: *s8*, *a1*, and *a2*. - The parameters *s6*, *s9*, and *alpha6* can be adjusted as well. - - ``d3mzero``: - Modified zero damping function for DFT-D3. This scheme adds an additional offset - parameter to the zero damping scheme of the original DFT-D3. - Requires at least three parameters: *s8*, *sr6*, and *beta*. - The parameters *s6*, *s9*, *sr8*, and *alpha6* can be adjusted as well. - - ``d3op``: - Optimized power version of the rational damping function for DFT-D3. - The functional form of the damping function is modified by adding an additional - zero-damping like power function. - Requires at least four parameters: *s8*, *a1*, *a2*, and *beta*. - The parameters *s6*, *s9*, and *alpha6* can be adjusted as well. - - All damping functions by default *include* the ATM three-body contributions, - it must be explicitly disabled by setting the *s9* value to zero. - """ - - _defaults = { - "name": "s-dftd3", - "scratch": False, - "thread_safe": True, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - """Check for the availability of the Python API of dftd3""" - - return which_import( - "dftd3", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install a dftd3 version with enabled Python API" - + " (e.g. conda install dftd3-python -c conda-forge)", - ) - - def get_version(self) -> str: - """Return the currently used version of dftd3""" - self.found(raise_error=True) - - which_prog = which_import("dftd3") - if which_prog not in self.version_cache: - import dftd3 - - self.version_cache[which_prog] = safe_version(dftd3.__version__) - - return self.version_cache[which_prog] - - def compute(self, input_model: AtomicInput, config: TaskConfig) -> AtomicResult: - """ - Actual interface to the dftd3 package. The compute function is just a thin - wrapper around the native QCSchema interface of the dftd3 Python-API. - """ - self.found(raise_error=True) - if parse_version(self.get_version()) < parse_version("0.5.1"): - raise ResourceError("QCEngine's dftd3 wrapper requires version 0.5.1 or greater.") - - import dftd3 - from dftd3.qcschema import run_qcschema - - # strip engine hint - input_data = input_model.dict() - method = input_model.model.method - if method.startswith("d3-"): - method = method[3:] - input_data["model"]["method"] = method - qcvkey = method.upper() if method is not None else None - - # send `from_arrays` the s-dftd3 behavior of functional specification overrides explicit parameters specification - # * differs from dftd3 harness behavior where parameters extend or override functional - # * stash the resolved plan in extras or, if errored, leave it for the proper dftd3 api to reject - param_tweaks = None if method else input_model.keywords.get("params_tweaks", None) - try: - planinfo = from_arrays( - verbose=1, - name_hint=method, - level_hint=input_model.keywords.get("level_hint", None), - param_tweaks=param_tweaks, - dashcoeff_supplement=input_model.keywords.get("dashcoeff_supplement", None), - ) - except InputError: - pass - else: - input_data["extras"]["info"] = planinfo - - # strip dispersion level from method - for alias, d3 in get_dispersion_aliases().items(): - if d3.startswith("d3") and method.lower().endswith(alias): - method = method[: -(len(alias) + 1)] - input_data["model"]["method"] = method - - # consolidate dispersion level aliases - if input_model.keywords.pop("apply_qcengine_aliases", False): - level_hint = input_model.keywords.get("level_hint", None) - if level_hint: - level_hint = get_dispersion_aliases()[level_hint.lower()] - if level_hint.endswith("atm"): - level_hint = level_hint[:-3] - if level_hint.endswith("2b"): - level_hint = level_hint[:-2] - input_data["keywords"]["params_tweaks"] = {**planinfo["dashparams"], "s9": 0.0} - input_data["keywords"]["level_hint"] = level_hint - - input_model = AtomicInput(**input_data) - - # Run the Harness - output = run_qcschema(input_model) - - if "info" in output.extras: - qcvkey = output.extras["info"]["fctldash"].upper() - - calcinfo = {} - energy = output.properties.return_energy - calcinfo["CURRENT ENERGY"] = energy - calcinfo["DISPERSION CORRECTION ENERGY"] = energy - if qcvkey: - calcinfo[f"{qcvkey} DISPERSION CORRECTION ENERGY"] = energy - - if output.driver == "gradient": - gradient = output.return_result - calcinfo["CURRENT GRADIENT"] = gradient - calcinfo["DISPERSION CORRECTION GRADIENT"] = gradient - if qcvkey: - calcinfo[f"{qcvkey} DISPERSION CORRECTION GRADIENT"] = gradient - - if output.keywords.get("pair_resolved", False): - pw2 = output.extras["dftd3"]["additive pairwise energy"] - pw3 = output.extras["dftd3"]["non-additive pairwise energy"] - assert abs(pw2.sum() + pw3.sum() - energy) < 1.0e-8, f"{pw2.sum()} + {pw3.sum()} != {energy}" - calcinfo["2-BODY DISPERSION CORRECTION ENERGY"] = pw2.sum() - calcinfo["3-BODY DISPERSION CORRECTION ENERGY"] = pw3.sum() - calcinfo["2-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] = pw2 - calcinfo["3-BODY PAIRWISE DISPERSION CORRECTION ANALYSIS"] = pw3 - - output.extras["qcvars"] = calcinfo - - return output diff --git a/qcengine/programs/empirical_dispersion_resources.py b/qcengine/programs/empirical_dispersion_resources.py deleted file mode 100644 index c6f60ecc7..000000000 --- a/qcengine/programs/empirical_dispersion_resources.py +++ /dev/null @@ -1,1368 +0,0 @@ -"""Collect empirical dispersion parameters.""" - -import collections -import copy -from typing import Dict, List, Optional, Union - -from ..exceptions import InputError -from qcelemental.util import parse_version - -## ==> Dispersion Aliases and Parameters <== ## - -# The dashcoeff dict below defines the -D parameters for most of the DFT methods. Some 'd2' are -# taken from already defined functionals in psi4. Other parameters taken from the -# references indicated in the "citation" parameter. The remainder of the parameters are -# from http://toc.uni-muenster.de/DFTD3/ on September 25, 2012, with the dict keys -# translated from Turbomole to Psi4 functional names. -# Note: most DSD-functionals have their dispersion correction defined in dict_dh_funcs.py -dashcoeff = { - "d2": { - "formal": "D2", - "alias": ["d"], - "description": " Grimme's -D2 Dispersion Correction", - "citation": " Grimme, S. (2006), J. Comp. Chem., 27: 1787-1799\n", - "bibtex": "Grimme:2006:1787", - "default": collections.OrderedDict([("s6", 1.0), ("alpha6", 20.0), ("sr6", 1.1)]), - "definitions": { - "blyp": {"params": {"s6": 1.2, "alpha6": 20.0, "sr6": 1.1}}, - "bp86": {"params": {"s6": 1.05, "alpha6": 20.0, "sr6": 1.1}}, - "b97": {"params": {"s6": 1.25, "alpha6": 20.0, "sr6": 1.1}}, # formerly b97-d - "revpbe": { - "params": {"s6": 1.25, "alpha6": 20.0, "sr6": 1.1}, - "citation": " S. Grimme, J. Antony, S. Ehrlich, H. Krieg, J. Chem. Phys 132, 154104, 2010\n", - }, - "pbe": {"params": {"s6": 0.75, "alpha6": 20.0, "sr6": 1.1}}, - "tpss": {"params": {"s6": 1.0, "alpha6": 20.0, "sr6": 1.1}}, - "b3lyp": {"params": {"s6": 1.05, "alpha6": 20.0, "sr6": 1.1}}, - "pbe0": {"params": {"s6": 0.6, "alpha6": 20.0, "sr6": 1.1}}, - "pw6b95": {"params": {"s6": 0.5, "alpha6": 20.0, "sr6": 1.1}}, - "tpss0": { - "params": {"s6": 0.85, "alpha6": 20.0, "sr6": 1.1}, - "citation": " S. Grimme, J. Antony, S. Ehrlich, H. Krieg, J. Chem. Phys 132, 154104, 2010\n", - }, - "b2plyp": {"params": {"s6": 0.55, "alpha6": 20.0, "sr6": 1.1}}, - "b2gpplyp": {"params": {"s6": 0.4, "alpha6": 20.0, "sr6": 1.1}}, - "dsd-blyp": {"params": {"s6": 0.41, "alpha6": 60.0, "sr6": 1.1}}, - "core-dsd-blyp": {"params": {"s6": 0.41, "alpha6": 60.0, "sr6": 1.1}}, - }, - }, - "d3zeroatm": { - "formal": "D3ATM", - "alias": [], - "description": " Grimme's -D3 (zero-damping) Dispersion Correction with ATM", - "citation": " Grimme S.; Antony J.; Ehrlich S.; Krieg H. (2010), J. Chem. Phys., 132: 154104\n", - "bibtex": "Grimme:2010:154104", - "default": collections.OrderedDict( - [("s6", 1.0), ("s8", 0.0), ("sr6", 1.0), ("alpha6", 14.0), ("sr8", 1.0), ("s9", 1.0)] - ), - "definitions": { - # D3 parameters loaded from d3zero2b and from authoritative source below and s9 parameter added - }, - }, - "d3zero2b": { - "formal": "D3", - "alias": ["d3", "d3zero", "d32b"], - "description": " Grimme's -D3 (zero-damping) Dispersion Correction", - "citation": " Grimme S.; Antony J.; Ehrlich S.; Krieg H. (2010), J. Chem. Phys., 132: 154104\n", - "bibtex": "Grimme:2010:154104", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 0.0), ("sr6", 1.0), ("alpha6", 14.0), ("sr8", 1.0)]), - "definitions": { - # S. Grimme, J. Antony, S. Ehrlich, H. Krieg, J. Chem. Phys 132, 154104, 2010 - # 'b2plyp' : {'s6': 0.5, 's8': 1.000, 'sr6': 1.332, 'alpha6': 14.0}, # superseded by a value below. - "pw6b95": {"params": {"s6": 1.0, "s8": 0.862, "sr6": 1.532, "alpha6": 14.0, "sr8": 1.000}}, - "b97": {"params": {"s6": 1.0, "s8": 0.909, "sr6": 0.892, "alpha6": 14.0, "sr8": 1.000}}, # formerly b97-d - "b3lyp": {"params": {"s6": 1.0, "s8": 1.703, "sr6": 1.261, "alpha6": 14.0, "sr8": 1.000}}, - "blyp": {"params": {"s6": 1.0, "s8": 1.682, "sr6": 1.094, "alpha6": 14.0, "sr8": 1.000}}, - "tpss0": {"params": {"s6": 1.0, "s8": 1.242, "sr6": 1.252, "alpha6": 14.0, "sr8": 1.000}}, - "pbe0": {"params": {"s6": 1.0, "s8": 0.928, "sr6": 1.287, "alpha6": 14.0, "sr8": 1.000}}, - "tpss": {"params": {"s6": 1.0, "s8": 1.105, "sr6": 1.166, "alpha6": 14.0, "sr8": 1.000}}, - "pbe": {"params": {"s6": 1.0, "s8": 0.722, "sr6": 1.217, "alpha6": 14.0, "sr8": 1.000}}, - "bp86": {"params": {"s6": 1.0, "s8": 1.683, "sr6": 1.139, "alpha6": 14.0, "sr8": 1.000}}, - # Later references - "rpw86pbe": { - "params": {"s6": 1.0, "s8": 0.901, "sr6": 1.224, "alpha6": 14.0, "sr8": 1.000}, - "citation": " S. Grimme, S. Ehrlich, L. Goerigk, J. Comput. Chem. 32, 1456-1465, 2011\n", - }, - "b2plyp": { - "params": {"s6": 0.64, "s8": 1.022, "sr6": 1.427, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, J. Chem. Theory Comput. 7, 291-309, 2011\n", - }, - "b2gpplyp": { - "params": {"s6": 0.56, "s8": 0.760, "sr6": 1.586, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, J. Chem. Theory Comput. 7, 291-309, 2011\n", - }, - "pwpb95": { - "params": {"s6": 0.82, "s8": 0.705, "sr6": 1.557, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, J. Chem. Theory Comput. 7, 291-309, 2011\n", - }, - "dsd-blyp": { - "params": {"s6": 0.50, "s8": 0.705, "sr6": 1.569, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, J. Chem. Theory Comput. 7, 291-309, 2011\n", - }, - "bpbe": { - "params": {"s6": 1.0, "s8": 2.033, "sr6": 1.087, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "opbe": { - "params": {"s6": 1.0, "s8": 2.055, "sr6": 0.837, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "olyp": { - "params": {"s6": 1.0, "s8": 1.764, "sr6": 0.806, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpwlyp": { - "params": {"s6": 1.0, "s8": 1.098, "sr6": 1.239, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "bmk": { - "params": {"s6": 1.0, "s8": 2.168, "sr6": 1.931, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "m05-2x": { - "params": {"s6": 1.0, "s8": 0.00, "sr6": 1.417, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "m05": { - "params": {"s6": 1.0, "s8": 0.595, "sr6": 1.373, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "m06-2x": { - "params": {"s6": 1.0, "s8": 0.00, "sr6": 1.619, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "m06": { - "params": {"s6": 1.0, "s8": 0.00, "sr6": 1.325, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "m06-l": { - "params": {"s6": 1.0, "s8": 0.00, "sr6": 1.581, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b3pw91": { - "params": {"s6": 1.0, "s8": 1.775, "sr6": 1.176, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "bhlyp": { - "params": {"s6": 1.0, "s8": 1.442, "sr6": 1.370, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b1b95": { - "params": {"s6": 1.0, "s8": 1.868, "sr6": 1.613, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpw1b95": { - "params": {"s6": 1.0, "s8": 1.118, "sr6": 1.605, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpwb1k": { - "params": {"s6": 1.0, "s8": 1.061, "sr6": 1.671, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "tpssh": { - "params": {"s6": 1.0, "s8": 1.219, "sr6": 1.223, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "scan": { - "params": {"s6": 1.0, "s8": 0.000, "sr6": 1.324, "alpha6": 14.0, "sr8": 1.000}, - "citation": " J.G. Brandenburg, J. E. Bates, J. Sun, J.P. Perdew, Phys. Rev. B 94, 115144, 2016\n", - }, - "m11-l": { - "params": {"s6": 1.0, "s8": 1.1129, "sr6": 2.3933, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "mn12-l": { - "params": {"s6": 1.0, "s8": 0.9622, "sr6": 2.2329, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "n12": { - "params": {"s6": 1.0, "s8": 2.3916, "sr6": 1.3493, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "sogga11-x": { - "params": {"s6": 1.0, "s8": 1.8151, "sr6": 1.5431, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "m11": { - "params": {"s6": 1.0, "s8": 0.6244, "sr6": 2.2300, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "mn12-sx": { - "params": {"s6": 1.0, "s8": 0.8205, "sr6": 1.9572, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "n12-sx": { - "params": {"s6": 1.0, "s8": 1.4713, "sr6": 1.4597, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "hse06": { - "params": {"s6": 1.0, "s8": 0.1090, "sr6": 1.1290, "alpha6": 14.0, "sr8": 1.000}, - "citation": " J. Moellmann, S. Grimme, J. Chem. Phys. C 118, 7615-7621, 2014\n", - }, - "wb97x": { - "params": {"s6": 1.0, "s8": 1.0, "sr6": 1.281, "alpha6": 14.0, "sr8": 1.094}, - "citation": " Y.-S. Lin, G.-D. Li, S.-P. Mao, J.-D. Chai, Chem. Theory Comput. 9, 263-272, 2013\n", - }, - "pbehpbe": { - "params": {"s6": 1.0, "s8": 1.4010, "sr6": 1.5703, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "rpbe": { - "params": {"s6": 1.0, "s8": 0.5140, "sr6": 0.8720, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "xlyp": { - "params": {"s6": 1.0, "s8": 0.7447, "sr6": 0.9384, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pw91p86": { - "params": {"s6": 1.0, "s8": 0.8747, "sr6": 2.1040, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpwpw91": { - "params": {"s6": 1.0, "s8": 1.9467, "sr6": 1.3725, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hcth407": { - "params": {"s6": 1.0, "s8": 2.7694, "sr6": 4.0426, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pkzb": { - "params": {"s6": 1.0, "s8": 0.0000, "sr6": 0.6327, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpss": { - "params": {"s6": 1.0, "s8": 1.3666, "sr6": 1.3491, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "thcth": { - "params": {"s6": 1.0, "s8": 0.5662, "sr6": 0.9320, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mn15-l": { - "params": {"s6": 1.0, "s8": 0.0000, "sr6": 3.3388, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b3p86": { - "params": {"s6": 1.0, "s8": 1.1961, "sr6": 1.1897, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b1p96": { - "params": {"s6": 1.0, "s8": 1.1209, "sr6": 1.1815, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b1lyp": { - "params": {"s6": 1.0, "s8": 1.9467, "sr6": 1.3725, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw1lyp": { - "params": {"s6": 1.0, "s8": 1.9529, "sr6": 2.0512, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw1pw91": { - "params": {"s6": 1.0, "s8": 1.4758, "sr6": 1.2892, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pw1pw": { - "params": {"s6": 1.0, "s8": 1.1786, "sr6": 1.4968, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw1kcis": { - "params": {"s6": 1.0, "s8": 2.2917, "sr6": 1.7231, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpwkcis1k": { - "params": {"s6": 1.0, "s8": 1.7553, "sr6": 1.4853, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbeh1pbe": { - "params": {"s6": 1.0, "s8": 1.0430, "sr6": 1.3719, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbe1kcis": { - "params": {"s6": 1.0, "s8": 1.7934, "sr6": 3.6355, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "x3lyp": { - "params": {"s6": 1.0, "s8": 0.2990, "sr6": 1.0000, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "o3lyp": { - "params": {"s6": 1.0, "s8": 1.8058, "sr6": 1.4060, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b97-1": { - "params": {"s6": 1.0, "s8": 1.6418, "sr6": 3.7924, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b97-2": { - "params": {"s6": 1.0, "s8": 2.4661, "sr6": 1.7066, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b98": { - "params": {"s6": 1.0, "s8": 1.9078, "sr6": 2.6895, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hiss": { - "params": {"s6": 1.0, "s8": 0.7615, "sr6": 1.3338, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hse03": { - "params": {"s6": 1.0, "s8": 1.0156, "sr6": 1.3944, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpssh": { - "params": {"s6": 1.0, "s8": 1.2504, "sr6": 1.3224, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpss0": { - "params": {"s6": 1.0, "s8": 1.0649, "sr6": 1.2881, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "tpss1kcis": { - "params": {"s6": 1.0, "s8": 2.0902, "sr6": 1.7729, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "thcthhyb": { - "params": {"s6": 1.0, "s8": 1.6302, "sr6": 1.5001, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "m08-hx": { - "params": {"s6": 1.0, "s8": 0.0000, "sr6": 1.6247, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "lcwhpbe": { - "params": {"s6": 1.0, "s8": 1.2797, "sr6": 1.3846, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw2plyp": { - "params": {"s6": 0.66, "s8": 0.7529, "sr6": 1.5527, "alpha6": 14.0, "sr8": 1.000}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbe0-dh": { - "params": {"s6": 0.840, "s8": 0.748, "sr6": 1.394, "alpha6": 14.0, "sr8": 1.000}, - "citation": " D. Bousquet, E. Bremond, J. C. Sancho-Garcia, I. Ciofini, C. Adamo, Theor. Chem. Acc. 134, 1602, 2015\n", - }, - # https://www.chemie.uni-bonn.de/pctc/mulliken-center/software/dft-d3/functionals - "pwb6k": {"params": {"s6": 1.0, "s8": 0.550, "sr6": 1.660, "alpha6": 14.0, "sr8": 1.000}}, - "revpbe": {"params": {"s6": 1.0, "s8": 1.010, "sr6": 0.923, "alpha6": 14.0, "sr8": 1.000}}, - "bop": {"params": {"s6": 1.0, "s8": 1.975, "sr6": 0.929, "alpha6": 14.0, "sr8": 1.000}}, - "otpss": {"params": {"s6": 1.0, "s8": 1.494, "sr6": 1.128, "alpha6": 14.0, "sr8": 1.000}}, - "pbe38": {"params": {"s6": 1.0, "s8": 0.998, "sr6": 1.333, "alpha6": 14.0, "sr8": 1.000}}, - "pbesol": {"params": {"s6": 1.0, "s8": 0.612, "sr6": 1.345, "alpha6": 14.0, "sr8": 1.000}}, - "revssb": {"params": {"s6": 1.0, "s8": 0.560, "sr6": 1.221, "alpha6": 14.0, "sr8": 1.000}}, - "ssb": {"params": {"s6": 1.0, "s8": 0.663, "sr6": 1.215, "alpha6": 14.0, "sr8": 1.000}}, - "cam-b3lyp": {"params": {"s6": 1.0, "s8": 1.217, "sr6": 1.378, "alpha6": 14.0, "sr8": 1.000}}, - "wpbe": {"params": {"s6": 1.0, "s8": 1.279, "sr6": 1.355, "alpha6": 14.0, "sr8": 1.000}}, # formerly lcwpbe - "m06-hf": {"params": {"s6": 1.0, "s8": 0.00, "sr6": 1.446, "alpha6": 14.0, "sr8": 1.000}}, - "hcth120": {"params": {"s6": 1.0, "s8": 1.206, "sr6": 1.221, "alpha6": 14.0, "sr8": 1.000}}, - "ptpss": {"params": {"s6": 0.75, "s8": 0.879, "sr6": 1.541, "alpha6": 14.0, "sr8": 1.000}}, - "revpbe0": {"params": {"s6": 1.0, "s8": 0.792, "sr6": 0.949, "alpha6": 14.0, "sr8": 1.000}}, - "revpbe38": {"params": {"s6": 1.0, "s8": 0.862, "sr6": 1.021, "alpha6": 14.0, "sr8": 1.000}}, - # unreferenced - "hf": {"params": {"s6": 1.0, "s8": 1.746, "sr6": 1.158, "alpha6": 14.0, "sr8": 1.000}}, - }, - }, - "d3bjatm": { - "formal": "D3(BJ)ATM", - "alias": [], - "description": " Grimme's -D3 (BJ-damping) Dispersion Correction with ATM", - "citation": " Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("a1", 0.0), ("a2", 1.0), ("s9", 1.0)]), - "definitions": { - # D3 parameters loaded from d3bj2b and from authoritative source below and s9 parameter added - }, - }, - "d3bj2b": { - "formal": "D3(BJ)", - "alias": ["d3bj"], - "description": " Grimme's -D3 (BJ-damping) Dispersion Correction", - "citation": " Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("a1", 0.0), ("a2", 1.0)]), - "definitions": { - # S. Grimme, S. Ehrlich, L. Goerigk, J. Comput. Chem. 7, 3297-3305, 2011 - "pbe": {"params": {"s6": 1.000, "s8": 0.7875, "a1": 0.4289, "a2": 4.4407}}, - "revpbe": {"params": {"s6": 1.000, "s8": 2.3550, "a1": 0.5238, "a2": 3.5016}}, - "blyp": {"params": {"s6": 1.000, "s8": 2.6996, "a1": 0.4298, "a2": 4.2359}}, - "bp86": {"params": {"s6": 1.000, "s8": 3.2822, "a1": 0.3946, "a2": 4.8516}}, - "rpw86pbe": {"params": {"s6": 1.000, "s8": 1.3845, "a1": 0.4613, "a2": 4.5062}}, - "b97": {"params": {"s6": 1.000, "s8": 2.2609, "a1": 0.5545, "a2": 3.2297}}, # formerly b97-d - "tpss": {"params": {"s6": 1.000, "s8": 1.9435, "a1": 0.4535, "a2": 4.4752}}, - "b3lyp": {"params": {"s6": 1.000, "s8": 1.9889, "a1": 0.3981, "a2": 4.4211}}, - "pw6b95": {"params": {"s6": 1.000, "s8": 0.7257, "a1": 0.2076, "a2": 6.3750}}, - "pbe0": {"params": {"s6": 1.000, "s8": 1.2177, "a1": 0.4145, "a2": 4.8593}}, - "tpss0": {"params": {"s6": 1.000, "s8": 1.2576, "a1": 0.3768, "a2": 4.5865}}, - # 'b2plyp' : {'params': {'s6': 0.500, 's8': 1.0860, 'a1': 0.3451, 'a2': 4.7735}}, # superseded by a value below. - "hf": {"params": {"s6": 1.000, "s8": 0.9171, "a1": 0.3385, "a2": 2.8830}}, - # Later references - "bpbe": { - "params": {"s6": 1.000, "s8": 4.0728, "a1": 0.4567, "a2": 4.3908}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "opbe": { - "params": {"s6": 1.000, "s8": 3.3816, "a1": 0.5512, "a2": 2.9444}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "olyp": { - "params": {"s6": 1.000, "s8": 2.6205, "a1": 0.5299, "a2": 2.8065}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpwlyp": { - "params": {"s6": 1.000, "s8": 2.0077, "a1": 0.4831, "a2": 4.5323}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b3pw91": { - "params": {"s6": 1.000, "s8": 2.8524, "a1": 0.4312, "a2": 4.4693}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "bhlyp": { - "params": {"s6": 1.000, "s8": 1.0354, "a1": 0.2793, "a2": 4.9615}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b1b95": { - "params": {"s6": 1.000, "s8": 1.4507, "a1": 0.2092, "a2": 5.5545}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpw1b95": { - "params": {"s6": 1.000, "s8": 1.0508, "a1": 0.1955, "a2": 6.4177}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "mpwb1k": { - "params": {"s6": 1.000, "s8": 0.9499, "a1": 0.1474, "a2": 6.6223}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "tpssh": { - "params": {"s6": 1.000, "s8": 2.2382, "a1": 0.4529, "a2": 4.6550}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "bmk": { - "params": {"s6": 1.000, "s8": 2.0860, "a1": 0.1940, "a2": 5.9197}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b2plyp": { - "params": {"s6": 0.640, "s8": 0.9147, "a1": 0.3065, "a2": 5.0570}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "b2gpplyp": { - "params": {"s6": 0.560, "s8": 0.2597, "a1": 0.0000, "a2": 6.3332}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "pwpb95": { - "params": {"s6": 0.820, "s8": 0.2904, "a1": 0.0000, "a2": 7.3141}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "dsd-blyp": { - "params": {"s6": 0.500, "s8": 0.2130, "a1": 0.0000, "a2": 6.0519}, - "citation": " L. Goerigk, S. Grimme, Phys. Chem. Chem. Phys. 13, 6670-6688, 2011\n", - }, - "hse06": { - "params": {"s6": 1.000, "s8": 2.3100, "a1": 0.3830, "a2": 5.6850}, - "citation": " J. Moellmann, S. Grimme, J. Chem. Phys. C 118, 7615-7621, 2014\n", - }, - "pw91": { - "params": {"s6": 1.000, "s8": 1.9598, "a1": 0.6319, "a2": 4.5718}, - "citation": " J.R. Reimers et al., Proc. Natl. Acad. Sci. USA 112, E6101-E6110, 2015\n", - }, - "m11-l": { - "params": {"s6": 1.000, "s8": 0.4446, "a1": 0.0000, "a2": 7.2496}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "mn12-l": { - "params": {"s6": 1.000, "s8": 2.2674, "a1": 0.0000, "a2": 9.1494}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "n12": { - "params": {"s6": 1.000, "s8": 4.8491, "a1": 0.3842, "a2": 5.3545}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "sogga11-x": { - "params": {"s6": 1.000, "s8": 1.1426, "a1": 0.1330, "a2": 5.7381}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "m11": { - "params": {"s6": 1.000, "s8": 2.8112, "a1": 0.0000, "a2": 10.1389}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "mn12-sx": { - "params": {"s6": 1.000, "s8": 1.1674, "a1": 0.0983, "a2": 8.0259}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "n12-sx": { - "params": {"s6": 1.000, "s8": 2.4900, "a1": 0.3283, "a2": 5.7898}, - "citation": " L. Goerigk, J. Phys. Chem. Lett. 6, 3891-3896, 2015\n", - }, - "scan": { - "params": {"s6": 1.000, "s8": 0.0000, "a1": 0.5380, "a2": 5.4200}, - "citation": " J.G. Brandenburg, J. E. Bates, J. Sun, J.P. Perdew, Phys. Rev. B 94, 115144, 2016\n", - }, - "pbehpbe": { - "params": {"s6": 1.000, "s8": 1.1152, "a1": 0.0000, "a2": 4.4407}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "rpbe": { - "params": {"s6": 1.000, "s8": 0.8318, "a1": 0.1820, "a2": 4.0094}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "xlyp": { - "params": {"s6": 1.000, "s8": 1.5669, "a1": 0.0809, "a2": 5.3166}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpwpw91": { - "params": {"s6": 1.000, "s8": 0.3168, "a1": 0.3168, "a2": 4.7732}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hcth407": { - "params": {"s6": 1.000, "s8": 0.6490, "a1": 0.0000, "a2": 4.8162}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpss": { - "params": {"s6": 1.000, "s8": 1.4023, "a1": 0.4426, "a2": 4.4723}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "thcth": { - "params": {"s6": 1.000, "s8": 1.2626, "a1": 0.0000, "a2": 5.6162}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b3p86": { - "params": {"s6": 1.000, "s8": 3.3211, "a1": 0.4601, "a2": 4.9294}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b1p86": { - "params": {"s6": 1.000, "s8": 3.5681, "a1": 0.4724, "a2": 4.9858}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b1lyp": { - "params": {"s6": 1.000, "s8": 2.1167, "a1": 0.1986, "a2": 5.3875}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw1pw91": { - "params": {"s6": 1.000, "s8": 1.8744, "a1": 0.3342, "a2": 4.9819}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw1kcis": { - "params": {"s6": 1.000, "s8": 1.0893, "a1": 0.0576, "a2": 5.5314}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpwkcis1k": { - "params": {"s6": 1.000, "s8": 1.2875, "a1": 0.0855, "a2": 5.8961}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbeh1pbe": { - "params": {"s6": 1.000, "s8": 1.4877, "a1": 0.0000, "a2": 7.0385}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbe1kcis": { - "params": {"s6": 1.000, "s8": 0.7688, "a1": 0.0000, "a2": 6.2794}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "x3lyp": { - "params": {"s6": 1.000, "s8": 1.5744, "a1": 0.2022, "a2": 5.4184}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "o3lyp": { - "params": {"s6": 1.000, "s8": 1.8171, "a1": 0.0963, "a2": 5.9940}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b97-1": { - "params": {"s6": 1.000, "s8": 0.4814, "a1": 0.0000, "a2": 6.2279}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b97-2": { - "params": {"s6": 1.000, "s8": 0.9448, "a1": 0.0000, "a2": 5.4603}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "b98": { - "params": {"s6": 1.000, "s8": 0.7086, "a1": 0.0000, "a2": 6.0672}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hiss": { - "params": {"s6": 1.000, "s8": 1.6112, "a1": 0.0000, "a2": 7.3539}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "hse03": { - "params": {"s6": 1.000, "s8": 1.1243, "a1": 0.0000, "a2": 6.8889}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpssh": { - "params": {"s6": 1.000, "s8": 1.4076, "a1": 0.2660, "a2": 5.3761}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "revtpss0": { - "params": {"s6": 1.000, "s8": 1.6151, "a1": 0.2218, "a2": 5.7985}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "tpss1kcis": { - "params": {"s6": 1.000, "s8": 1.0542, "a1": 0.0000, "a2": 6.0201}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "thcthhyb": { - "params": {"s6": 1.000, "s8": 0.9585, "a1": 0.0000, "a2": 6.2303}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mn15": { - "params": {"s6": 1.000, "s8": 2.0971, "a1": 0.7862, "a2": 7.5923}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "lcwhpbe": { - "params": {"s6": 1.000, "s8": 1.1908, "a1": 0.2746, "a2": 5.3157}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "mpw2plyp": { - "params": {"s6": 1.000, "s8": 0.6223, "a1": 0.4105, "a2": 5.0136}, - "citation": " L. Goerigk, A. Hansen, C. Bauer, S. Ehrlich, A. Najibi, S. Grimme, Phys. Chem. Chem. Phys. 19, 32184-32215, 2017\n", - }, - "pbe0-dh": { - "params": {"s6": 0.840, "s8": 0.095, "a1": 0.000, "a2": 6.102}, - "citation": " D. Bousquet, E. Bremond, J. C. Sancho-Garcia, I. Ciofini, C. Adamo, Theor. Chem. Acc. 134, 1602, 2015\n", - }, - # https://www.chemie.u{'params': ni-bonn.de/pctc/mulliken-center/software/dft-d3/functiona}lsbj - "bop": {"params": {"s6": 1.000, "s8": 3.295, "a1": 0.4870, "a2": 3.5043}}, - "cam-b3lyp": {"params": {"s6": 1.000, "s8": 2.0674, "a1": 0.3708, "a2": 5.4743}}, - "wpbe": {"params": {"s6": 1.000, "s8": 1.8541, "a1": 0.3919, "a2": 5.0897}}, # formerly lcwpbe - "otpss": {"params": {"s6": 1.000, "s8": 2.7495, "a1": 0.4634, "a2": 4.3153}}, - "pbe38": {"params": {"s6": 1.000, "s8": 1.4623, "a1": 0.3995, "a2": 5.1405}}, - "pbesol": {"params": {"s6": 1.000, "s8": 2.9491, "a1": 0.4466, "a2": 6.1742}}, - "ptpss": {"params": {"s6": 0.750, "s8": 0.2804, "a1": 0.000, "a2": 6.5745}}, - "pwb6k": {"params": {"s6": 1.000, "s8": 0.9383, "a1": 0.1805, "a2": 7.7627}}, - "revssb": {"params": {"s6": 1.000, "s8": 0.4389, "a1": 0.4720, "a2": 4.0986}}, - "ssb": {"params": {"s6": 1.000, "s8": -0.1744, "a1": -0.0952, "a2": 5.2170}}, - "hcth120": {"params": {"s6": 1.000, "s8": 1.0821, "a1": 0.3563, "a2": 4.3359}}, - "revpbe0": {"params": {"s6": 1.000, "s8": 1.7588, "a1": 0.4679, "a2": 3.7619}}, - "revpbe38": {"params": {"s6": 1.000, "s8": 1.4760, "a1": 0.4309, "a2": 3.9446}}, - # unreferenced - # special HF/DFT with eBSSE correction - "hf/mixed": {"params": {"s6": 1.000, "s8": 3.9027, "a1": 0.5607, "a2": 4.5622}}, - "hf/sv": {"params": {"s6": 1.000, "s8": 2.1849, "a1": 0.4249, "a2": 4.2783}}, - "hf/minis": {"params": {"s6": 1.000, "s8": 0.9841, "a1": 0.1702, "a2": 3.8506}}, - "b3lyp/6-31gd": {"params": {"s6": 1.000, "s8": 4.0672, "a1": 0.5014, "a2": 4.8409}}, - # special HF-D3-gCP-SRB/MINIX parametrization - "hf3c": {"params": {"s6": 1.000, "s8": 0.8777, "a1": 0.4171, "a2": 2.9149}}, - # special HF-D3-gCP-SRB2/ECP-2G parametrization - "hf3cv": {"params": {"s6": 1.000, "s8": 0.5022, "a1": 0.3063, "a2": 3.9856}}, - # special PBEh-D3-gCP/def2-mSVP parametrization - "pbeh3c": {"params": {"s6": 1.000, "s8": 0.0000, "a1": 0.4860, "a2": 4.5000}}, - "core-dsd-blyp": {"params": {"s6": 0.500, "s8": 0.2130, "a1": 0.0000, "a2": 6.0519}}, - }, - }, - "d3mzeroatm": { - "formal": "D3MATM", - "alias": [], - "description": " Grimme's -D3 (zero-damping, short-range refitted) Dispersion Correction with ATM", - "citation": " Grimme S.; Antony J.; Ehrlich S.; Krieg H. (2010), J. Chem. Phys., 132: 154104\n" - + " Smith, D. G. A.; Burns, L. A.; Patkowski, K.; Sherrill, C. D. (2016), J. Phys. Chem. Lett.; 7: 2197\n", - "bibtex": "Grimme:2010:154104", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("sr6", 1.0), ("beta", 1.0), ("s9", 1.0)]), - "definitions": { - # D3 parameters loaded from d3mzero2b and from authoritative source below and s9 parameter added - }, - }, - "d3mzero2b": { - "formal": "D3M", - "alias": ["d3m", "d3mzero", "d3m2b"], - "description": " Grimme's -D3 (zero-damping, short-range refitted) Dispersion Correction", - "citation": " Grimme S.; Antony J.; Ehrlich S.; Krieg H. (2010), J. Chem. Phys., 132: 154104\n" - + " Smith, D. G. A.; Burns, L. A.; Patkowski, K.; Sherrill, C. D. (2016), J. Phys. Chem. Lett.; 7: 2197\n", - "bibtex": "Grimme:2010:154104", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("sr6", 1.0), ("beta", 1.0)]), - "definitions": { - "b2plyp": {"params": {"s6": 0.640, "s8": 0.717543, "sr6": 1.313134, "beta": 0.016035}}, - "b3lyp": {"params": {"s6": 1.000, "s8": 1.532981, "sr6": 1.338153, "beta": 0.013988}}, - "b97": {"params": {"s6": 1.000, "s8": 1.020078, "sr6": 1.151808, "beta": 0.035964}}, # formerly b97-d - "blyp": {"params": {"s6": 1.000, "s8": 1.841686, "sr6": 1.279637, "beta": 0.014370}}, - "bp86": {"params": {"s6": 1.000, "s8": 1.945174, "sr6": 1.233460, "beta": 0.000000}}, - "pbe": {"params": {"s6": 1.000, "s8": 0.000000, "sr6": 2.340218, "beta": 0.129434}}, - "pbe0": {"params": {"s6": 1.000, "s8": 0.000081, "sr6": 2.077949, "beta": 0.116755}}, - "wpbe": {"params": {"s6": 1.000, "s8": 1.280619, "sr6": 1.366361, "beta": 0.003160}}, # formerly lcwpbe - "sapt0": {"params": {"s6": 1.000, "s8": 0.885517, "sr6": 1.383214, "beta": 0.075488}}, # JBS 01/2021 - "hf": {"params": {"s6": 1.000, "s8": 0.885517, "sr6": 1.383214, "beta": 0.075488}}, # JBS 01/2021 - }, - }, - "d3mbjatm": { - "formal": "D3M(BJ)ATM", - "alias": [], - "description": " Grimme's -D3 (BJ-damping, short-range refitted) Dispersion Correction with ATM", - "citation": " Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456\n" - + " Smith, D. G. A.; Burns, L. A.; Patkowski, K.; Sherrill, C. D. (2016), J. Phys. Chem. Lett.; 7: 2197\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("a1", 1.0), ("a2", 1.0), ("s9", 1.0)]), - "definitions": { - # D3 parameters loaded from d3mbj2b and from authoritative source below and s9 parameter added - }, - }, - "d3mbj2b": { - "formal": "D3M(BJ)", - "alias": ["d3mbj"], - "description": " Grimme's -D3 (BJ-damping, short-range refitted) Dispersion Correction", - "citation": " Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456\n" - + " Smith, D. G. A.; Burns, L. A.; Patkowski, K.; Sherrill, C. D. (2016), J. Phys. Chem. Lett.; 7: 2197\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict([("s6", 1.0), ("s8", 1.0), ("a1", 1.0), ("a2", 1.0)]), - "definitions": { - "b2plyp": {"params": {"s6": 0.640, "s8": 0.672820, "a1": 0.486434, "a2": 3.656466}}, - "b3lyp": {"params": {"s6": 1.000, "s8": 1.466677, "a1": 0.278672, "a2": 4.606311}}, - "b97": {"params": {"s6": 1.000, "s8": 1.206988, "a1": 0.240184, "a2": 3.864426}}, # formerly b97-d - "blyp": {"params": {"s6": 1.000, "s8": 1.875007, "a1": 0.448486, "a2": 3.610679}}, - "bp86": {"params": {"s6": 1.000, "s8": 3.140281, "a1": 0.821850, "a2": 2.728151}}, - "pbe": {"params": {"s6": 1.000, "s8": 0.358940, "a1": 0.012092, "a2": 5.938951}}, - "pbe0": {"params": {"s6": 1.000, "s8": 0.528823, "a1": 0.007912, "a2": 6.162326}}, - "wpbe": {"params": {"s6": 1.000, "s8": 0.906564, "a1": 0.563761, "a2": 3.593680}}, # formerly lcwpbe - "sapt0": {"params": {"s6": 1.000, "s8": 0.713190, "a1": 0.079541, "a2": 3.627854}}, # JBS 01/2021 - "hf": {"params": {"s6": 1.000, "s8": 0.713190, "a1": 0.079541, "a2": 3.627854}}, # JBS 01/2021 - }, - }, - "d3opatm": { - "formal": "D3(op)ATM", - "alias": [], - "description": " D3 dispersion correction with optimized power damping function with ATM. Based on rational damping function and additional zero-damping like power function.", - "citation": " S. Grimme, S. Ehrlich, and L. Goerigk., Comput. Chem., 32:1456–1465, 2011. doi:10.1002/jcc.21759.\n" - + " Jonathon Witte, Narbe Mardirossian, Jeffrey B Neaton, and Martin Head-Gordon., J. Chem. Theory Comput., 13(5):2043–2052, 2017. doi:10.1021/acs.jctc.7b00176.\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict( - [("s6", 1.0), ("s8", 1.0), ("a1", 1.0), ("a2", 1.0), ("beta", 0.0), ("s9", 1.0)] - ), - "definitions": { - # D3 parameters loaded from d3op2b and from authoritative source below and s9 parameter added - }, - }, - "d3op2b": { - "formal": "D3(op)", - "alias": ["d3op"], - "description": " D3 dispersion correction with optimized power damping function. Based on rational damping function and additional zero-damping like power function.", - "citation": " S. Grimme, S. Ehrlich, and L. Goerigk., Comput. Chem., 32:1456–1465, 2011. doi:10.1002/jcc.21759.\n" - + " Jonathon Witte, Narbe Mardirossian, Jeffrey B Neaton, and Martin Head-Gordon., J. Chem. Theory Comput., 13(5):2043–2052, 2017. doi:10.1021/acs.jctc.7b00176.\n", - "bibtex": "Grimme:2011:1456", - "default": collections.OrderedDict( - [("s6", 1.0), ("s8", 1.0), ("a1", 1.0), ("a2", 1.0), ("beta", 0.0)], - ), - "definitions": { - # will be loaded later - # From https://github.com/dftd3/simple-dftd3/blob/main/assets/parameters.toml - }, - }, - "nl": { - "formal": "NL", - "alias": [], - "description": " Grimme's -NL (DFT plus VV10 correlation) ", - "citation": " Hujo, W.; Grimme, S; (2011), J. Chem. Theory Comput.; 7:3866\n", - "bibtex": "Hujo:2011:3866", - "default": collections.OrderedDict([("b", 1.0), ("c", 0.0093)]), - "definitions": { - "blyp": { - "params": {"b": 4.000, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "hf": { - "params": {"b": 3.900, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, # not implemented - "revpbe": { - "params": {"b": 3.700, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "revpbe38": { - "params": {"b": 4.700, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "b3lyp": { - "params": {"b": 4.800, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "b3pw91": { - "params": {"b": 4.500, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "revpbe0": { - "params": {"b": 4.300, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme J. Chem. Theory Comput. 7, 3866-3871, 2011\n", - }, - "bp86": { - "params": {"b": 4.400, "c": 0.0093}, - "citation": " M. K. Kesharwani, A. Karton, J.M. L. Martin, J. Chem. Theory Comput. 12, 444-454, 2016\n", - }, # unclear if this is the real origin - "pbe0": { - "params": {"b": 6.900, "c": 0.0093}, - "citation": " M. K. Kesharwani, A. Karton, J.M. L. Martin, J. Chem. Theory Comput. 12, 444-454, 2016\n", - }, # unclear if this is the real origin - "pbe": { - "params": {"b": 6.400, "c": 0.0093}, - "citation": " M. K. Kesharwani, A. Karton, J.M. L. Martin, J. Chem. Theory Comput. 12, 444-454, 2016\n", - }, # unclear if this is the real origin - "tpss0": { - "params": {"b": 5.500, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme, J. Chem. Theory Comput. 9, 308-315, 2013\n", - }, - "tpss": { - "params": {"b": 5.000, "c": 0.0093}, - "citation": " W. Hujo, S. Grimme, J. Chem. Theory Comput. 9, 308-315, 2013\n", - }, - "b2gpplyp": { - "params": {"b": 9.900, "c": 0.0093}, - "citation": " M. K. Kesharwani, A. Karton, J.M. L. Martin, J. Chem. Theory Comput. 12, 444-454, 2016\n", - }, - "b2plyp": { - "params": {"b": 7.800, "c": 0.0093}, - "citation": " J. Calbo, E. Orti, J. C. Sancho-Garcia, J. Arago, J. Chem. Theory Comput. 11, 932-939, 1015\n", - }, - "pwpb95": { - "params": {"b": 11.100, "c": 0.0093}, - "citation": " F. Yu J. Chem. Theory Comput. 10, 4400-4407, 2014\n", - }, - "revtpss": { - "params": {"b": 5.465, "c": 0.0093}, - "citation": " H. Kruse, P. Banas, J. Sponer, JCTC 2018, 10.1021/acs.jctc.8b00643 \n", - }, # "just accepted" - }, - }, - "d1": { - "formal": "D1", - "alias": [], - "description": " Grimme's -D1 Dispersion Correction", - "citation": " Grimme, S. (2004), J. Comp. Chem., 25: 1463-1473\n", - "bibtex": "Grimme:2004:1463", - "default": collections.OrderedDict([("s6", 1.0)]), - "definitions": {"***": {"params": {"s6": 1.0}}}, - }, - "chg": { - "formal": "CHG", - "alias": [], - "description": " Chai and Head-Gordon Dispersion Correction", - "citation": " Chai, J.-D.; Head-Gordon, M. (2010), J. Chem. Phys., 132: 6615-6620\n", - "bibtex": "Chai:2010:6615", - "default": collections.OrderedDict([("s6", 1.0)]), - "definitions": {"***": {"params": {"s6": 1.0}}}, # disp->d_ = 6.0; - }, - "das2009": { - "formal": "DAS2009", - "alias": [], - "description": " Podeszwa and Szalewicz Dispersion Correction", - "citation": " Pernal, K.; Podeszwa, R.; Patkowski, K.; Szalewicz, K. (2009), Phys. Rev. Lett., 103: 263201\n", - "bibtex": "Pernal:2009:263201", - "default": collections.OrderedDict([("s6", 1.0)]), - "definitions": {"***": {"params": {"s6": 1.0}}}, - }, - "das2010": { - "formal": "DAS2010", - "alias": [], - "description": " Podeszwa and Szalewicz Dispersion Correction", - "citation": " Podeszwa, R.; Pernal, K.; Patkowski, K.; Szalewicz, K. (2010), J. Phys. Chem. Lett., 1: 550\n", - "bibtex": "Podeszwa:2010:550", - "default": collections.OrderedDict([("s6", 1.0)]), - "definitions": {"***": {"params": {"s6": 1.0}}}, - }, - "atmgr": { - "formal": "ATM(GR)", - "alias": [], - "description": " Grimme approximate Axilrod-Teller-Muto 3-body Dispersion Correction", - "citation": " Grimme S.; Antony J.; Ehrlich S.; Krieg H. (2010), J. Chem. Phys., 132: 154104\n", - "bibtex": "Grimme:2010:154104", - "default": collections.OrderedDict([("alpha6", 14.0)]), - "definitions": { - "***": {"params": {"alpha6": 14.0}, "citation": ""} # alpha6 = 14 => damping parameter used is alp8 = 16 - }, - }, - "dmp2": { - "formal": "DMP2", - "alias": [], - "description": " Beran's MP2D Dispersion Correction for MP2", - "citation": " Rezac, J.; Greenwell, C.; Beran, G. (2018), J. Chem. Theory Comput., 14: 4711-4721\n", - "bibtex": "Rezac:2018:4711", - "doi": "10.1021/acs.jctc.8b00548", - "default": collections.OrderedDict([("s8", 1.187), ("a1", 0.944), ("a2", 0.480), ("rcut", 0.72), ("w", 0.20)]), - "definitions": { - "mp2": {"params": {"s8": 1.187, "a1": 0.944, "a2": 0.480, "rcut": 0.72, "w": 0.20}} # Rezac:2018:4711 - }, - }, - "d4bjeeqatm": { - "formal": "D4(BJ,EEQ)ATM", - "alias": ["d4", "d4bj", "d4(bj)"], - "description": " Grimme's -D4 (BJ-damping) Dispersion Correction with ATM", - "citation": " Caldeweyher, E.; Ehlert, S.; Hansen, A.; Neugebauer, H.; Spicher, S.; Bannwarth, C.; Grimmme, S., J. Chem. Phys. 150, 154122 (2019)\n", - "bibtex": "Caldeweyher:2019:154122", - "doi": "10.1063/1.5090222150", - "default": collections.OrderedDict( - [ - ("a1", 1.0), - ("a2", 1.0), - ("alp", 16.0), - ("s6", 1.0), - ("s8", 1.0), - ("s9", 1.0), - ("ga", 3.0), - ("gc", 2.0), - ("wf", 6.0), - ] - ), - "definitions": { - # D4 parameters loaded below from authoritative source below. Keep a couple for reference - # "b3lyp": {"params": {"a1": 0.40868035, "a2": 4.53807137, "alp": 16.0, "s6": 1.0, "s8": 2.02929367, "s9": 1.0}}, - # "pbe": {"params": {"a1": 0.38574991, "a2": 4.80688534, "alp": 16.0, "s6": 1.0, "s8": 0.95948085, "s9": 1.0}}, - }, - }, -} - -try: - from dftd4 import __version__ as d4_version - - new_d4_api = parse_version(d4_version) >= parse_version("3.5.0") -except (ModuleNotFoundError, ImportError): - new_d4_api = False - -# different defaults for dftd4 versions < 3.5.0 -if not new_d4_api: - dashcoeff["d4bjeeqatm"]["default"] = collections.OrderedDict( - [("a1", 1.0), ("a2", 1.0), ("alp", 16.0), ("s6", 1.0), ("s8", 1.0), ("s9", 1.0)] - ) - -# for d3*atm, only skeleton entries with metadata defined above. below copies in parameters from d3*2b -for d in ["d3zero", "d3bj", "d3mzero", "d3mbj", "d3op"]: - for k, v in dashcoeff[d + "2b"]["definitions"].items(): - dashcoeff[d + "atm"]["definitions"][k] = copy.deepcopy(dashcoeff[d + "2b"]["definitions"][k]) - dashcoeff[d + "atm"]["definitions"][k]["params"][ - "s9" - ] = 1.0 # set twice: this one adds s9 to the params list for atm - - -def _get_d4bj_definitions() -> dict: - """DFTD4 provides access to damping parameters on per functional basis. - But we want all of them. - - We let DFTD4 take care of finding and loading the parameter file, - but to get all parameters, we implement the logic to read those - parameters ourselves again. - - This method is upstreamed in dftd4 >3.3.0 and could be replaced in the future. - """ - - try: - from dftd4.parameters import get_data_file_name, load_data_base - except ModuleNotFoundError: - return {} - - def get_params(entry: dict, base: dict, defaults: list) -> dict: - """Retrive the parameters from the data base, make sure the default - values are applied correctly in the process. In case we have multiple - defaults search for the first of the list defined for this method.""" - - for default in defaults: - try: - params = base[default].copy() - params.update(**entry[default]) - params.pop("mbd", None) - params.pop("damping", None) - return params - except KeyError: - continue - - raise KeyError("No entry for " + method + " in parameter data base") - - try: - _data_base = load_data_base(get_data_file_name()) - except FileNotFoundError: - return {} - - try: - _defaults = _data_base["default"]["d4"] - _base = _data_base["default"]["parameter"]["d4"] - _parameters = _data_base["parameter"] - except KeyError: - return {} - - definitions = {} - - for method in _parameters: - try: - _entry = _parameters[method]["d4"] - params = get_params(_entry, _base, _defaults) - - citation = params.pop("doi", None) - # Make Psi4's citation style checker happy - if citation is not None: - citation = " " + citation + "\n" - definitions[method] = dict( - params=params, - citation=citation, - ) - except KeyError: - continue - - # defaults ga, gc, wf are not in the toml parameter file and need to be provided by qcengine - if new_d4_api: - for entry in definitions.keys(): - definitions[entry]["params"]["ga"] = 3.0 - definitions[entry]["params"]["gc"] = 2.0 - definitions[entry]["params"]["wf"] = 6.0 - - return definitions - - -dashcoeff["d4bjeeqatm"]["definitions"].update(_get_d4bj_definitions()) - -try: - - def _get_d3_definitions(dashlevel: str) -> dict: - """ - Lucky for us dftd3 provides access to all damping parameters directly. - - However, we are not happy with the structure of the dict and - therefore do some restructuring. The below solution tries to bend - the data returned by dftd3 into the right shape to make qcng happy. - """ - - from dftd3.parameters import get_all_damping_params - - # The names here are the subset allowed by qcng with the names used in dftd3 - allowed = { - "bj": ["a1", "a2", "s6", "s8", "s9"], - "zero": ["rs6", "rs8", "alp", "s6", "s8", "s9"], - "mbj": ["a1", "a2", "s6", "s8", "s9"], - "mzero": ["rs6", "rs8", "alp", "s6", "s8", "bet", "s9"], - "op": ["a1", "a2", "s6", "s8", "bet", "s9"], - } - # mapping from dftd3 to qcng names, also, we have to reverse it later again - rename = { - "rs6": "sr6", - "rs8": "sr8", - "alp": "alpha6", - "bet": "beta", - } - - # FIXME: dict comprehension at its finest - # - # 1. insert an additional level each method - # 2. filter parameter names accepted by qcng - # 3. map from dftd3 to qcng names - # 4. return the whole mess - return { - key: { - "params": { - rename.get(param, param): value for param, value in params.items() if param in allowed[dashlevel] - } - } - for key, params in get_all_damping_params([dashlevel]).items() - } - - dashcoeff["d3bjatm"]["definitions"].update(_get_d3_definitions("bj")) - dashcoeff["d3zeroatm"]["definitions"].update(_get_d3_definitions("zero")) - dashcoeff["d3mbjatm"]["definitions"].update(_get_d3_definitions("mbj")) - dashcoeff["d3mzeroatm"]["definitions"].update(_get_d3_definitions("mzero")) - dashcoeff["d3opatm"]["definitions"].update(_get_d3_definitions("op")) - - for d in ["d3zero", "d3bj", "d3mzero", "d3mbj", "d3op"]: - for k, v in dashcoeff[d + "atm"]["definitions"].items(): - dashcoeff[d + "atm"]["definitions"][k]["params"][ - "s9" - ] = 1.0 # set twice: this one establishes 1.0 if upstream is still 0.0 (pre v0.7.0 simple-dftd3) - -except ModuleNotFoundError: - pass - - -def get_dispersion_aliases(): - """Returns dict where keys are all (lowercased) strings that are - interpretable as dashlevels, and values are the keys dealiased - (d->d2) and deformalized (d3(bj)->d3bj) into valid dashcoeff keys. - - """ - alias = {} - for dash, ddash in dashcoeff.items(): - alias[dash] = dash - alias[ddash["formal"].lower()] = dash - for al in ddash["alias"]: - alias[al] = dash - - return alias - - -def from_arrays( - name_hint: Optional[str] = None, - level_hint: Optional[str] = None, - param_tweaks: Union[List[float], Dict[str, float]] = None, - dashcoeff_supplement: Optional[Dict[str, Dict]] = None, - verbose: int = 1, -): - """Use the three paths of empirical dispersion parameter information - (DFT functional, dispersion correction level, and particular - parameters) to populate the parameter array and validate a - "functional-dispersion" label. - - Parameters - ---------- - name_hint - Name of functional (func only, func & disp, or disp only) for - which to compute dispersion (e.g., blyp, BLYP-D2, blyp-d3bj, - blyp-d3(bj), hf+d). Any or all parameters initialized from - `dashcoeff[dashlevel][functional-without-dashlevel]` or - `dashcoeff_supplement[dashlevel][functional-with-dashlevel] - can be overwritten via `param_tweaks`. - level_hint - Name of dispersion correction to be applied (e.g., d, D2, - d3(bj), das2010). Must be key in `dashcoeff` or "alias" or - "formal" to one. - param_tweaks - Values for the same keys as `dashcoeff[dashlevel]['default']` - (and same order if list) used to override any or all values - initialized by `name_hint`. Extra parameters will error. - dashcoeff_supplement - Dictionary of the same structure as `dashcoeff` that contains - in "definitions" field full functional names, rather than - fctl less dashlvl. Used to validate dict_builder fctls with - dispersion or identify disp level from just `name_hint`. - verbose - Amount of printing. - - Returns - ------- - dict - Metadata defining dispersion calculation. - - dashlevel : {'d1', 'd2', 'd3zero2b', 'd3bj2b', 'd3mzero2b', 'd3mbj2b', 'd3op2b', 'd3zeroatm', 'd3bjatm', 'd3mzeroatm', 'd3mbjatm', 'd3opatm', 'chg', 'das2009', 'das2010', 'nl', "d4bjeeqatm"} - Name (de-aliased, de-formalized, lowercase) of dispersion - correction -- atom data, dispersion model, damping functional - form -- to be applied. Resolved from `name_hint` and/or - `level_hint` into a key of `dashparam.dashcoeff`. - dashparams : dict - Complete (number and parameter names vary by `dashlevel`) - set of parameter values defining the flexible parts - of `dashlevel`. Resolved into a complete set (keys of - dashcoeff[dashlevel]['default']) from `name_hint` and/or - `dashcoeff_supplement` and/or user `param_tweaks`. - fctldash : str - If `dashparams` for `dashlevel` corresponds to a defined, - named, untweaked "functional-dashlevel" set, then that - label. Otherwise, empty string. - dashparams_citation : str - If `dashparams` for `dashlevel` corresponds to a defined, - named, untweaked "functional-dashlevel" set, and that - parameter set has a literature citation, then that - citation. Otherwise, empty string. - - Notes - ----- - * No parameter is required, but somewhere the dispersion level and - intended parameters must be clear. Explicit contradictory info will - cause an error. - * Function intended to be idempotent. - - """ - try: - # try/except block retrofits Psi4 for pre-2b/atm-split in dashcoeff - import psi4 - - if "d3bj" in psi4.procrouting.empirical_dispersion._engine_can_do["dftd3"]: - psi4.procrouting.empirical_dispersion._engine_can_do["dftd3"] = [ - "d2", - "d3zero2b", - "d3bj2b", - "d3mzero2b", - "d3mbj2b", - ] - for d in ["d3zero", "d3bj", "d3mzero", "d3mbj"]: - psi4.procrouting.empirical_dispersion._capable_engines_for_disp[ - d + "2b" - ] = psi4.procrouting.empirical_dispersion._capable_engines_for_disp.pop(d) - except ImportError: - pass - - if verbose > 1: - print( - "empirical_dispersion_resources.from_arrays HINTS:", - name_hint, - level_hint, - param_tweaks, - bool(dashcoeff_supplement), - ) - - # << 0 >> prep - if dashcoeff_supplement is not None: - supplement_dashlevel_lookup = {} - for disp, ddisp in dashcoeff_supplement.copy().items(): - for func, params in ddisp["definitions"].items(): - try: - dashcoeff[disp] - except KeyError: - # try/except block accommodates dashcoeff_supplement from pre-2b/atm-split in dashcoeff - disp = get_dispersion_aliases()[disp] - dashcoeff_supplement[disp] = ddisp - if params["params"].keys() != dashcoeff[disp]["default"].keys(): - if verbose > 2: - print( - "Warning: trouble in dict_builder def:", - func, - params["params"].keys(), - "!=", - dashcoeff[disp]["default"].keys(), - ) - else: - supplement_dashlevel_lookup[func] = disp - - # << 1 >> use name_hint and/or level_hint to determine intended dispersion level - if name_hint is None and level_hint is None: - raise InputError( - """Can't guess -D level without name_hint ({}) or level_hint ({})""".format(name_hint, level_hint) - ) - - if level_hint is None: - dashlevel_candidate_1 = None - else: - level_hint = level_hint.lower() - try: - dashlevel_candidate_1 = get_dispersion_aliases()[level_hint] - except KeyError: - raise InputError( - """Requested -D correction level ({}) not among ({})""".format(level_hint, dashcoeff.keys()) - ) - - if name_hint is None: - dashlevel_candidate_2 = None - name_key = None - disp_params = {} - else: - name_hint = name_hint.lower() - trial_split = name_hint.rsplit("-", 1) - - if name_hint in get_dispersion_aliases(): - dashlevel_candidate_2 = get_dispersion_aliases()[name_hint] - if list(dashcoeff[dashlevel_candidate_2]["definitions"]) == ["***"]: - # case disp-only fctl-indep: chg, atmgr - name_key = "***" - disp_params = dashcoeff[dashlevel_candidate_2]["definitions"][name_key]["params"] - else: - # case disp-only: d3, d3zero, d3(bj) - name_key = None - disp_params = {} - elif (dashcoeff_supplement is not None) and name_hint in supplement_dashlevel_lookup: - # case fctldisp: wb97x-d, hf+d - dashlevel_candidate_2 = supplement_dashlevel_lookup[name_hint] - name_key = name_hint - disp_params = dashcoeff_supplement[dashlevel_candidate_2]["definitions"][name_hint]["params"] - elif ( - len(trial_split) == 2 - and trial_split[1] in get_dispersion_aliases() - and trial_split[0] in dashcoeff[get_dispersion_aliases()[trial_split[1]]]["definitions"] - ): - # case fctldisp: b3lyp-d3, b3lyp-d3zero, b3lyp-d3(bj) - dashlevel_candidate_2 = get_dispersion_aliases()[trial_split[1]] - name_key = trial_split[0] - disp_params = dashcoeff[dashlevel_candidate_2]["definitions"][trial_split[0]]["params"] - elif ( - len(trial_split) == 2 - and trial_split[1] in get_dispersion_aliases() - and list(dashcoeff[get_dispersion_aliases()[trial_split[1]]]["definitions"]) == ["***"] - ): - # case fctldisp: asdf-chg, pbe-atmgr - dashlevel_candidate_2 = get_dispersion_aliases()[trial_split[1]] - name_key = "***" - disp_params = dashcoeff[dashlevel_candidate_2]["definitions"][name_key]["params"] - elif (level_hint is not None) and name_hint in dashcoeff[dashlevel_candidate_1]["definitions"]: - # case fctl: b3lyp - dashlevel_candidate_2 = None - name_key = name_hint - disp_params = dashcoeff[dashlevel_candidate_1]["definitions"][name_hint]["params"] - elif name_hint == "": - dashlevel_candidate_2 = None - name_key = None - disp_params = {} - # elif ((level_hint is not None) and list(dashcoeff[get_dispersion_aliases()[level_hint]]['definitions']) == ['***']): - # dashlevel_candidate_2 = get_dispersion_aliases()[level_hint] - # name_key = '***' - # disp_params = dashcoeff[dashlevel_candidate_2]['definitions'][name_key]['params'] - else: - # dashlevel_candidate_2 = None - raise InputError("""Can't guess -D correction level from ({})""".format(name_hint)) - - disp_params = copy.deepcopy(disp_params) - - if dashlevel_candidate_1 is None and dashlevel_candidate_2 is None: - raise InputError( - f"""Can't guess -D correction level from name_hint ({name_hint}) and level_hint ({level_hint})""" - ) - elif dashlevel_candidate_1 is not None and dashlevel_candidate_2 is not None: - if dashlevel_candidate_1 != dashlevel_candidate_2: - raise InputError( - f"""Inconsistent -D correction level ({dashlevel_candidate_2} != {dashlevel_candidate_1}) from name_hint ({name_hint}) and level_hint ({level_hint})""" - ) - dashleveleff = dashlevel_candidate_1 or dashlevel_candidate_2 - - allowed_params = dashcoeff[dashleveleff]["default"].keys() - - # << 2 >> use name_hint and/or param_tweaks to determine intended dispersion parameters - if name_hint is None and param_tweaks is None: - raise InputError( - """Can't guess -D parameters without name_hint ({}) or param_tweaks ({})""".format(name_hint, param_tweaks) - ) - - if isinstance(param_tweaks, list): - param_tweaks = dict(zip(allowed_params, param_tweaks)) - - if param_tweaks is None: - param_tweaks = {} - - if not set(param_tweaks.keys()).issubset(allowed_params): - raise InputError( - "Requested keys ({}) not among allowed ({}) for dispersion level ({})".format( - list(param_tweaks.keys()), list(allowed_params), dashleveleff - ) - ) - - disp_params.update(param_tweaks) - - if disp_params.keys() != allowed_params: - raise InputError( - "Requested keys ({}) insufficient ({}) for dispersion level ({})".format( - list(param_tweaks.keys()), list(allowed_params), dashleveleff - ) - ) - - # << 3 >> use final dashlevel and disp_params to determine if a defined "fctl-disp" label exists - # * plucks any citation for the parameters from definition source - # * if/elif chooses right label when some fctls have identical param sets - if ( - (name_hint is not None) - and (dashcoeff_supplement is not None) - and (name_key in dashcoeff_supplement[dashleveleff]["definitions"]) - and (disp_params == dashcoeff_supplement[dashleveleff]["definitions"][name_key]["params"]) - ): - citeff = dashcoeff_supplement[dashleveleff]["definitions"][name_key].get("citation", "") - if name_key == "***": - fctldasheff = "" - else: - fctldasheff = name_key - elif name_hint not in [None, ""] and (disp_params == dashcoeff[dashleveleff]["definitions"][name_key]["params"]): - citeff = dashcoeff[dashleveleff]["definitions"][name_key].get("citation", "") - if name_key == "***": - fctldasheff = dashcoeff[dashleveleff]["formal"].lower() - else: - fctldasheff = "-".join([name_key, dashcoeff[dashleveleff]["formal"].lower()]) - else: - if dashcoeff_supplement is not None: - for func, params in dashcoeff_supplement[dashleveleff]["definitions"].items(): - if disp_params == params["params"]: - fctldasheff = func - citeff = params.get("citation", "") - break - - for func, params in dashcoeff[dashleveleff]["definitions"].items(): - if disp_params == params["params"]: - fctldasheff = "-".join([func, dashcoeff[dashleveleff]["formal"].lower()]) - citeff = params.get("citation", "") - break - else: - fctldasheff = "" - citeff = "" - - # TODO right now fctldasheff is empty if undefined. use '-dash' or 'custom dash' instead? - # TODO right now citation is empty if undefined. remove key or use None or False instead? - - if verbose > 1: - print( - f"empirical_dispersion_resources.from_arrays RESOLVED: dashlevel={dashleveleff}, dashparams={disp_params}, fctldash={fctldasheff}, dashparams_citation={citeff}" - ) - - return { - "dashlevel": dashleveleff, - "dashparams": disp_params, - "fctldash": fctldasheff, - "dashparams_citation": citeff, - } diff --git a/qcengine/programs/gamess/__init__.py b/qcengine/programs/gamess/__init__.py deleted file mode 100644 index daacff7f3..000000000 --- a/qcengine/programs/gamess/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .runner import GAMESSHarness diff --git a/qcengine/programs/gamess/germinate.py b/qcengine/programs/gamess/germinate.py deleted file mode 100644 index a653b784d..000000000 --- a/qcengine/programs/gamess/germinate.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import Any, Dict - -from qcengine.exceptions import InputError - - -def muster_modelchem(method: str, derint: int) -> Dict[str, Any]: - """Converts the QC method into GAMESS keywords.""" - - method = method.lower() - opts = {} - - runtyp = { - 0: "energy", - 1: "gradient", - 2: "hessian", - #'properties': 'prop', - }[derint] - - opts["contrl__runtyp"] = runtyp - - if method == "gamess": - pass - - elif method in ["scf", "hf"]: - pass - - # opts['contrl__mplevl'] = 0 - # opts['contrl__cityp'] = 'none' - # opts['contrl__cctyp'] = 'none' - - elif method == "mp2": - opts["contrl__mplevl"] = 2 - - elif method == "lccd": - opts["contrl__cctyp"] = "lccd" - - elif method == "ccd": - opts["contrl__cctyp"] = "ccd" - - elif method == "ccsd": - opts["contrl__cctyp"] = "ccsd" - - elif method in ["ccsd+t(ccsd)", "ccsd(t)"]: - opts["contrl__cctyp"] = "ccsd(t)" - - elif method == "pbe": - opts["contrl__dfttyp"] = "pbe" - - elif method == "b3lyp": - opts["contrl__dfttyp"] = "b3lypv1r" - - elif method == "b3lyp5": - opts["contrl__dfttyp"] = "b3lyp" - - else: - raise InputError(f"Method not recognized: {method}") - - return opts diff --git a/qcengine/programs/gamess/harvester.py b/qcengine/programs/gamess/harvester.py deleted file mode 100644 index 1597dbd05..000000000 --- a/qcengine/programs/gamess/harvester.py +++ /dev/null @@ -1,680 +0,0 @@ -"""Compute quantum chemistry using Iowa State's GAMESS executable.""" - -import logging -import pprint -import re -from decimal import Decimal -from typing import Dict, Optional, Tuple - -import numpy as np -import qcelemental as qcel -from qcelemental.models import Molecule -from qcelemental.molparse import regex - -from ..util import PreservingDict, load_hessian - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) -logger = logging.getLogger(__name__) - - -def harvest( - in_mol: Molecule, method: str, gamessout: str, **outfiles -) -> Tuple[PreservingDict, Optional[np.ndarray], Optional[np.ndarray], Molecule]: - """Parses all the pieces of output from gamess: the stdout in - *gamessout* Scratch files are not yet considered at this moment. - """ - qcvars, calc_mol, calc_grad, module = harvest_output(gamessout) - - datasections = {} - if outfiles.get("gamess.dat"): - datasections = harvest_datfile(outfiles["gamess.dat"]) - - calc_hess = None - if "$HESS" in datasections: - calc_hess = load_hessian(datasections["$HESS"], dtype="gamess") - if np.count_nonzero(calc_hess) == 0: - calc_hess = None - - # Sometimes the hierarchical setting of CURRENT breaks down - if method == "ccsd+t(ccsd)": - qcvars["CURRENT CORRELATION ENERGY"] = qcvars["CCSD+T(CCSD) CORRELATION ENERGY"] - qcvars["CURRENT ENERGY"] = qcvars["CCSD+T(CCSD) TOTAL ENERGY"] - - if calc_mol: - qcvars["NUCLEAR REPULSION ENERGY"] = str(round(calc_mol.nuclear_repulsion_energy(), 8)) - if in_mol: - if abs(calc_mol.nuclear_repulsion_energy() - in_mol.nuclear_repulsion_energy()) > 1.0e-3: - raise ValueError( - f"""GAMESS outfile (NRE: {calc_mol.nuclear_repulsion_energy()}) inconsistent with AtomicInput.molecule (NRE: {in_mol.nuclear_repulsion_energy()}).""" - ) - - # Frame considerations - # * `in_mol` built with deliberation and with all fields accessible. - # * `calc_mol` has the internally consistent geometry frame but otherwise dinky (geom & symbols & maybe chgmult). - if in_mol.fix_com and in_mol.fix_orientation: - # Impose input frame if important as signalled by fix_*=T - return_mol = in_mol - _, data = calc_mol.align(in_mol, atoms_map=False, mols_align=True, run_mirror=True, verbose=0) - mill = data["mill"] - - else: - return_mol, _ = in_mol.align(calc_mol, atoms_map=False, mols_align=True, run_mirror=True, verbose=0) - mill = qcel.molutil.compute_scramble( - len(in_mol.symbols), do_resort=False, do_shift=False, do_rotate=False, do_mirror=False - ) # identity AlignmentMill - - return_grad = None - if calc_grad is not None: - return_grad = mill.align_gradient(calc_grad) - - return_hess = None - if calc_hess is not None: - return_hess = mill.align_hessian(np.array(calc_hess)) - - else: - raise ValueError("""No coordinate information extracted from gamess output.""") - - return qcvars, return_hess, return_grad, return_mol, module - - -def harvest_datfile(datfile: str) -> Dict[str, str]: - sections = datfile.split(r"$END") - goodies = {} - - for i, sec in enumerate(sections): - lsec = sec.split("\n") - for iln, ln in enumerate(lsec): - if ln.strip(): - key = ln.strip() - break - goodies[key] = "\n".join(lsec[iln + 1 :]) - - return goodies - - -def harvest_output(outtext): - """Function to separate portions of a gamess output file *outtext*, - divided by "Step". - """ - pass_qcvar = [] - pass_coord = [] - pass_grad = [] - - for outpass in re.split( - # fmt: off - r"^\s+" + r"--------" + r"NSERCH:" + r"([1-9][0-9][0-9][0-9]*)" + r"\s*" + - r"^\s+" + r"--------", - # fmt: on - outtext, - re.MULTILINE, - ): - qcvar, gamesscoord, gamessgrad, module = harvest_outfile_pass(outpass) - pass_qcvar.append(qcvar) - pass_coord.append(gamesscoord) - pass_grad.append(gamessgrad) - - retindx = -1 if pass_coord[-1] else -2 - return pass_qcvar[retindx], pass_coord[retindx], pass_grad[retindx], module - - -def harvest_outfile_pass(outtext): - """Function to read gamess output file *outtext* and parse important - quantum chemical information from it in - """ - qcvar = PreservingDict() - qcvar_coord = None - qcvar_grad = None - module = None - - NUMBER = r"(?x:" + regex.NUMBER + ")" - - # If calculation fail to converge - mobj = re.search(r"^\s+" + r"(?:GAMESS TERMINATED ABNORMALLY)" + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("GAMESS TERMINATED ABNORMALLY") - - # If calculation converged - else: - mobj = re.search( - r"^\s+" + r"(?: TOTAL ENERGY)" + r"\s+=\s*" + NUMBER + r"s*$", outtext, re.MULTILINE - ) - if mobj: - logger.debug("matched gamess_RHF energy") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["SCF TOTAL ENERGY"] = mobj.group(1) - - # Process NRE - mobj = re.search( - r"^\s+" + r"(?: NUCLEAR REPULSION ENERGY)" + r"\s+=\s*" + NUMBER + r"\s*$", outtext, re.MULTILINE - ) - if mobj: - logger.debug("matched NRE") - qcvar["NUCLEAR REPULSION ENERGY"] = mobj.group(1) - - # Process calcinfo - mobj = re.search( - # fmt: off - r"^\s+" + r"NUMBER OF OCCUPIED ORBITALS \(ALPHA\)" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF OCCUPIED ORBITALS \(BETA \)" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"TOTAL NUMBER OF ATOMS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched calcinfo") - qcvar["N ALPHA ELECTRONS"] = mobj.group("nao") - qcvar["N BETA ELECTRONS"] = mobj.group("nbo") - - mobj = re.search( - # fmt: off - r"^\s+" + r"TOTAL NUMBER OF MOS IN VARIATION SPACE=" + r"\s+" + r"(?P\d+)" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched calcinfo 2") - qcvar["N MOLECULAR ORBITALS"] = mobj.group("nmo") - qcvar["N BASIS FUNCTIONS"] = mobj.group("nmo") # TODO BAD - else: - mobj2 = re.search( - # fmt: off - r"^\s+" + r"NUMBER OF CORE -A- ORBITALS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF CORE -B- ORBITALS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF OCC. -A- ORBITALS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF OCC. -B- ORBITALS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF MOLECULAR ORBITALS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"NUMBER OF BASIS FUNCTIONS" + r"\s+=\s+" + r"(?P\d+)" + r"\s*", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj2: - logger.debug("matched calcinfo 3") - qcvar["N ALPHA ELECTRONS"] = mobj2.group("occ_nao") - qcvar["N BETA ELECTRONS"] = mobj2.group("occ_nbo") - qcvar["N MOLECULAR ORBITALS"] = mobj2.group("nmo") - qcvar["N BASIS FUNCTIONS"] = mobj2.group("nbf") - - # Process MP2 - mobj = re.search( - # fmt: off - r"^\s+" + r"MP2 CONTROL INFORMATION" + r"\s*" + - r"^\s+" + r"-+" + r"\s*" + - r"^\s+" + r"NACORE" + r"\s+=\s+" + r"\d+" + r"\s+" + r"NBCORE" + r"\s+=\s+" + r"\d+" + r"\s*" + - r"^\s+" + r"LMOMP2" + r"\s+=\s+" + r"\w+" + r"\s+" + r"AOINTS" + r"\s+=\s+" + r"\w+" + r"\s*" + - r"^\s+" + r"METHOD" + r"\s+=\s+" + r"\d+" + r"\s+" + r"NWORD" + r"\s+=\s+" + r"\d+" + r"\s*" + - r"^\s+" + r"MP2PRP" + r"\s+=\s+" + r"\w+" + r"\s+" + r"OSPT" + r"\s+=\s+" + r"\w+"+ r"\s*" + - r"^\s+" + r"CUTOFF" + r"\s+=\s+" + NUMBER + r"\s+" + r"CPHFBS" + r"\s+=\s+" + r"\w+"+ r"\s*" + - r"^\s+" + r"CODE" + r"\s+=\s+" + r"(?P\w+)" + r"\s*", - # Oct 2023: terminating `$` removed from `+ r"\s*$",` to allow it to match gamess 2021 - # and gamess 2023 that has both `CODE` and `SCSPT` on the line. - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 module") - module = mobj.group("code").lower() - - mobj = re.search( - # fmt: off - r'^\s+' + r'RESULTS OF MOLLER-PLESSET 2ND ORDER CORRECTION ARE\n' - r'^\s+' + r'E\(0\)' + r'=\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(1\)' + r'=\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2\)' + r'=\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(MP2\)' + r'=\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 a") - qcvar["MP2 CORRELATION ENERGY"] = mobj.group(3) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(4) - mobj3 = re.search(r"\s+[RU]HF\s*SCF\s*CALCULATION", outtext, re.MULTILINE) - if mobj3: - qcvar[f"MP2 DOUBLES ENERGY"] = mobj.group(3) - - mobj = re.search( - # fmt: off - r'^\s+' + r'RESULTS OF 2ND-ORDER ZAPT CORRECTION' + r'\s*' + - r'^\s+' + r'E\(HF\) ' + r'=\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(ZAPT\) ' + r'=\s+' + NUMBER + r'\s*' + - r'^\s+' + r'-----------------------------------' + r'\s*' + - r'^\s+' + r'E\(MP2\) ' + r'=\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 b") - qcvar["ZAPT2 CORRELATION ENERGY"] = mobj.group(2) - qcvar["ZAPT2 TOTAL ENERGY"] = mobj.group(3) - - mobj = re.search( - # fmt: off - r'^\s+' + r'RESULTS OF MOLLER-PLESSET 2ND ORDER CORRECTION ARE\n' - r'^\s+' + r'E\(0\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(1\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(MP2\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'SPIN-COMPONENT-SCALED MP2 RESULTS ARE' + r'\s*' + - r'^\s+' + r'E\(2S\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2T\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2ST\)=' + r'\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 rhf c") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["MP2 SINGLES ENERGY"] = mobj.group(2) - qcvar["MP2 DOUBLES ENERGY"] = mobj.group(3) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(4) - qcvar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(5) - qcvar["MP2 SAME-SPIN CORRELATION ENERGY"] = mobj.group(6) - - mobj = re.search( - # fmt: off - r'^\s+' + r'SINGLE EXCITATION CONTRIBUTION' + r'\s*' + - r'^\s+' + r'ALPHA' + r'\s*' + NUMBER + r'\s*' + - r'^\s+' + r'BETA' + r'\s*' + NUMBER + r'\s*' + - r'^\s+' + r'DOUBLE EXCITATION CONTRIBUTION' + r'\s*' + - r'^\s+' + NUMBER + r'\s*' + - r'^\s+' + - r'^\s+' + r'RESULTS OF MOLLER-PLESSET 2ND ORDER CORRECTION ARE' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 rohf d") - qcvar["MP2 SINGLES ENERGY"] = Decimal(mobj.group(1)) + Decimal(mobj.group(2)) - qcvar["MP2 DOUBLES ENERGY"] = mobj.group(3) - - mobj = re.search(r"^\s+" + "UHF-MP2 CALCULATION", outtext, re.MULTILINE) - if mobj: - logger.debug("matched mp2 uhf e") - qcvar["MP2 SINGLES ENERGY"] = "0.0" - - mobj = re.search( - # fmt: off - r"^\s+" + r"E\(SCF\)" + r"\s*=\s+" + NUMBER + r"\s*" + - r"^\s+" + r"ZAPT E\(2\)" + r"\s*=\s+" + NUMBER + r"\s*" + - r"^\s+" + r"E\(MP2\)" + r"\s*=\s+" + NUMBER + r"\s*", - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 rohf f") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["MP2 CORRELATION ENERGY"] = mobj.group(2) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(3) - - mobj = re.search( - # fmt: off - r'^\s+' + r'RESULTS OF MOLLER-PLESSET 2ND ORDER CORRECTION ARE\n' - r'^\s+' + r'E\(SCF\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(1\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(MP2\)=' + r'\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 g llel") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["MP2 SINGLES ENERGY"] = mobj.group(2) - qcvar["MP2 DOUBLES ENERGY"] = mobj.group(3) - qcvar["MP2 CORRELATION ENERGY"] = mobj.group(3) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(4) - - mobj = re.search( - # fmt: off - r'^\s+' + r'RESULTS OF MOLLER-PLESSET 2ND ORDER CORRECTION ARE\n' - r'^\s+' + r'E\(SCF\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(MP2\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'SPIN-COMPONENT-SCALED MP2 RESULTS ARE' + r'\s*' + - r'^\s+' + r'E\(2S\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2T\)=' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'E\(2ST\)=' + r'\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched mp2 rhf h llel") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["MP2 SINGLES ENERGY"] = "0.0" - qcvar["MP2 DOUBLES ENERGY"] = mobj.group(2) - qcvar["MP2 CORRELATION ENERGY"] = mobj.group(2) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(3) - qcvar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(4) - qcvar["MP2 SAME-SPIN CORRELATION ENERGY"] = mobj.group(5) - - # Process CCSD - mobj = re.search( - # fmt: off - r'^\s+' + r'SUMMARY OF RESULTS' + r'\s+' + r'\n' + - r'^\s+' + r'REFERENCE ENERGY:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'MBPT\(2\) ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r"(?PCCSD|LCCD|CCD)" + r"\s+" r'ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched rhf ccsd/lccd/ccd") - mtd = mobj.group("mtd") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["SCF TOTAL ENERGY"] = mobj.group(1) - qcvar["MP2 CORRELATION ENERGY"] = mobj.group(3) - qcvar["MP2 DOUBLES ENERGY"] = mobj.group(3) - qcvar["MP2 TOTAL ENERGY"] = mobj.group(2) - qcvar[f"{mtd} DOUBLES ENERGY"] = mobj.group(6) - qcvar[f"{mtd} CORRELATION ENERGY"] = mobj.group(6) - qcvar[f"{mtd} TOTAL ENERGY"] = mobj.group(5) - - mobj = re.search( - # fmt: off - r'^\s+' + r'SUMMARY OF CCSD RESULTS' + r'\s+' + r'\n' + - r'^\s+' + r'REFERENCE ENERGY:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CCSD ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR. E=\s+' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched rohf ccsd") - qcvar["SCF TOTAL ENERGY"] = mobj.group(1) - qcvar["CCSD CORRELATION ENERGY"] = mobj.group(3) - qcvar["CCSD TOTAL ENERGY"] = mobj.group(2) - - # Process CR-CC(2,3) - mobj = re.search( - # fmt: off - r'^\s+' + r'CCSD ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CR-CC\(2,3\),A OR CCSD\(2\)_T ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CR-CC\(2,3\) OR CR-CCSD\(T\)_L ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched cc-cr(2,3)") - qcvar["CCSD TOTAL ENERGY"] = mobj.group(1) - qcvar["CCSD CORRELATION ENERGY"] = mobj.group(2) - qcvar["CR-CC(2,3),A TOTAL ENERGY"] = mobj.group(3) - qcvar["CR-CC(2,3),A CORRELATION ENERGY"] = mobj.group(4) - qcvar["CR-CC(2,3) TOTAL ENERGY"] = mobj.group(5) - qcvar["CR-CC(2,3) CORRELATION ENERGY"] = mobj.group(6) - - # Process CCSD(T) - mobj = re.search( - # fmt: off - r'^\s+' + r'SUMMARY OF RESULTS' + r'\s+' + r'\n' + - r'^\s+' + r'REFERENCE ENERGY:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'MBPT\(2\) ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CCSD ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CCSD\[T\] ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CCSD\(T\) ENERGY:' + r'\s+' + NUMBER + r'\s*' + r'CORR.E=\s+' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched ccsd(t)") - qcvar["HF TOTAL ENERGY"] = mobj.group(1) - qcvar["SCF TOTAL ENERGY"] = mobj.group(1) - qcvar["CCSD CORRELATION ENERGY"] = mobj.group(5) - qcvar["CCSD TOTAL ENERGY"] = mobj.group(4) - qcvar["(T) CORRECTION ENERGY"] = Decimal(mobj.group(8)) - Decimal(mobj.group(4)) - # qcvar['CCSD(T) CORRELATION ENERGY'] = Decimal(mobj.group(5)) - qcvar['SCF TOTAL ENERGY'] - qcvar["CCSD+T(CCSD) CORRELATION ENERGY"] = mobj.group(7) - qcvar["CCSD+T(CCSD) TOTAL ENERGY"] = mobj.group(6) - qcvar["CCSD(T) CORRELATION ENERGY"] = mobj.group(9) - qcvar["CCSD(T) TOTAL ENERGY"] = mobj.group(8) - - # Process CISD - mobj = re.search( - # fmt: off - r"^\s+" + r"PROPERTY VALUES FOR THE " + r"(RHF|ROHF)" + r"\s+" + r"SELF-CONSISTENT FIELD WAVEFUNCTION" + r"\s*" + - r"(?:.*?)" + - r"^\s+" + r"ENERGY COMPONENTS" + r"\s*" + - r"(?:.*?)" + - r"^\s*" + r"(TOTAL ENERGY =)\s+" + r"(?P" + NUMBER + r")" + r"\s*" + - r"(?:.*?)" + - r"^\s+" + r"(?P(FSOCI|GUGA))" + r"\s+" + r"CI PROPERTIES...FOR THE WAVEFUNCTION OF STATE 1" + r"\s*" + - r"(?:.*?)" + - r"^\s+" + r"ENERGY COMPONENTS" + r"\s*" + - r"(?:.*?)" + - r"^\s*" + r"(TOTAL ENERGY =)\s+" + r"(?P" + NUMBER + r")" + r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched cisd fsoci/guga", mobj.groupdict()) - module = mobj.group("module").lower() - qcvar["CISD CORRELATION ENERGY"] = Decimal(mobj.group("ci")) - Decimal(mobj.group("hf")) - qcvar["CISD TOTAL ENERGY"] = mobj.group("ci") - qcvar["CI TOTAL ENERGY"] = mobj.group("ci") - - # Process FCI - mobj = re.search( - # fmt: off - r'^\s+' + r'ALDET CI PROPERTIES...FOR THE WAVEFUNCTION OF STATE' + r'\s+' + r'(?P\d+)' + r'\s*' + - r'^\s+' + r'USING THE EXPECTATION VALUE DENSITY' + r'\s*' + - r'(?:.*?)' + - r'^\s+' + r'TOTAL ENERGY =' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched fci") - qcvar["FCI TOTAL ENERGY"] = mobj.group(2) - qcvar["CI TOTAL ENERGY"] = mobj.group(2) - - # Process EOM-CCSD - mobj = re.search( - # fmt: off - r'^\s+' + r'---- SUMMARY OF EOM-CCSD CALCULATIONS ----' + - r'\s+' + r'\n' + - r'^\s+' + r'EXCITATION EXCITATION TOTAL' + r'\s*' + - r'^\s+' + r'SYMMETRY ENERGY \(H\) ENERGY \(EV\) ENERGY \(H\) ITERATIONS' + r'\s*' + - r'^\s+' + r'A' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + r'CONVERGED\s+' + - r'^\s+' + r'A' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + r'CONVERGED\s+' + - r'^\s+' + r'A' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + r'CONVERGED\s+' + - r'^\s+' + r'A' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + r'CONVERGED\s+', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched eom-ccsd") - qcvar["EOM-CCSD ROOT 1 EXCITATION ENERGY"] = mobj.group(1) - qcvar["EOM-CCSD ROOT 2 EXCITATION ENERGY"] = mobj.group(4) - qcvar["EOM-CCSD ROOT 3 EXCITATION ENERGY"] = mobj.group(7) - qcvar["EOM-CCSD ROOT 4 EXCITATION ENERGY"] = mobj.group(10) - qcvar["EOM-CCSD ROOT 1 TOTAL ENERGY"] = mobj.group(3) - qcvar["EOM-CCSD ROOT 2 TOTAL ENERGY"] = mobj.group(6) - qcvar["EOM-CCSD ROOT 3 TOTAL ENERGY"] = mobj.group(9) - qcvar["EOM-CCSD ROOT 4 TOTAL ENERGY"] = mobj.group(12) - - # Process DFT - # mobj = re.search( - # r'^\s+' + r'DFT EXCHANGE + CORRELATION ENERGY' + r'=\s+' + NUMBER + r'\s*$' - # ,outtext, re.MULTILINE) - mobj = re.search( - r"^\s+" + r"DFT EXCHANGE \+ CORRELATION ENERGY" + r"\s+=\s*" + NUMBER + r"\s*$", outtext, re.MULTILINE - ) - if mobj: - logger.debug("matched dft xc") - qcvar["DFT XC ENERGY"] = mobj.group(1) - - # Process Geometry - mobj = re.search( - # fmt: off - r'^\s+' + r'ATOM ATOMIC COORDINATES \(BOHR\)' + r'\s*' + - r'^\s+' + r'CHARGE X Y Z'+ r'\s*' + - r'((?:\s+([A-Za-z]\w*)\s+\d+\.\d+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s*\n)+)' + - r"\s*$", - # fmt: on - outtext, - re.MULTILINE | re.IGNORECASE, - ) - if mobj: - logger.debug("matched geom") - molxyz = "%d bohr\n\n" % len(mobj.group(1).splitlines()) - for line in mobj.group(1).splitlines(): - lline = line.split() - chg_on_center = int(float(lline[-4])) - if chg_on_center > 0: - tag = f"{lline[-5]}" - else: - tag = f"@{lline[-5].strip()}" - molxyz += "%s %16s %16s %16s\n" % (tag, lline[-3], lline[-2], lline[-1]) - qcvar_coord = Molecule( - validate=False, - **qcel.molparse.to_schema( - qcel.molparse.from_string(molxyz, dtype="xyz+", fix_com=True, fix_orientation=True)["qm"], dtype=2 - ), - ) - - # Process Gradient - mobj = re.search( - # fmt: off - r'^\s+' + r'GRADIENT OF THE ENERGY' + r'\s*'+ - r'^\s+' + r'----------------------' + r'\s*'+ - r'\s+' + r'\n'+ - r'^\s+' + r'UNITS ARE HARTREE/BOHR E\'X E\'Y E\'Z' + r'\s*' + - r'((?:\s+([1-9][0-9]*)+\s+([A-Za-z]\w*)+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s+[-+]?\d+\.\d+\s*\n)+)' + - r'\s*$', - # fmt: off - outtext, re.MULTILINE - ) - if mobj: - logger.debug("matched gradient - after") - atoms = [] - qcvar_grad = [] - for line in mobj.group(1).splitlines(): - lline = line.split() - if lline == []: - pass - else: - logger.debug("printing gradient") - atoms.append(lline[1]) - qcvar_grad.append([float(lline[-3]), float(lline[-2]), float(lline[-1])]) - qcvar_grad = np.array(qcvar_grad).reshape((-1, 3)) - - # Process SCF blocks - mobj = re.search( - # fmt: off - r'^\s+' + r'PROPERTIES FOR THE .* DFT FUNCTIONAL .* DENSITY MATRIX' + - r'(.*)' + - r'\.\.\.\.\.\. END OF PROPERTY EVALUATION \.\.\.\.\.\.', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched dft props") - prop_block = mobj.group(1) - mobj_energy = re.search( - # fmt: off - r'\s+ENERGY COMPONENTS\s*\n' + - r'\s+-----------------\s*\n' + - r'\s*\n' + - r'\s+WAVEFUNCTION NORMALIZATION =.*\n' + - r'\s*\n' + - r'\s+ONE ELECTRON ENERGY =\s+' + NUMBER + r'\n' + - r'\s+TWO ELECTRON ENERGY =\s+' + NUMBER + r'\n' + - r'\s+NUCLEAR REPULSION ENERGY =\s+' + NUMBER + r'\n' + - r'\s+------------------\s*\n' + - r'\s+TOTAL ENERGY =\s+' + NUMBER+r'\n', - # fmt: on - prop_block - ) - if mobj_energy: - qcvar["ONE-ELECTRON ENERGY"] = mobj_energy.group(1) - qcvar["TWO-ELECTRON ENERGY"] = mobj_energy.group(2) - qcvar["SCF TOTAL ENERGY"] = mobj_energy.group(4) - qcvar["DFT TOTAL ENERGY"] = mobj_energy.group(4) - - mobj_dipole = re.search( - # fmt: off - r'\s+ELECTROSTATIC MOMENTS\s*\n' - r'\s+---------------------\s*\n' - r'\s*\n' - r'\s*POINT\s+1\s+X\s+Y\s+Z\s+\(BOHR\)\s+CHARGE\s*\n' - r'.*\n' - r'\s*DX\s+DY\s+DZ\s+/D/\s+\(DEBYE\)\s*\n' - r'\s*' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*\n', - # fmt: on - prop_block - ) - if mobj_dipole: - d2au = Decimal(qcel.constants.conversion_factor("debye", "e * bohr")) - qcvar["SCF DIPOLE"] = d2au * np.array( - [Decimal(mobj_dipole.group(1)), Decimal(mobj_dipole.group(2)), Decimal(mobj_dipole.group(3))] - ) - - # Process CURRENT Energies - if "HF TOTAL ENERGY" in qcvar: - qcvar["CURRENT REFERENCE ENERGY"] = qcvar["HF TOTAL ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["HF TOTAL ENERGY"] - - if "MP2 TOTAL ENERGY" in qcvar and "MP2 CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["MP2 CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["MP2 TOTAL ENERGY"] - - if "ZAPT2 TOTAL ENERGY" in qcvar and "ZAPT2 CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["ZAPT2 CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["ZAPT2 TOTAL ENERGY"] - - if "CISD TOTAL ENERGY" in qcvar and "CISD CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CISD CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CISD TOTAL ENERGY"] - - if "LCCD TOTAL ENERGY" in qcvar and "LCCD CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["LCCD CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["LCCD TOTAL ENERGY"] - - if "CCD TOTAL ENERGY" in qcvar and "CCD CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CCD CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CCD TOTAL ENERGY"] - - if "CCSD TOTAL ENERGY" in qcvar and "CCSD CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CCSD CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CCSD TOTAL ENERGY"] - - if "CR-CC(2,3) TOTAL ENERGY" in qcvar and "CR-CC(2,3) CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CR-CC(2,3) CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CR-CC(2,3) TOTAL ENERGY"] - - if "CCSD+T(CCSD) TOTAL ENERGY" in qcvar and "CCSD+T(CCSD) CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CCSD+T(CCSD) CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CCSD+T(CCSD) TOTAL ENERGY"] - - if "CCSD(T) TOTAL ENERGY" in qcvar and "CCSD(T) CORRELATION ENERGY" in qcvar: - qcvar["CURRENT CORRELATION ENERGY"] = qcvar["CCSD(T) CORRELATION ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["CCSD(T) TOTAL ENERGY"] - - if "DFT TOTAL ENERGY" in qcvar: - qcvar["CURRENT REFERENCE ENERGY"] = qcvar["DFT TOTAL ENERGY"] - qcvar["CURRENT ENERGY"] = qcvar["DFT TOTAL ENERGY"] - qcvar.pop("HF TOTAL ENERGY") - - if "FCI TOTAL ENERGY" in qcvar: # and 'FCI CORRELATION ENERGY' in qcvar: - qcvar["CURRENT ENERGY"] = qcvar["FCI TOTAL ENERGY"] - - qcvar[f"N ATOMS"] = len(qcvar_coord.symbols) - - return qcvar, qcvar_coord, qcvar_grad, module diff --git a/qcengine/programs/gamess/keywords.py b/qcengine/programs/gamess/keywords.py deleted file mode 100644 index eb127617c..000000000 --- a/qcengine/programs/gamess/keywords.py +++ /dev/null @@ -1,44 +0,0 @@ -import collections -import textwrap -from typing import Any, Dict, Tuple - - -def format_keyword(keyword: str, val: Any, lop_off: bool = True) -> Tuple[str, str]: - """Reformat value `val` for `keyword` from python into GAMESS-speak.""" - - text = "" - - # Transform booleans into Fortran booleans - if str(val) == "True": - text += ".true." - elif str(val) == "False": - text += ".false." - - # No Transform - else: - text += str(val).lower() - - if lop_off: - return keyword[7:].lower(), text - else: - return keyword.lower(), text - - -def format_keywords(keywords: Dict[str, Any]) -> str: - """From GAMESS-directed, non-default `keywords` dictionary, write a GAMESS deck.""" - - grouped_options = collections.defaultdict(dict) - for group_key, val in keywords.items(): - group, key = group_key.split("__") - grouped_options[group.lower()][key.lower()] = val - - grouped_lines = {} - for group, opts in sorted(grouped_options.items()): - line = [] - line.append(f"${group.lower()}") - for key, val in sorted(grouped_options[group].items()): - line.append("=".join(format_keyword(key, val, lop_off=False))) - line.append("$end\n") - grouped_lines[group] = textwrap.fill(" ".join(line), initial_indent=" ", subsequent_indent=" ") - - return "\n".join(grouped_lines.values()) + "\n" diff --git a/qcengine/programs/gamess/runner.py b/qcengine/programs/gamess/runner.py deleted file mode 100644 index b456dbc4d..000000000 --- a/qcengine/programs/gamess/runner.py +++ /dev/null @@ -1,303 +0,0 @@ -"""Compute quantum chemistry using Iowa State's GAMESS executable.""" - -import copy -import pprint -from decimal import Decimal -from typing import Any, Dict, Optional, Tuple - -from qcelemental.models import AtomicInput, AtomicResult, BasisSet, Provenance -from qcelemental.util import safe_version, which - -from ...exceptions import InputError, UnknownError -from ...util import execute -from ..model import ProgramHarness -from ..qcvar_identities_resources import build_atomicproperties, build_out -from ..util import error_stamp -from .germinate import muster_modelchem -from .harvester import harvest -from .keywords import format_keywords - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) - - -class GAMESSHarness(ProgramHarness): - """ - - Notes - ----- - Required edits to the ``rungms`` script are as follows:: - set SCR=./ # will be managed by QCEngine instead - set USERSCR=./ # ditto - set GMSPATH=/home/psilocaluser/gits/gamess # full path to installation - - """ - - _defaults = { - "name": "GAMESS", - "scratch": True, - "thread_safe": False, - "thread_parallel": True, - "node_parallel": True, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "rungms", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via https://www.msg.chem.iastate.edu/GAMESS/GAMESS.html", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("rungms") - if which_prog not in self.version_cache: - success, output = execute([which_prog, "v.inp"], {"v.inp": ""}) - - if success: - for line in output["stdout"].splitlines(): - if "GAMESS VERSION" in line: - branch = " ".join(line.strip(" *\t").split()[3:]) - self.version_cache[which_prog] = safe_version(branch) - - return self.version_cache[which_prog] - - def compute(self, input_model: AtomicInput, config: "TaskConfig") -> AtomicResult: - self.found(raise_error=True) - - job_inputs = self.build_input(input_model, config) - success, dexe = self.execute(job_inputs) - - if "INPUT HAS AT LEAST ONE SPELLING OR LOGIC MISTAKE" in dexe["stdout"]: - raise InputError(error_stamp(job_inputs["infiles"]["gamess.inp"], dexe["stdout"], dexe["stderr"])) - - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - dexe["outfiles"]["input"] = job_inputs["infiles"]["gamess.inp"] - return self.parse_output(dexe["outfiles"], input_model) - - def build_input( - self, input_model: AtomicInput, config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - gamessrec = { - "infiles": {}, - "scratch_directory": config.scratch_directory, - "scratch_messy": config.scratch_messy, - } - - opts = copy.deepcopy(input_model.keywords) - - # Handle molecule - if not all(input_model.molecule.real): - raise InputError("GAMESS+QCEngine can't handle ghost atoms yet.") - - molcmd, moldata = input_model.molecule.to_string(dtype="gamess", units="Bohr", return_data=True) - opts.update(moldata["keywords"]) - - # Handle calc type and quantum chemical method - opts.update(muster_modelchem(input_model.model.method, input_model.driver.derivative_int())) - - # Handle basis set - if isinstance(input_model.model.basis, BasisSet): - raise InputError("QCSchema BasisSet for model.basis not implemented. Use string basis name.") - if input_model.model.basis is None: - raise InputError("None for model.basis is not useable.") - - # * for gamess, usually insufficient b/c either ngauss or ispher needed - opts["basis__gbasis"] = input_model.model.basis - - # Handle memory - # * [GiB] --> [M QW] - # * docs on mwords: "This is given in units of 1,000,000 words (as opposed to 1024*1024 words)" - # * docs: "the memory required on each processor core for a run using p cores is therefore MEMDDI/p + MWORDS." - # * int() rounds down - mwords_total = int(config.memory * (1024**3) / 8e6) - - for mem_frac_replicated in (1, 0.5, 0.1, 0.75): - mwords, memddi = self._partition(mwords_total, mem_frac_replicated, config.ncores) - # DEBUG print(f"loop {mwords_total=} {mem_frac_replicated=} {config.ncores=} -> repl: {mwords=} dist: {memddi=} -> percore={memddi/config.ncores + mwords} tot={memddi + config.ncores * mwords}\n") - trial_opts = copy.deepcopy(opts) - trial_opts["contrl__exetyp"] = "check" - trial_opts["system__parall"] = not (config.ncores == 1) - trial_opts["system__mwords"] = mwords - trial_opts["system__memddi"] = memddi - trial_inp = format_keywords(trial_opts) + molcmd - trial_gamessrec = { - "infiles": {"trial_gamess.inp": trial_inp}, - "command": [which("rungms"), "trial_gamess"], - "scratch_messy": False, - "scratch_directory": config.scratch_directory, - } - success, dexe = self.execute(trial_gamessrec) - - # TODO: switch to KnownError and better handle clobbering of user ncores - # when the "need serial exe" messages show up compared to mem messages isn't clear - # this would be a lot cleaner if there was a unique or list of memory error strings - # if ( - # ("ERROR: ONLY CCTYP=CCSD OR CCTYP=CCSD(T) CAN RUN IN PARALLEL." in dexe["stdout"]) - # or ("ERROR: ROHF'S CCTYP MUST BE CCSD OR CR-CCL, WITH SERIAL EXECUTION" in dexe["stdout"]) - # or ("CI PROGRAM CITYP=FSOCI DOES NOT RUN IN PARALLEL." in dexe["stdout"]) - # ): - # print("RESTETTITNG TO 1") - # config.ncores = 1 - # break - if "INPUT HAS AT LEAST ONE SPELLING OR LOGIC MISTAKE" in dexe["stdout"]: - raise InputError(error_stamp(trial_inp, dexe["stdout"], dexe["stderr"])) - elif "EXECUTION OF GAMESS TERMINATED -ABNORMALLY-" in dexe["stdout"]: - pass - else: - opts["system__mwords"] = mwords - opts["system__memddi"] = memddi - break - - # TODO: "semget errno=ENOSPC -- check system limit for sysv semaphores." `ipcs -l`, can fix if too many gamess tests run at once by config.ncores = 4 - - # ERROR: ROHF'S CCTYP MUST BE CCSD OR CR-CCL, WITH SERIAL EXECUTION - if opts.get("contrl__cctyp", "").lower() == "ccsd" and opts.get("contrl__scftyp", "").lower() == "rohf": - config.ncores = 1 - - # Handle conversion from schema (flat key/value) keywords into local format - optcmd = format_keywords(opts) - - gamessrec["infiles"]["gamess.inp"] = optcmd + molcmd - gamessrec["command"] = [ - which("rungms"), - "gamess", - "00", - str(config.ncores), - ] # rungms JOB VERNO NCPUS >& JOB.log & - - return gamessrec - - # Note decr MEMORY=100000 to get - # ***** ERROR: MEMORY REQUEST EXCEEDS AVAILABLE MEMORY - # to test gms fail - - # $CONTRL SCFTYP=ROHF MULT=3 RUNTYP=GRADIENT COORD=CART $END - # $SYSTEM TIMLIM=1 MEMORY=800000 $END - # $SCF DIRSCF=.TRUE. $END - # $BASIS GBASIS=STO NGAUSS=2 $END - # $GUESS GUESS=HUCKEL $END - # $DATA - # Methylene...3-B-1 state...ROHF/STO-2G - # Cnv 2 - # - # Hydrogen 1.0 0.82884 0.7079 0.0 - # Carbon 6.0 - # Hydrogen 1.0 -0.82884 0.7079 0.0 - # $END - - def execute(self, inputs, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None): - - success, dexe = execute( - inputs["command"], - inputs["infiles"], - ["gamess.dat"], - scratch_messy=inputs["scratch_messy"], - scratch_directory=inputs["scratch_directory"], - ) - return success, dexe - - def parse_output(self, outfiles: Dict[str, str], input_model: AtomicInput) -> AtomicResult: - - # Get the stdout from the calculation (required) - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - method = input_model.model.method.lower() - method = method[4:] if method.startswith("gms-") else method - - # gamessmol, if it exists, is dinky, just a clue to geometry of gamess results - try: - # July 2021: gamessmol & vector returns now atin/outfile orientation depending on fix_com,orientation=T/F. previously always outfile orientation - qcvars, gamesshess, gamessgrad, gamessmol, module = harvest( - input_model.molecule, method, stdout, **outfiles - ) - # TODO: "EXECUTION OF GAMESS TERMINATED -ABNORMALLY-" in dexe["stdout"]: - - except Exception: - raise UnknownError(error_stamp(outfiles["input"], stdout, stderr)) - - try: - if gamessgrad is not None: - qcvars[f"{method.upper()} TOTAL GRADIENT"] = gamessgrad - qcvars["CURRENT GRADIENT"] = gamessgrad - - if gamesshess is not None: - qcvars[f"{method.upper()} TOTAL HESSIAN"] = gamesshess - qcvars["CURRENT HESSIAN"] = gamesshess - - if input_model.driver.upper() == "PROPERTIES": - retres = qcvars[f"CURRENT ENERGY"] - else: - retres = qcvars[f"CURRENT {input_model.driver.upper()}"] - except KeyError: - if "EXETYP=CHECK" in stdout and "EXECUTION OF GAMESS TERMINATED NORMALLY" in stdout: - # check run that completed normally - # * on one hand, it's still an error return_result-wise - # * but on the other hand, often the reason for the job is to get gamessmol, so let it return success=T below - retres = 0.0 - else: - raise UnknownError(error_stamp(outfiles["input"], stdout, stderr)) - - build_out(qcvars) - atprop = build_atomicproperties(qcvars) - - provenance = Provenance(creator="GAMESS", version=self.get_version(), routine="rungms").dict() - if module is not None: - provenance["module"] = module - - output_data = { - "schema_version": 1, - "molecule": gamessmol, # overwrites with outfile Cartesians in case fix_*=F - "extras": {**input_model.extras}, - "native_files": {k: v for k, v in outfiles.items() if v is not None}, - "properties": atprop, - "provenance": provenance, - "return_result": retres, - "stderr": stderr, - "stdout": stdout, - "success": True, - } - - # got to even out who needs plump/flat/Decimal/float/ndarray/list - # * formerly unnp(qcvars, flat=True).items() - output_data["extras"]["qcvars"] = { - k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in qcvars.items() - } - - return AtomicResult(**{**input_model.dict(), **output_data}) - - @staticmethod - def _partition(total: float, fraction_replicated: float, ncores: int) -> Tuple[int, int]: - """Compute memory keyword values from memory and core parameters. - - Parameters - ---------- - total - Total memory per node in mwords. - fraction_replicated - Portion on interval (0, 1] to be replicated memory, as opposed to distributed memory. - ncores - Number of cores needing replicated memory. - - Returns - ------- - mwords, memddi - Return mwords and memddi values for ``total`` memory (already in mwords) partitioned so (0, 1] - ``fraction_replicated`` is replicated over ``ncores`` and remainder distributed. - - """ - replicated_summed = total * fraction_replicated - replicated_each = int(max(1, replicated_summed / ncores)) - distributed = int(total - replicated_summed) - - return replicated_each, distributed diff --git a/qcengine/programs/gcp.py b/qcengine/programs/gcp.py deleted file mode 100644 index 368f82521..000000000 --- a/qcengine/programs/gcp.py +++ /dev/null @@ -1,316 +0,0 @@ -"""Compute geometrical counterpoise correction using Kruse & Grimme's GCP executable.""" - -import os -import pathlib -import pprint -import re -import socket -import sys -from decimal import Decimal -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple - -import numpy as np -import qcelemental as qcel -from qcelemental.models import AtomicResult, FailedOperation, Provenance -from qcelemental.util import safe_version, which - -from ..exceptions import InputError, UnknownError -from ..util import execute -from .model import ProgramHarness - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput - - from ..config import TaskConfig - - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) - - -class GCPHarness(ProgramHarness): - - _defaults = { - "name": "GCP", - "scratch": True, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "gcp", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install gcp -c psi4`.", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - version = None - which_prog = which("gcp") - if which_prog not in self.version_cache: - # option not (yet) available, instead find in help output - command = [which_prog, "-version"] - import subprocess - - proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for ln in proc.stdout.decode("utf-8").splitlines(): - if re.match(".*Version", ln): - version = ln.split()[2] - - if version is None: - raise UnknownError(f"Could not identify GCP version") - # pending upcoming gcp update - # self.version_cache[which_prog] = safe_version( - # proc.stdout.decode("utf-8").strip()) - self.version_cache[which_prog] = safe_version(version) - - return self.version_cache[which_prog] - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - self.found(raise_error=True) - - job_inputs = self.build_input(input_model, config) - - success, dexe = self.execute(job_inputs) - - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - dexe["outfiles"]["input"] = " ".join(job_inputs["command"]) # stretch to call this input - dexe["outfiles"]["gcp_geometry.xyz"] = job_inputs["infiles"]["gcp_geometry.xyz"] - output_model = self.parse_output(dexe["outfiles"], input_model) - - else: - output_model = FailedOperation( - success=False, - error={"error_type": "execution_error", "error_message": dexe["stderr"]}, - input_data=input_model.dict(), - ) - - return output_model - - def execute( - self, inputs: Dict[str, Any], *, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None - ) -> Tuple[bool, Dict]: - - success, dexe = execute( - inputs["command"], - inputs["infiles"], - inputs["outfiles"], - scratch_messy=inputs["scratch_messy"], - scratch_directory=inputs["scratch_directory"], - blocking_files=inputs["blocking_files"], - ) - return success, dexe - - def build_input( - self, input_model: "AtomicInput", config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - - if (input_model.driver.derivative_int() > 1) or (input_model.driver == "properties"): - raise InputError(f"Driver {input_model.driver} not implemented for GCP.") - - # live somewhere else? - available_levels = [ - "HF/MINIS", - "DFT/MINIS", - "HF/MINIX", - "DFT/MINIX", - "HF/SV", - "DFT/SV", - "HF/def2-SV(P)", - "DFT/def2-SV(P)", - "HF/def2-SVP", - "DFT/def2-SVP", - "HF/DZP", - "DFT/DZP", - "HF/def-TZVP", - "DFT/def-TZVP", - "HF/def2-TZVP", - "DFT/def2-TZVP", - "HF/631Gd", - "DFT/631Gd", - "HF/cc-pVDZ", - "DFT/cc-pVDZ", - "HF/aug-cc-pVDZ", - "DFT/aug-cc-pVDZ", - # DFT only - "DFT/SVX", # = def2-SV(P/h,c) - "DFT/LANL", - "DFT/pobTZVP", - # HF only, needs update - # 'HF/631G', - # functional specific - "TPSS/def2-SVP", - "PW6B95/def2-SVP", - # 3c specials - "hf3c", - "pbeh3c", - # custom - # will need gcp code changes if $HOME is to be avoided - # thus code blocks with FILE below are not used yet. - # 'file', - ] - # some methods not available in legacy version - mctc_gcp_levels = ["B973C", "R2SCAN3C"] - - executable = self._defaults["name"].lower() - if executable == "mctc-gcp": - available_levels.extend(mctc_gcp_levels) - - available_levels = [f.upper() for f in available_levels] - # temp until actual options object - method = input_model.model.method.upper() - if method not in available_levels: - if method in mctc_gcp_levels and executable == "gcp": - raise InputError(f"GCP does not have method {method} but MCTC-GCP does.") - else: - raise InputError(f"GCP does not have method: {method}") - - # Need 'real' field later and that's only guaranteed for molrec - molrec = qcel.molparse.from_schema(input_model.molecule.dict()) - - calldash = {"gcp": "-", "mctc-gcp": "--"}[executable] - - command = [executable, "gcp_geometry.xyz", calldash + "level", method] - - if input_model.driver == "gradient": - command.append(calldash + "grad") - - infiles = { - "gcp_geometry.xyz": qcel.molparse.to_string(molrec, dtype="xyz", units="Angstrom", ghost_format=""), - } - if method == "FILE": - infiles[".gcppar"] = input_model.extras["parameters"] - - return { - "command": command, - "infiles": infiles, - "outfiles": ["gcp_gradient"], - "scratch_messy": config.scratch_messy, - "scratch_directory": config.scratch_directory, - "input_result": input_model.copy(deep=True), - "blocking_files": [os.path.join(pathlib.Path.home(), ".gcppar." + socket.gethostname())], - } - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) - real = np.array(input_model.molecule.real) - full_nat = real.shape[0] - real_nat = np.sum(real) - - for ln in stdout.splitlines(): - if re.match(" Egcp:", ln): - ene = Decimal(ln.split()[1]) - elif re.match(" normal termination of gCP", ln): - break - else: - if self._defaults["name"] == "GCP" and not ((real_nat == 1) and (input_model.driver == "gradient")): - raise UnknownError( - f"Unsuccessful run. Check input, particularly geometry in [a0]. Model: {input_model.model}" - ) - - # parse gradient output - if outfiles["gcp_gradient"] is not None: - srealgrad = outfiles["gcp_gradient"].replace("D", "E") - realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) - elif real_nat == 1: - realgrad = np.zeros((1, 3)) - - if input_model.driver == "gradient": - ireal = np.argwhere(real).reshape((-1)) - fullgrad = np.zeros((full_nat, 3)) - try: - fullgrad[ireal, :] = realgrad - except NameError as exc: - raise UnknownError("Unsuccessful gradient collection.") from exc - - qcvkey = input_model.model.method.upper() - - calcinfo = [] - - calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) - calcinfo.append(qcel.Datum("GCP CORRECTION ENERGY", "Eh", ene)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} GCP CORRECTION ENERGY", "Eh", ene)) - - if input_model.driver == "gradient": - calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("GCP CORRECTION GRADIENT", "Eh/a0", fullgrad)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} GCP CORRECTION GRADIENT", "Eh/a0", fullgrad)) - - calcinfo = {info.label: info.data for info in calcinfo} - - # Decimal --> str preserves precision - calcinfo = {k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in calcinfo.items()} - - retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] - if isinstance(retres, Decimal): - retres = float(retres) - elif isinstance(retres, np.ndarray): - retres = retres.ravel().tolist() - - output_data = { - "extras": input_model.extras, - "native_files": {k: v for k, v in outfiles.items() if v is not None}, - "properties": {}, - "provenance": Provenance( - creator="GCP", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name - ), - "return_result": retres, - "stderr": stderr, - "stdout": stdout, - } - - output_data["extras"]["qcvars"] = calcinfo - - output_data["success"] = True - return AtomicResult(**{**input_model.dict(), **output_data}) - - -class MCTCGCPHarness(GCPHarness): - - _defaults = { - "name": "MCTC-GCP", - "scratch": True, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "mctc-gcp", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install gcp-correction -c conda-forge`.", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("mctc-gcp") - if which_prog not in self.version_cache: - command = [which_prog, "--version"] - import subprocess - - proc = subprocess.run(command, stdout=subprocess.PIPE) - self.version_cache[which_prog] = safe_version(proc.stdout.decode("utf-8").strip().split()[-1]) - - return self.version_cache[which_prog] diff --git a/qcengine/programs/mace.py b/qcengine/programs/mace.py deleted file mode 100644 index 8ef5400df..000000000 --- a/qcengine/programs/mace.py +++ /dev/null @@ -1,139 +0,0 @@ -from typing import TYPE_CHECKING, Dict, Union -from qcelemental.models import AtomicResult, Provenance, FailedOperation -from qcelemental.util import safe_version, which_import -from qcengine.exceptions import InputError -from qcengine.programs.model import ProgramHarness -from qcengine.units import ureg - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput, FailedOperation - from qcengine.config import TaskConfig - - -class MACEHarness(ProgramHarness): - """Can be used to execute a published MACE-OFF23 model or local mace model. - For more info on the MACE-OFF23 models see . - The models can be found at - """ - - _CACHE = {} - - _defaults = { - "name": "MACE", - "scratch": False, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - def found(self, raise_error: bool = False) -> bool: - return which_import( - "mace", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `mamba install pymace -c conda-forge`", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which_import("mace") - if which_prog not in self.version_cache: - import mace - - self.version_cache[which_prog] = safe_version(mace.__version__) - - return self.version_cache[which_prog] - - def load_model(self, name: str): - """Compile and cache the model to make it faster when calling many times in serial""" - model_name = name.lower() - if model_name in self._CACHE: - return self._CACHE[model_name] - - import torch - from e3nn.util import jit - - if model_name in ["small", "medium", "large"]: - from mace.calculators.foundations_models import mace_off - - model = mace_off(model=model_name, return_raw_model=True) - else: - try: - model = torch.load(name, map_location=torch.device("cpu")) - except FileNotFoundError: - raise InputError( - "The mace harness can only run local models or a MACE-OFF23 model (`small`, `medium`, `large`)" - ) - comp_mod = jit.compile(model) - self._CACHE[model_name] = (comp_mod, float(model.r_max), model.atomic_numbers) - return self._CACHE[model_name] - - def compute(self, input_data: "AtomicInput", config: "TaskConfig") -> Union["AtomicResult", "FailedOperation"]: - - self.found(raise_error=True) - - import mace - import numpy as np - import torch - from mace.data import AtomicData - from mace.data.utils import AtomicNumberTable, Configuration - from mace.tools.torch_geometric import DataLoader - - torch.set_default_dtype(torch.float64) - - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - # Failure flag - ret_data = {"success": False} - - # Build model - method = input_data.model.method - - # load the torch model which can be a MACE-OFF23 or local model - model, r_max, atomic_numbers = self.load_model(name=method) - - z_table = AtomicNumberTable([int(z) for z in atomic_numbers]) - atomic_numbers = input_data.molecule.atomic_numbers - pbc = (False, False, False) - # set the cell as None and mace will automatically create a cell big enough to include all atoms - cell = None - - config = Configuration( - atomic_numbers=atomic_numbers, - positions=input_data.molecule.geometry * ureg.conversion_factor("bohr", "angstrom"), - pbc=pbc, - cell=cell, - ) - - data_loader = DataLoader( - dataset=[AtomicData.from_config(config, z_table=z_table, cutoff=r_max)], - batch_size=1, - shuffle=False, - drop_last=False, - ) - input_dict = next(iter(data_loader)).to_dict() - model.to(device) - mace_data = model(input_dict, compute_force=True) - ret_data["properties"] = {"return_energy": mace_data["energy"] * ureg.conversion_factor("eV", "hartree")} - - if input_data.driver == "energy": - ret_data["return_result"] = ret_data["properties"]["return_energy"] - elif input_data.driver == "gradient": - ret_data["return_result"] = ( - np.asarray(-1.0 * mace_data["forces"] * ureg.conversion_factor("eV / angstrom", "hartree / bohr")) - .ravel() - .tolist() - ) - - else: - raise InputError("MACE only supports the energy and gradient driver methods.") - - ret_data["extras"] = input_data.extras.copy() - ret_data["provenance"] = Provenance(creator="mace", version=mace.__version__, routine="mace") - ret_data["schema_name"] = "qcschema_output" - ret_data["success"] = True - - # Form up a dict first, then sent to BaseModel to avoid repeat kwargs which don't override each other - return AtomicResult(**{**input_data.dict(), **ret_data}) diff --git a/qcengine/programs/model.py b/qcengine/programs/model.py deleted file mode 100644 index 96e953d10..000000000 --- a/qcengine/programs/model.py +++ /dev/null @@ -1,164 +0,0 @@ -import abc -import logging -from typing import Any, Dict, List, Optional, Tuple, Union - -try: - from pydantic.v1 import BaseModel -except ImportError: - from pydantic import BaseModel -from qcelemental.models import AtomicInput, AtomicResult, FailedOperation - -from qcengine.exceptions import KnownErrorException -from qcengine.config import TaskConfig - -logger = logging.getLogger(__name__) - - -class ProgramHarness(BaseModel, abc.ABC): - - _defaults: Dict[str, Any] = {} - name: str - scratch: bool - thread_safe: bool - thread_parallel: bool - node_parallel: bool - managed_memory: bool - extras: Optional[Dict[str, Any]] - - class Config: - allow_mutation: False - extra: "forbid" - - def __init__(self, **kwargs): - super().__init__(**{**self._defaults, **kwargs}) - - @abc.abstractmethod - def compute(self, input_data: AtomicInput, config: TaskConfig) -> Union[AtomicResult, FailedOperation]: - """Top-level compute method to be implemented for every ProgramHarness - - Note: - This method behave in any of the following ways: - 1. Return AtomicResult upon successful completion of a calculation - 2. Return FailedOperation object if an operation was unsuccessful or raised an exception. This is most - likely to occur if the underlying QC package has a QCSchema API that catches exceptions and - returns them as FailedOperation objects to end users. - 3. Raise an exception if a computation failed. The raised exception will be handled by the - qcng.compute() method and either raised or packaged as a FailedOperation object. - """ - pass - - @staticmethod - @abc.abstractmethod - def found(raise_error: bool = False) -> bool: - """ - Checks if the program can be found. - - Parameters - ---------- - raise_error : bool, optional - If True, raises an error if the program cannot be found. - - Returns - ------- - bool - Returns True if the program was found, False otherwise. - """ - - ## Utility - - def get_version(self) -> str: - """Finds program, extracts version, returns normalized version string. - - Returns - ------- - str - Return a valid, safe python version string. - """ - - ## Computers - - def build_input( - self, input_model: AtomicInput, config: TaskConfig, template: Optional[str] = None - ) -> Dict[str, Any]: - raise ValueError("build_input is not implemented for {}.", self.__class__) - - def execute( - self, - inputs: Dict[str, Any], - extra_outfiles: Optional[List[str]] = None, - extra_commands: Optional[List[str]] = None, - scratch_name: Optional[str] = None, - timeout: Optional[int] = None, - ) -> Tuple[bool, Dict[str, Any]]: - raise ValueError("execute is not implemented for {}.", self.__class__) - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - raise ValueError("parse_output is not implemented for {}.", self.__class__) - - -class ErrorCorrectionProgramHarness(ProgramHarness, abc.ABC): - """Base class for Harnesses that include logic to correct common errors - - Classes which implement this Harness must override the :meth:`_compute` method - rather than :meth:`compute`. The ``compute`` method from this class will make - calls to ``_compute`` with different actions as it attempts to correct errors in the - input files. - - The error corrections are defined by first implementing a :class:`KnownErrorException` - that contains logic to determine if a certain error occurs and returns an appropriate - update to the keywords of an AtomicInput. - - Then, modify the ``_compute`` method of your Harness to run the ``detect_error`` method - of each ``KnownErrorException`` for the Harness after it finishes performing the computation. - The ``detect_error`` method will raise an exception that is caught by the - ``ErrorCorrectionProgramHarness`` and used to determine if/how to re-run the computation. - """ - - def _compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: - raise NotImplementedError() - - def compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: - # Get the error correction configuration - error_policy = input_data.protocols.error_correction - - # Create a local copy of the input data - local_input_data = input_data - - # Run the method and, if it fails, assess if the failure is restartable - observed_errors = {} # Errors that have been observed previously - while True: - try: - result = self._compute(local_input_data, config) - break - except KnownErrorException as e: - logger.info(f"Caught a {type(e)} error.") - - # Determine whether this specific type of error is allowed - correction_allowed = error_policy.allows(e.error_name) - if not correction_allowed: - logger.info(f'Error correction for "{e.error_name}" is not allowed') - raise e - logger.info(f'Error correction for "{e.error_name}" is allowed') - - # Check if it has run before - # TODO (wardlt): Should we allow errors to be run >1 time? - previously_run = e.error_name in observed_errors - if previously_run: - logger.info( - "Error has been observed before and mitigation did not fix the issue. Raising exception" - ) - raise e - - # Generate and apply the updated keywords - keyword_updates = e.create_keyword_update(local_input_data) - new_keywords = local_input_data.keywords.copy() - new_keywords.update(keyword_updates) - local_input_data = AtomicInput(**local_input_data.dict(exclude={"keywords"}), keywords=new_keywords) - - # Store the error details and mitigations employed - observed_errors[e.error_name] = {"details": e.details, "keyword_updates": keyword_updates} - - # Add the errors observed and corrected for, if any - if len(observed_errors) > 0: - result.extras["observed_errors"] = observed_errors - return result diff --git a/qcengine/programs/molpro.py b/qcengine/programs/molpro.py deleted file mode 100644 index 0025ae0f4..000000000 --- a/qcengine/programs/molpro.py +++ /dev/null @@ -1,451 +0,0 @@ -""" -Calls the Molpro executable. -""" - -import string -from typing import Any, Dict, List, Optional, Set, Tuple -from xml.etree import ElementTree as ET - -from qcelemental.models import AtomicResult -from qcelemental.util import parse_version, safe_version, which - -from ..exceptions import InputError, UnknownError -from ..util import execute -from .model import ProgramHarness - - -class MolproHarness(ProgramHarness): - _defaults: Dict[str, Any] = { - "name": "Molpro", - "scratch": True, - "thread_safe": False, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - # Set of implemented dft functionals in Molpro according to dfunc.registry (version 2019.2) - # fmt: off - _dft_functionals: Set[str] = { - "B86MGC", "B86R", "B86", "B88C", "B88", "B95", "B97DF", "B97RDF", "BR", "BRUEG", "BW", "CS1", "CS2", "DIRAC", - "ECERFPBE", "ECERF", "EXACT", "EXERFPBE", "EXERF", "G96", "HCTH120", "HCTH147", "HCTH93", "HJSWPBEX", "LTA", - "LYP", "M052XC", "M052XX", "M05C", "M05X", "M062XC", "M062XX", "M06C", "M06HFC", "M06HFX", "M06LC", "M06LX", - "M06X", "M12C", "MK00B", "MK00", "P86", "PBEC", "PBESOLC", "PBESOLX", "PBEXREV", "PBEX", "PW86", "PW91C", - "PW91X", "PW92C", "STEST", "TFKE", "TH1", "TH2", "TH3", "TH4", "THGFCFO", "THGFCO", "THGFC", "THGFL", "TPSSC", - "TPSSX", "VSXC", "VW", "VWN3", "VWN5", "XC-M05-2X", "XC-M05", "XC-M06-2X", "XC-M06-HF", "XC-M06-L", "XC-M06", - "XC-M08-HX", "XC-M08-SO", "XC-M11-L", "XC-SOGGA11", "XC-SOGGA11-X", "XC-SOGGA", "FRMTST", "LHF", "TLHF", - "LXBECKE", "ELP", "NULL", "YTEST", "TREF2", "TREF", "TTEST", "GLE", "GREEN", "SRB88", "SRLYP", "LB94", "EI", - "SAOP", "USER", "INE", "ECERF2", "ECERFINTER", "ECERFLOCAL2", "ECERFLOCAL", "EXERFLOCAL", "FC", "FCFO", "FCO", - "FL", "XC-M11", "PBEXANAL", "PBECANAL", "PBESOLCANAL", "PBESOLXANAL", "EXSRLDA", "EXSRLPBE", "ECSRLPBE", - "ECSRLLPBE", "ECSQRTLPBE", "ECMUDIVLPBE", "EXERFPHS", "ECLERFMUPBE", "ECERFERFCPBE", "ECSQRTLDA", "REVPBEX", - "B", "B-LYP", "BLYP", "B-P", "BP86", "B-VWN", "B3LYP", "B3LYP3", "B3LYP5", "B88X", "B97", "B97R", "BECKE", - "BH-LYP", "CS", "D", "HFB", "HFS", "LDA", "LSDAC", "LSDC", "KYP88", "MM05", "MM05-2X", "MM06", "MM06-2X", - "MM06-L", "MM06-HF", "PBE", "PBE0", "PBE0MOL", "PBEREV", "PW91", "S", "S-VWN", "SLATER", "VS99", "VWN", - "VWN80", 'M05', "M05-2X", "M06", "M06-2X", "M06-L", "M06-HF", "M08-HX", "M08-SO", "M11-L", "TPSS", "TPSSH", - "M12HFC", "HJSWPBE", "HJSWPBEH", "TCSWPBE", "PBESOL" - } - # fmt: on - - # Different open-shell scenarios: - # - Restricted references - # RHF-RMP2 (like RHF-UMP2 using Molpro's naming convention for CC methods) - # RHF-UCCSD - # RHF-UCCSD(T) - # RHF-RCCSD - # RHF-RCCSD(T) - # - Unrestricted references (Only supported up to UMP2, no CC support) - # UHF-UMP2 - # NOTE: Unrestricted SCF methods must be specified by using keyword reference - _hf_methods: Set[str] = {"HF", "RHF"} - _restricted_post_hf_methods: Set[str] = {"MP2", "CCSD", "CCSD(T)"} # RMP2, RCCSD, RCCSD(T)} - # TODO Add keyword to specify unrestricted for WF method - # _unrestricted_post_hf_methods: Set[str] = {"UMP2", "UCCSD", "UCCSD(T)"} - _post_hf_methods: Set[str] = {*_restricted_post_hf_methods} - - class Config(ProgramHarness.Config): - pass - - def found(self, raise_error: bool = False) -> bool: - return which( - "molpro", return_bool=True, raise_error=raise_error, raise_msg="Please install via https://www.molpro.net/" - ) - - # TODO Consider changing this to use molpro --version instead of performing a full execute - def get_version(self) -> str: - self.found(raise_error=True) - - name_space = {"molpro_uri": "http://www.molpro.net/schema/molpro-output"} - which_prog = which("molpro") - if which_prog not in self.version_cache: - success, output = execute( - [which_prog, "version.inp", "-d", ".", "-W", "."], - infiles={"version.inp": ""}, - outfiles=["version.out", "version.xml"], - ) - - if success: - tree = ET.ElementTree(ET.fromstring(output["outfiles"]["version.xml"])) - root = tree.getroot() - version_tree = root.find("molpro_uri:job/molpro_uri:platform/molpro_uri:version", name_space) - if version_tree is None: - # some older schema - name_space = {"molpro_uri": "http://www.molpro.net/schema/molpro2006"} - version_tree = root.find("molpro_uri:job//molpro_uri:version", name_space) - if version_tree is None: - return safe_version("0.0.0") - year = version_tree.attrib["major"] - minor = version_tree.attrib["minor"] - molpro_version = year + "." + minor - self.version_cache[which_prog] = safe_version(molpro_version) - - return self.version_cache[which_prog] - - def compute(self, input_data: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - """ - Run Molpro - """ - # Check if Molpro executable is found - self.found(raise_error=True) - - # Check Molpro version - if parse_version(self.get_version()) < parse_version("2018.1"): - raise TypeError("Molpro version '{}' not supported".format(self.get_version())) - - # Setup the job - job_inputs = self.build_input(input_data, config) - - # Run Molpro - exe_success, proc = self.execute(job_inputs) - - # Determine whether the calculation succeeded - if exe_success: - # If execution succeeded, collect results - result = self.parse_output(proc["outfiles"], input_data) - return result - else: - # Return UnknownError for error propagation - return UnknownError(proc["stderr"]) - - def execute( - self, - inputs: Dict[str, Any], - extra_infiles: Optional[Dict[str, str]] = None, - extra_outfiles: Optional[List[str]] = None, - as_binary: Optional[List[str]] = None, - extra_commands: Optional[List[str]] = None, - scratch_name: Optional[str] = None, - scratch_messy: bool = False, - timeout: Optional[int] = None, - ) -> Tuple[bool, Dict[str, Any]]: - """ - For option documentation go look at qcengine/util.execute - """ - - # Collect all input files and update with extra_infiles - infiles = inputs["infiles"] - if extra_infiles is not None: - infiles.update(extra_infiles) - - # Collect all output files and update with extra_outfiles - outfiles = ["dispatch.out", "dispatch.xml"] - if extra_outfiles is not None: - outfiles.extend(extra_outfiles) - - # Replace commands with extra_commands if present - commands = inputs["commands"] - if extra_commands is not None: - commands = extra_commands - - # Run the Molpro program - exe_success, proc = execute( - commands, - infiles=infiles, - outfiles=outfiles, - as_binary=as_binary, - scratch_name=scratch_name, - scratch_directory=inputs["scratch_directory"], - scratch_messy=scratch_messy, - timeout=timeout, - ) - return exe_success, proc - - def build_input( - self, input_model: "AtomicInput", config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - if template is None: - input_file = [] - - # Resolving keywords - caseless_keywords = {k.lower(): v for k, v in input_model.keywords.items()} - unrestricted = False - # Following Molpro default that ROHF is done for open-shell calculations unless unrestricted is specified - if "reference" in caseless_keywords and caseless_keywords["reference"] == "unrestricted": - unrestricted = True - - # Memory is in megawords per core for Molpro - memory_mw_core = int(config.memory * (1024**3) / 8e6 / config.ncores) - input_file.append("memory,{},M".format(memory_mw_core)) - input_file.append("") - - # Write the geom - xyz_block = input_model.molecule.to_string(dtype="molpro", units="Bohr") - input_file.append(xyz_block) - - # Write the basis set - input_file.append("basis={") - input_file.append(f"default,{input_model.model.basis}") - input_file.append("}") - input_file.append("") - - # Determine what SCF type (restricted vs. unrestricted) - hf_type = "RHF" - dft_type = "RKS" - if unrestricted: - hf_type = "UHF" - dft_type = "UKS" - - # Write energy call - energy_call = [] - # If post-hf method is called then make sure to write a HF call first - if input_model.model.method.upper() in self._post_hf_methods: # post SCF case - energy_call.append(f"{{{hf_type}}}") - energy_call.append("") - energy_call.append(f"{{{input_model.model.method}}}") - # If DFT call make sure to write {rks,method} - elif input_model.model.method.upper() in self._dft_functionals: # DFT case - energy_call.append(f"{{{dft_type},{input_model.model.method}}}") - elif input_model.model.method.upper() in self._hf_methods: # HF case - energy_call.append(f"{{{hf_type}}}") - else: - raise InputError(f"Method {input_model.model.method} not implemented for Molpro.") - - # Write appropriate driver call - if input_model.driver == "energy": - input_file.extend(energy_call) - elif input_model.driver == "gradient": - input_file.extend(energy_call) - input_file.append("") - input_file.append("{force}") - else: - raise InputError(f"Driver {input_model.driver} not implemented for Molpro.") - - input_file = "\n".join(input_file) - else: - # Some of the potential different template options - # (A) ordinary build_input (need to define a base template) - # (B) user wants to add stuff after normal template (A) - # (C) user knows their domain language (doesn't use any QCSchema quantities) - - # # Build dictionary for substitute - # sub_dict = { - # "method": input_model.model.method, - # "basis": input_model.model.basis, - # "charge": input_model.molecule.molecular_charge - # } - - # Perform substitution to create input file - str_template = string.Template(template) - input_file = str_template.substitute() - - return { - "commands": ["molpro", "dispatch.mol", "-d", ".", "-W", ".", "-n", str(config.ncores)], - "infiles": {"dispatch.mol": input_file}, - "scratch_directory": config.scratch_directory, - "input_result": input_model.copy(deep=True), - } - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - tree = ET.ElementTree(ET.fromstring(outfiles["dispatch.xml"])) - root = tree.getroot() - # print(root.tag) - - # TODO Read information from molecule tag - # - cml:molecule, cml:atomArray (?) - # - basisSet - # - Be aware of symmetry. Might only be able to support if symmetry,nosym - # - orbitals - # - Be aware of symmetry. Might only be able to support if symmetry,nosym - # NOTE: Spherical basis set ordering in Molpro (with no symmetry) - # S --> 0 - # P --> +1, -1, 0 - # D --> 0, -2, +1, +2, -1 - # F --> +1, -1, 0, +3, -2, -3, +2 - # G --> 0, -2, +1, +4, -1, +2, -4, +3, -3 - # H --> +1, -1, +2, +3, -4, -3, +4, -5, 0, +5, -2 - # I --> +6, -2, +5, +4, -5, +2, -6, +3, -4, 0, -3, -1, +1 - properties = {} - extras = {} - name_space = {"molpro_uri": "http://www.molpro.net/schema/molpro-output"} - - # Molpro commands map - molpro_map = { - "Energy": { - "HF": "scf_total_energy", - "RHF": "scf_total_energy", - "UHF": "scf_total_energy", - "KS": "scf_total_energy", - "RKS": "scf_total_energy", - "UKS": "scf_total_energy", - }, - "total energy": { - "MP2": "mp2_total_energy", - "CCSD": "ccsd_total_energy", - "CCSD(T)": "ccsd_prt_pr_total_energy", - }, - "correlation energy": { - "MP2": "mp2_correlation_energy", - "CCSD": "ccsd_correlation_energy", - "CCSD(T)": "ccsd_prt_pr_correlation_energy", # Need both CCSD(T) and Total - "Total": "ccsd_prt_pr_correlation_energy", # Total corresponds to CCSD(T) correlation energy - }, - "singlet pair energy": {"MP2": "mp2_singlet_pair_energy", "CCSD": "ccsd_singlet_pair_energy"}, - "triplet pair energy": {"MP2": "mp2_triplet_pair_energy", "CCSD": "ccsd_triplet_pair_energy"}, - "Dipole moment": { - "HF": "scf_dipole_moment", - "RHF": "scf_dipole_moment", - "UHF": "scf_dipole_moment", - "KS": "scf_dipole_moment", - "RKS": "scf_dipole_moment", - "UKS": "scf_dipole_moment", - "MP2": "mp2_dipole_moment", - "CCSD": "ccsd_dipole_moment", - "CCSD(T)": "ccsd_prt_pr_dipole_moment", - }, - } - - # Started adding basic support for local correlation methods in Molpro - molpro_extras_map = { - "total energy": { - "LMP2": "local_mp2_total_energy", - "LCCSD": "local_ccsd_total_energy", - "LCCSD(T0)": "local_ccsd_prt0_pr_total_energy", - "LCCSD(T)": "local_ccsd_prt_pr_total_energy", - }, - "correlation energy": {"LMP2": "local_mp2_correlation_energy", "LCCSD": "local_ccsd_correlation_energy"}, - "singlet pair energy": {"LMP2": "local_mp2_singlet_pair_energy", "LCCSD": "local_ccsd_singlet_pair_energy"}, - "triplet pair energy": {"LMP2": "local_mp2_triplet_pair_energy", "LCCSD": "local_ccsd_triplet_pair_energy"}, - "singles energy": {"LCCSD": "local_ccsd_singles_energy"}, - # "strong pair energy": { - # "LCCSD": "local_ccsd_strong_pair_energy" - # }, - # "weak pair energy": { - # "LMP2": "local_mp2_weak_pair_energy" - # } - } - - # Molpro variables map used for quantities not found in the command map - molpro_variable_map = { - "_ENUC": "nuclear_repulsion_energy", - "_DFTFUN": "scf_xc_energy", - "_NELEC": ["calcinfo_nalpha", "calcinfo_nbeta"] - # "_EMP2_SCS": "scs_mp2_total_energy" - } - - # Process data in molpro_map by looping through each jobstep - # The jobstep tag in Molpro contains output from commands (e.g. {HF}, {force}) - for jobstep in root.findall("molpro_uri:job/molpro_uri:jobstep", name_space): - command = jobstep.attrib["command"] - if "FORCE" in command: # Grab gradient - for child in jobstep.findall("molpro_uri:gradient", name_space): - # Stores gradient as a single list where the ordering is [1x, 1y, 1z, 2x, 2y, 2z, ...] - properties["gradient"] = [float(x) for x in child.text.split()] - else: # Grab energies and dipole moment - for child in jobstep.findall("molpro_uri:property", name_space): - property_name = child.attrib["name"] - property_method = child.attrib["method"] - value = child.attrib["value"] - if property_name in molpro_map and property_method in molpro_map[property_name]: - if property_name == "Dipole moment": - properties[molpro_map[property_name][property_method]] = [float(x) for x in value.split()] - else: - properties[molpro_map[property_name][property_method]] = float(value) - elif property_name in molpro_extras_map and property_method in molpro_extras_map[property_name]: - extras[molpro_extras_map[property_name][property_method]] = float(value) - - # Convert triplet and singlet pair correlation energies to opposite-spin and same-spin correlation energies - if "mp2_singlet_pair_energy" in properties and "mp2_triplet_pair_energy" in properties: - properties["mp2_same_spin_correlation_energy"] = (2.0 / 3.0) * properties["mp2_triplet_pair_energy"] - properties["mp2_opposite_spin_correlation_energy"] = (1.0 / 3.0) * properties[ - "mp2_triplet_pair_energy" - ] + properties["mp2_singlet_pair_energy"] - del properties["mp2_singlet_pair_energy"] - del properties["mp2_triplet_pair_energy"] - - if "ccsd_singlet_pair_energy" in properties and "ccsd_triplet_pair_energy" in properties: - properties["ccsd_same_spin_correlation_energy"] = (2.0 / 3.0) * properties["ccsd_triplet_pair_energy"] - properties["ccsd_opposite_spin_correlation_energy"] = (1.0 / 3.0) * properties[ - "ccsd_triplet_pair_energy" - ] + properties["ccsd_singlet_pair_energy"] - del properties["ccsd_singlet_pair_energy"] - del properties["ccsd_triplet_pair_energy"] - - # Process data in molpro_variable_map - # Note: For the DFT case molecule_method is the name of the functional plus R or U in front - molecule = root.find("molpro_uri:job/molpro_uri:molecule", name_space) - molecule_method = molecule.attrib["method"] - molecule_final_energy = float(molecule.attrib["energy"]) # Energy from the molecule tag in case its needed - # Loop over each variable under the variables tag to grab additional info from molpro_variable_map - for variable in molecule.findall("molpro_uri:variables/molpro_uri:variable", name_space): - variable_name = variable.attrib["name"] - if variable_name in molpro_variable_map: - if variable_name == "_NELEC": - nelec = int(float(variable[0].text)) - nunpaired = input_model.molecule.molecular_multiplicity - 1 - nbeta = (nelec - nunpaired) // 2 - nalpha = nelec - nbeta - properties[molpro_variable_map[variable_name][0]] = nalpha - properties[molpro_variable_map[variable_name][1]] = nbeta - else: - properties[molpro_variable_map[variable_name]] = float(variable[0].text) - - # Process basis set data - basis_set = root.find("molpro_uri:job/molpro_uri:molecule/molpro_uri:basisSet", name_space) - nbasis = int(basis_set.attrib["length"]) - # angular_type = basis_set.attrib['angular'] # cartesian vs spherical - properties["calcinfo_nbasis"] = nbasis - - # Grab the method from input - method = input_model.model.method.upper() - - # Determining the final energy - # Throws an error if the energy isn't found for the method specified from the input_model. - if method in molpro_map["total energy"].keys() and molpro_map["total energy"][method] in properties: - final_energy = properties[molpro_map["total energy"][method]] - elif method in molpro_map["Energy"].keys() and molpro_map["Energy"][method] in properties: - final_energy = properties[molpro_map["Energy"][method]] - else: - # Back up method for determining final energy if not already present in properties - # Use the total energy from the molecule tag if it matches the input method - # if input_model.model.method.upper() in molecule_method: - if method in molecule_method: - final_energy = molecule_final_energy - if method in self._post_hf_methods: - properties[molpro_map["total energy"][method]] = molecule_final_energy - properties[molpro_map["correlation energy"][method]] = ( - molecule_final_energy - properties["scf_total_energy"] - ) - elif method in self._dft_functionals: - properties[molpro_map["Energy"]["KS"]] = molecule_final_energy - elif method in self._hf_methods: - properties[molpro_map["Energy"][method]] = molecule_final_energy - else: - raise KeyError(f"Could not find {method} total energy") - - # Initialize output_data by copying over input_model.dict() - output_data = input_model.dict() - - # Determining return_result - if input_model.driver == "energy": - output_data["return_result"] = final_energy - elif input_model.driver == "gradient": - output_data["return_result"] = properties.pop("gradient") - - # Final output_data assignments needed for the AtomicResult object - output_data["properties"] = properties - output_data["extras"].update(extras) - output_data["schema_name"] = "qcschema_output" - output_data["stdout"] = outfiles["dispatch.out"] - output_data["success"] = True - - return AtomicResult(**output_data) diff --git a/qcengine/programs/mopac.py b/qcengine/programs/mopac.py deleted file mode 100644 index e97a319c8..000000000 --- a/qcengine/programs/mopac.py +++ /dev/null @@ -1,299 +0,0 @@ -""" -Calls the Psi4 executable. -""" -import os -from typing import Any, Dict, List, Optional, Tuple - -from qcelemental.models import AtomicResult -from qcelemental.util import which - -from ..exceptions import InputError, UnknownError -from ..util import execute -from .model import ProgramHarness - - -class MopacHarness(ProgramHarness): - - _defaults = { - "name": "MOPAC", - "scratch": True, # Input/output file - "thread_safe": True, - "thread_parallel": True, - "node_parallel": False, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - def __init__(self, **kwargs): - extras = { # All units taken from within MOPAC - "bohr_to_angstroms": 0.5291772083, - "hartree_to_ev": 27.2113834, - "ev_to_kcalmol": 23.060529, - } - extras["au_to_debye"] = 2.99792458e10 * 1.602176462e0 * 1e-10 * extras["bohr_to_angstroms"] - extras["hartree_to_kcalmol"] = extras["hartree_to_ev"] * extras["ev_to_kcalmol"] - - kwargs["extras"] = extras - super().__init__(**kwargs) - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "mopac", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install -c conda-forge mopac`", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - # Not really possible to pull at the moment, MolSSI will add a version ability - return "2016" - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - self.found(raise_error=True) - - exec_command = self.build_input(input_model, config) - - success, output = self.execute(exec_command) - - ret = self.parse_output(output["outfiles"], input_model) - - # Determine whether the calculation succeeded - if success: - # raise Exception() - return ret - else: - return UnknownError(ret["stderr"]) - - def execute( - self, - inputs: Dict[str, Any], - extra_infiles: Optional[Dict[str, str]] = None, - extra_outfiles: Optional[List[str]] = None, - extra_commands: Optional[List[str]] = None, - scratch_name: Optional[str] = None, - scratch_messy: bool = False, - timeout: Optional[int] = None, - ) -> Tuple[bool, Dict[str, Any]]: - """ - For option documentation go look at qcengine/util.execute - """ - - infiles = inputs["infiles"] - if extra_infiles is not None: - infiles.update(extra_infiles) - - outfiles = inputs["outfiles"] - if extra_outfiles is not None: - outfiles.extend(extra_outfiles) - - command = inputs["command"] - if extra_commands is not None: - command.extend(extra_commands) - - exe_success, proc = execute( - command, - infiles=infiles, - outfiles=outfiles, - scratch_directory=inputs["scratch_directory"], - scratch_name=scratch_name, - scratch_messy=scratch_messy, - timeout=timeout, - environment=inputs.get("environment", None), - ) - return exe_success, proc - - def build_input( - self, input_model: "AtomicInput", config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - - if template is not None: - raise KeyError("MOPAC does not currently support input templates.") - - method = input_model.model.method.lower() - if method not in { - "mndo", - "am1", - "pm3", - "rm1", - "mndod", - "pm6", - "pm6-d3", - "pm6-dh+", - "pm6-dh2", - "pm6-dh2x", - "pm6-d3h4", - "pm6-3dh4x", - "pm7", - "pm7-ts", - }: - raise InputError(f"MOPAC does not have method: {method.upper()}") - - if input_model.driver not in ["energy", "gradient"]: - raise InputError(f"Driver {input_model.driver} not implemented for MOPAC.") - - input_file = [] - - keywords = {"ITER": 100, "PULAY": True} - keywords.update({k.upper(): v for k, v in input_model.keywords.items()}) - - if keywords["PULAY"]: - pulay = " PULAY" - else: - pulay = " " - - unknown_keywords = keywords.keys() - {"ITER", "PULAY"} - if unknown_keywords: - raise InputError(f"Unknown keywords given to MOPAC: {unknown_keywords}") - - # 1SCF says not to compute a geometry optimization, always compute a gradient (free), and dump the aux file - input_file.append( - f"{method.upper()} " - f"CHARGE={input_model.molecule.molecular_charge} " - f"MS={(input_model.molecule.molecular_multiplicity-1)/2}&" - ) - input_file.append(f"1SCF GRADIENTS AUX(PRECISION=9, XP, XS, XW) A0{pulay} ITER={keywords['ITER']}&") - - # input_file[-1] = input_file[-1][:-1] - input_file.append("") - - mol = input_model.molecule - - for x in range(len(mol.symbols)): - input_file.append( - f"{mol.symbols[x]} {mol.geometry[x, 0]:17.12f} {mol.geometry[x, 1]:17.12f} {mol.geometry[x, 2]:17.12f}" - ) - - env = os.environ.copy() - env["MKL_NUM_THREADS"] = str(config.ncores) - env["OMP_NUM_THREADS"] = str(config.ncores) - - return { - "command": ["mopac", "dispatch.mop"], - "infiles": {"dispatch.mop": "\n".join(input_file)}, - "outfiles": {"dispatch.out", "dispatch.aux"}, - "scratch_directory": config.scratch_directory, - "environment": env, - } - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - - keep_keys = { - "heat_of_formation", - "energy_electronic", - "energy_nuclear", - "gradient_norm", - "dip_vec", - "spin_component", - "total_spin", - "molecular_weight", - "molecular_weight", - "total_energy", - "gradients", - "mopac_version", - "atom_charges", - "point_group", - } - - # Convert back to atomic units - conversions = { - "KCAL/MOL": 1 / self.extras["hartree_to_kcalmol"], - "KCAL/MOL/ANGSTROM": self.extras["bohr_to_angstroms"] / self.extras["hartree_to_kcalmol"], - "EV": 1 / self.extras["hartree_to_ev"], - "DEBYE": 1 / self.extras["au_to_debye"], - "AMU": 1, - None: 1, - } - - data = {} - last_key = None - - # Parse the weird structure - if outfiles["dispatch.aux"] is None: - error = "An unknown error occured and no results were captured." - if outfiles["dispatch.out"] is not None: - error = outfiles["dispatch.out"] - raise UnknownError(error) - - for line in outfiles["dispatch.aux"].splitlines(): - if ("START" in line) or ("END" in line) or ("#" in line): - continue - - if "=" in line: - - # Primary split - key, value = line.split("=", 1) - - # Format key, may have units - # IONIZATION_POTENTIAL:EV - # GRADIENTS:KCAL/MOL/ANGSTROM[09] - key_list = key.split(":", 1) - if len(key_list) == 1: - key, units = key_list[0], None - else: - key, units = key.split(":", 1) - - # Pop off [xx] items - if units and "[" in units: - units, _ = units.split("[", 1) - - if "[" in key: - key, _ = key.split("[", 1) - - key = key.strip().lower() - last_key = key - - # Skip keys that are not useful - if key not in keep_keys: - last_key = None - continue - - # 1D+3 -> 1E3 conversion - cf = conversions[units] - - value = value.strip().replace("D+", "E+").replace("D-", "E-") - if ("E+" in value) or ("E-" in value): - if value.count("E") > 1: - value = [float(x) * cf for x in value.split()] - else: - value = float(value) * cf - - if value == "": - value = [] - - data[key] = (cf, value) - else: - if last_key is None: - continue - - cf = data[last_key][0] - data[last_key][1].extend([float(x) * cf for x in line.split()]) - - data = {k: v[1] for k, v in data.items()} - if ("gradients" not in data) or ("mopac_version" not in data): - raise UnknownError("Could not correctly parse the MOPAC output file.") - - gradient = data.pop("gradients") - - output = input_model.dict() - output["provenance"] = {"creator": "mopac", "version": data.pop("mopac_version")} - - output["properties"] = {} - output["properties"]["return_energy"] = data["heat_of_formation"] - - output["extras"].update(data) - - if input_model.driver == "energy": - output["return_result"] = data["heat_of_formation"] - else: - output["return_result"] = gradient - - output["stdout"] = outfiles["dispatch.out"] - output["success"] = True - - return AtomicResult(**output) diff --git a/qcengine/programs/mp2d.py b/qcengine/programs/mp2d.py deleted file mode 100644 index 2fd08c8aa..000000000 --- a/qcengine/programs/mp2d.py +++ /dev/null @@ -1,234 +0,0 @@ -"""Compute dispersion correction using Greenwell & Beran's MP2D executable.""" - -import pprint -import re -import sys -from decimal import Decimal -from typing import Any, Dict, Optional, Tuple - -import numpy as np -import qcelemental as qcel -from qcelemental.models import AtomicResult, Provenance -from qcelemental.util import safe_version, which - -from ..exceptions import InputError, ResourceError, UnknownError -from ..util import execute -from . import empirical_dispersion_resources -from .model import ProgramHarness - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) - - -class MP2DHarness(ProgramHarness): - - _defaults = { - "name": "MP2D", - "scratch": True, - "thread_safe": True, - "thread_parallel": False, - "node_parallel": False, - "managed_memory": False, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - return which( - "mp2d", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via `conda install mp2d -c psi4`", - ) - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("mp2d") - if which_prog not in self.version_cache: - # Note: anything below v1.1 will return an input error message here. but that's fine as version compare evals to False. - command = [which_prog, "--version"] - import subprocess - - proc = subprocess.run(command, stdout=subprocess.PIPE) - self.version_cache[which_prog] = safe_version(proc.stdout.decode("utf-8").strip()) - - return self.version_cache[which_prog] - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - from ..testing import is_program_new_enough - - self.found(raise_error=True) - - if not is_program_new_enough("mp2d", "1.1"): - raise ResourceError(f"MP2D version '{self.get_version()}' too old. Please update to at least '1.1'.") - - job_inputs = self.build_input(input_model, config) - - success, dexe = self.execute(job_inputs) - - if success: - dexe["outfiles"]["stdout"] = dexe["stdout"] - dexe["outfiles"]["stderr"] = dexe["stderr"] - dexe["outfiles"]["input"] = " ".join(job_inputs["command"]) - dexe["outfiles"]["mp2d_geometry"] = job_inputs["infiles"]["mp2d_geometry"] - output_model = self.parse_output(dexe["outfiles"], input_model) - - else: - output_model = input_model - output_model["error"] = {"error_type": "execution_error", "error_message": dexe["stderr"]} - - return output_model - - def execute( - self, inputs: Dict[str, Any], *, extra_outfiles=None, extra_commands=None, scratch_name=None, timeout=None - ) -> Tuple[bool, Dict]: - - success, dexe = execute( - inputs["command"], - inputs["infiles"], - inputs["outfiles"], - scratch_messy=inputs["scratch_messy"], - scratch_directory=inputs["scratch_directory"], - ) - return success, dexe - - def build_input( - self, input_model: "AtomicInput", config: "TaskConfig", template: Optional[str] = None - ) -> Dict[str, Any]: - - # strip engine hint - mtd = input_model.model.method - if mtd.startswith("mp2d-"): - mtd = mtd[5:] - - if input_model.driver.derivative_int() > 1: - raise InputError(f"Driver {input_model.driver} not implemented for MP2D.") - - # temp until actual options object - input_model.extras["info"] = empirical_dispersion_resources.from_arrays( - name_hint=mtd, - level_hint=input_model.keywords.get("level_hint", None), - param_tweaks=input_model.keywords.get("params_tweaks", None), - dashcoeff_supplement=input_model.keywords.get("dashcoeff_supplement", None), - ) - - # Need 'real' field later and that's only guaranteed for molrec - molrec = qcel.molparse.from_schema(input_model.molecule.dict()) - xyz = qcel.molparse.to_string(molrec, dtype="xyz", units="Angstrom", ghost_format="") - infiles = {"mp2d_geometry": xyz} - # jobrec['molecule']['real'] = molrec['real'] - - # env = { - # 'HOME': os.environ.get('HOME'), - # 'PATH': os.environ.get('PATH'), - # #'PATH': os.pathsep.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(os.pathsep) if x != '']) + \ - # # os.pathsep + os.environ.get('PATH'), - # #'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH'), - # } - - command = ["mp2d", "mp2d_geometry"] - command.extend( - """--TT_a1={a1} --TT_a2={a2} --rcut={rcut} --w={w} --s8={s8}""".format( - **input_model.extras["info"]["dashparams"] - ).split() - ) - if input_model.driver == "gradient": - command.append("--gradient") - - return { - "command": command, - "infiles": infiles, - "outfiles": ["mp2d_gradient"], - "scratch_messy": config.scratch_messy, - "scratch_directory": config.scratch_directory, - "input_result": input_model.copy(deep=True), - } - - def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": - stdout = outfiles.pop("stdout") - stderr = outfiles.pop("stderr") - - for fl, contents in outfiles.items(): - if contents is not None: - # LOG text += f'\n MP2D scratch file {fl} has been read.\n' - pass - - # parse energy output (could go further and break into UCHF, CKS) - real = np.array(input_model.molecule.real) - full_nat = real.shape[0] - real_nat = np.sum(real) - - for ln in stdout.splitlines(): - if re.match(" MP2D dispersion correction Eh", ln): - ene = Decimal(ln.split()[4]) - elif re.match("Atomic Coordinates in Angstroms", ln): - break - else: - if not ((real_nat == 1) and (input_model.driver == "gradient")): - raise UnknownError("Unknown issue occured.") - - # parse gradient output - if outfiles["mp2d_gradient"] is not None: - srealgrad = outfiles["mp2d_gradient"] - realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) - - if input_model.driver == "gradient": - ireal = np.argwhere(real).reshape((-1)) - fullgrad = np.zeros((full_nat, 3)) - try: - fullgrad[ireal, :] = realgrad - except NameError as exc: - raise UnknownError("Unsuccessful gradient collection.") from exc - - qcvkey = input_model.extras["info"]["fctldash"].upper() - - calcinfo = [] - calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", ene)) - calcinfo.append(qcel.Datum("2-BODY DISPERSION CORRECTION ENERGY", "Eh", ene)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} DISPERSION CORRECTION ENERGY", "Eh", ene)) - - if input_model.driver == "gradient": - calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - calcinfo.append(qcel.Datum("2-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - if qcvkey: - calcinfo.append(qcel.Datum(f"{qcvkey} DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) - - # LOGtext += qcel.datum.print_variables({info.label: info for info in calcinfo}) - calcinfo = {info.label: info.data for info in calcinfo} - # calcinfo = qcel.util.unnp(calcinfo, flat=True) - - # got to even out who needs plump/flat/Decimal/float/ndarray/list - # Decimal --> str preserves precision - calcinfo = {k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in calcinfo.items()} - - # jobrec['properties'] = {"return_energy": ene} - # jobrec["molecule"]["real"] = list(jobrec["molecule"]["real"]) - - retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] - if isinstance(retres, Decimal): - retres = float(retres) - elif isinstance(retres, np.ndarray): - retres = retres.ravel().tolist() - - output_data = { - "extras": input_model.extras, - "native_files": {k: v for k, v in outfiles.items() if v is not None}, - "properties": {}, - "provenance": Provenance( - creator="MP2D", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name - ), - "return_result": retres, - "stderr": stderr, - "stdout": stdout, - } - output_data["extras"]["local_keywords"] = input_model.extras["info"] - output_data["extras"]["qcvars"] = calcinfo - - output_data["success"] = True - return AtomicResult(**{**input_model.dict(), **output_data}) diff --git a/qcengine/programs/mrchem.py b/qcengine/programs/mrchem.py deleted file mode 100644 index e52c889ee..000000000 --- a/qcengine/programs/mrchem.py +++ /dev/null @@ -1,339 +0,0 @@ -""" -Calls the MRChem executable. -""" -import copy -import json -import logging -import pprint -import sys -from collections import Counter -from functools import reduce -from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple - -from qcelemental.models import AtomicResult -from qcelemental.util import safe_version, which - -from ..exceptions import InputError, RandomError, UnknownError -from ..util import create_mpi_invocation, execute, popen, temporary_directory -from .model import ProgramHarness - -if TYPE_CHECKING: - from qcelemental.models import AtomicInput - - from ..config import TaskConfig - -pp = pprint.PrettyPrinter(width=120, compact=True, indent=1) -logger = logging.getLogger(__name__) - - -class MRChemHarness(ProgramHarness): - - _defaults = { - "name": "MRChem", - "scratch": False, - "thread_safe": False, - "thread_parallel": True, - "node_parallel": True, - "managed_memory": True, - } - version_cache: Dict[str, str] = {} - - class Config(ProgramHarness.Config): - pass - - @staticmethod - def found(raise_error: bool = False) -> bool: - """Whether MRChem harness is ready for operation. - - Parameters - ---------- - raise_error: bool - Passed on to control negative return between False and ModuleNotFoundError raised. - - Returns - ------- - bool - If mrchem and mrchem.x are found, returns True. - If raise_error is False and MRChem is missing, returns False. - If raise_error is True and MRChem is missing, the error message is raised. - - """ - mrchem_x = which( - "mrchem.x", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via https://mrchem.readthedocs.io", - ) - mrchem = which( - "mrchem", - return_bool=True, - raise_error=raise_error, - raise_msg="Please install via https://mrchem.readthedocs.io", - ) - - return mrchem and mrchem_x - - def get_version(self) -> str: - self.found(raise_error=True) - - which_prog = which("mrchem.x") - if which_prog not in self.version_cache: - with popen([which_prog, "--version"]) as exc: - exc["proc"].wait(timeout=30) - self.version_cache[which_prog] = safe_version(exc["stdout"].split()[-1]) - - candidate_version = self.version_cache[which_prog] - - return candidate_version - - def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult": - """ - Runs MRChem in executable mode - """ - self.found(raise_error=True) - - # Location resolution order config.scratch_dir, /tmp - parent = config.scratch_directory - - error_message = None - compute_success = False - - job_input = self.build_input(input_model, config) - input_data = copy.deepcopy(job_input["mrchem_json"]) - - output_data = { - "keywords": input_data, - "schema_name": "qcschema_output", - "schema_version": 1, - "model": input_model.model, - "molecule": input_model.molecule, - "driver": input_model.driver, - "extras": input_model.extras, - } - - with temporary_directory(parent=parent, suffix="_mrchem_scratch") as tmpdir: - # create folders - for d in job_input["folders"]: - if not Path(d).exists(): - Path(d).mkdir() - - # Execute the program - success, output = execute( - command=job_input["command"] + ["data.json"], - infiles={"data.json": json.dumps(job_input["mrchem_json"])}, - outfiles=["data.json"], - scratch_directory=tmpdir, - ) - - if success: - output_data["stdout"] = output["stdout"] - # get data from the MRChem JSON output and transfer it to the QCSchema output - mrchem_json = json.loads(output["outfiles"]["data.json"]) - mrchem_output = mrchem_json["output"] - output_data["success"] = mrchem_output["success"] - output_data["provenance"] = mrchem_output["provenance"] - # parallel set up - output_data["provenance"].update(job_input["parallel"]) - # remove nthreads, above info is more detailed anyway - output_data["provenance"].pop("nthreads") - # update the "routine" under "provenance" - output_data["provenance"]["routine"] = " ".join(job_input["command"]) - - # fill up properties - output_data["properties"] = extract_properties(mrchem_output) - - # prepare a list of computed response properties - known_rsp_props = [ - ("dipole_moment", "vector"), - ("quadrupole_moment", "tensor"), - ("polarizability", "tensor"), - ("magnetizability", "tensor"), - ("nmr_shielding", "tensor"), - ] - computed_rsp_props = [ - ("properties", x, y, z) - for x, z in known_rsp_props - if x in mrchem_output["properties"] - for y in mrchem_output["properties"][x].keys() - ] - - # fill up extras: - # * under "raw_output" the whole JSON output from MRChem - # * under "properties" all the properties computed by MRChem - output_data["extras"].update( - { - "raw_output": mrchem_json, - "properties": { - f"{ks[1]}": {f"{ks[2]}": _nested_get(mrchem_output, ks)} for ks in computed_rsp_props - }, - } - ) - - # fill up return_result - if input_model.driver == "energy": - output_data["return_result"] = mrchem_output["properties"]["scf_energy"]["E_tot"] - elif input_model.driver == "gradient": - output_data["return_result"] = mrchem_output["properties"]["geometric_derivative"]["geom-1"][ - "total" - ] - elif input_model.driver == "properties": - output_data["return_result"] = { - f"{ks[1]}": {f"{ks[2]}": _nested_get(mrchem_output, ks)} for ks in computed_rsp_props - } - else: - raise InputError(f"Driver {input_model.driver} not implemented for MRChem.") - - compute_success = mrchem_output["success"] - - else: - output_data["stderr"] = output["stderr"] - output_data["error"] = { - "error_message": output["stderr"], - "error_type": "execution_error", - } - - # Dispatch errors, PSIO Errors are not recoverable for future runs - if compute_success is False: - - if ("SIGSEV" in error_message) or ("SIGSEGV" in error_message) or ("segmentation fault" in error_message): - raise RandomError(error_message) - else: - raise UnknownError(error_message) - - return AtomicResult(**output_data) - - def build_input(self, input_model: "AtomicInput", config: "TaskConfig") -> Dict[str, Any]: - with popen([which("mrchem"), "--module"]) as exc: - exc["proc"].wait(timeout=30) - sys.path.append(exc["stdout"].split()[-1]) - from mrchem import translate_input, validate - - ranks_per_node = config.ncores // config.cores_per_rank if config.use_mpiexec else 1 - mrchemrec = { - "scratch_directory": config.scratch_directory, - # parallel set up - "parallel": { - "ncores": config.ncores, - "nnodes": config.nnodes, - "ranks_per_node": ranks_per_node, - "cores_per_rank": config.cores_per_rank, - "total_cores": config.nnodes * ranks_per_node * config.cores_per_rank, - "total_ranks": config.nnodes * ranks_per_node, - }, - } - - opts = copy.deepcopy(input_model.keywords) - - # Handle molecule - _, moldict = input_model.molecule.to_string( - dtype="mrchem", units=opts.get("world_unit", "bohr"), return_data=True - ) - opts["Molecule"] = moldict["keywords"] - - if "WaveFunction" in opts.keys(): - opts["WaveFunction"]["method"] = input_model.model.method - else: - opts["WaveFunction"] = {"method": input_model.model.method} - - # The molecular gradient is just a first-order property for MRChem - if input_model.driver == "gradient": - opts.update({"Properties": {"geometric_derivative": True}}) - - # Log the job settings as constructed from the input model - logger.debug("JOB_OPTS from InputModel") - logger.debug(pp.pformat(opts)) - - try: - opts = validate(ir_in=opts) - except Exception as e: - raise InputError(f"Failure preparing input to MRChem\n {str(e)}") - # Log the validated job settings - logger.debug("JOB_OPTS after validation") - logger.debug(pp.pformat(opts)) - mrchemrec["folders"] = [ - opts["SCF"]["path_checkpoint"], - opts["SCF"]["path_orbitals"], - opts["Response"]["path_checkpoint"], - opts["Response"]["path_orbitals"], - opts["Plotter"]["path"], - ] - - try: - opts = translate_input(opts) - except Exception as e: - raise InputError(f"Failure preparing input to MRChem\n {str(e)}") - opts["printer"]["file_name"] = "data.inp" - # Log the final job settings - logger.debug("JOB_OPTS after translation") - logger.debug(pp.pformat(opts)) - - mrchemrec["mrchem_json"] = { - "input": opts, - } - - # Determine the command - if config.use_mpiexec: - mrchemrec["command"] = create_mpi_invocation(which("mrchem.x"), config) - logger.info(f"Launching with mpiexec: {' '.join(mrchemrec['command'])}") - else: - mrchemrec["command"] = [which("mrchem.x")] - - return mrchemrec - - -def extract_properties(mrchem_output: Dict[str, Any]) -> Dict[str, Any]: - """Translate MRChem output to QCSChema properties. - - Parameters - ---------- - - Returns - ------- - """ - - occs = Counter(mrchem_output["properties"]["orbital_energies"]["spin"]) - properties = { - "calcinfo_nmo": 2 * occs["p"] + occs["a"] + occs["b"], - "calcinfo_nalpha": occs["p"] + occs["a"], - "calcinfo_nbeta": occs["p"] + occs["b"], - "calcinfo_natom": len(mrchem_output["properties"]["geometry"]), - "return_energy": mrchem_output["properties"]["scf_energy"]["E_tot"], - "scf_one_electron_energy": mrchem_output["properties"]["scf_energy"]["E_kin"] - + mrchem_output["properties"]["scf_energy"]["E_en"] - + mrchem_output["properties"]["scf_energy"]["E_next"] - + mrchem_output["properties"]["scf_energy"]["E_eext"], - "scf_two_electron_energy": mrchem_output["properties"]["scf_energy"]["E_ee"] - + mrchem_output["properties"]["scf_energy"]["E_x"] - + mrchem_output["properties"]["scf_energy"]["E_xc"], - "nuclear_repulsion_energy": mrchem_output["properties"]["scf_energy"]["E_nn"], - "scf_xc_energy": mrchem_output["properties"]["scf_energy"]["E_xc"], - "scf_total_energy": mrchem_output["properties"]["scf_energy"]["E_tot"], - "scf_iterations": len(mrchem_output["scf_calculation"]["scf_solver"]["cycles"]), - "scf_dipole_moment": mrchem_output["properties"]["dipole_moment"]["dip-1"]["vector"], - } - - return properties - - -def _nested_get(d: Dict[str, Any], ks: Tuple[str, ...]) -> Optional[Any]: - """Get value from a nested dictionary. - - Parameters - ---------- - d : Dict[str, Any] - ks : str - - Returns - ------- - v : Optional[Any] - - Notes - ----- - Adapted from: https://stackoverflow.com/a/40675868/2528668 - """ - - def _func(x: Dict[str, Any], k: str) -> Optional[Dict[str, Any]]: - return x.get(k, None) if isinstance(x, dict) else None - - return reduce(_func, ks, d) # type: ignore diff --git a/qcengine/programs/nwchem/__init__.py b/qcengine/programs/nwchem/__init__.py deleted file mode 100644 index 5a9766069..000000000 --- a/qcengine/programs/nwchem/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .runner import NWChemHarness diff --git a/qcengine/programs/nwchem/errors.py b/qcengine/programs/nwchem/errors.py deleted file mode 100644 index e1c214d14..000000000 --- a/qcengine/programs/nwchem/errors.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Known errors for NWChem""" -import logging -from typing import Any, Dict - -from qcelemental.models import AtomicInput - -from qcengine.exceptions import SimpleKnownErrorException -from qcengine.programs.nwchem.germinate import xc_functionals - -logger = logging.getLogger(__name__) - - -class GeomBinvrError(SimpleKnownErrorException): - """GeomBinvr Error""" - - error_name = "geom_binvr" - description = "Error when generating redundant atomic coordinates. Often fixed by turning autoz off (`noautoz`)" - - @classmethod - def _detect(cls, outputs: Dict[str, str]) -> bool: - return "geom_binvr: #indep variables incorrect" in outputs["stdout"] - - def create_keyword_update(self, input_data: AtomicInput) -> Dict[str, Any]: - return {"geometry__noautoz": True} - - -class ConvergenceFailedError(SimpleKnownErrorException): - """Failed to converge on electronic steps. This will increase the limit by 4x""" - - error_name = "convergence_failed" - description = "The computation failed to converge." - - @classmethod - def _detect(cls, outputs: Dict[str, str]) -> bool: - return ( - "This type of error is most commonly" in outputs["stdout"] and "convergence criteria" in outputs["stdout"] - ) - - def create_keyword_update(self, input_data: AtomicInput) -> Dict[str, Any]: - # Fit the correct keyword we are looking to update is different for different methods - method = input_data.model.method - use_tce = input_data.keywords.get("qc_module", False) - - if method == "dft" or method.split()[0] in xc_functionals: - if "dft__iterations" in input_data.keywords: - kwd = "dft__iterations" - cur_iter = input_data.keywords["dft__iterations"] - elif "dft__maxiter" in input_data.keywords: - kwd = "dft__maxiter" - cur_iter = input_data.keywords["dft__maxiter"] - else: - kwd = "dft__maxiter" - cur_iter = 20 # The NWChem default - elif method in ["scf", "hf", "mp2"]: - kwd = "scf__maxiter" - cur_iter = input_data.keywords.get(kwd, 8) - elif method.startswith("ccsd") and not use_tce: - kwd = "ccsd__maxiter" - cur_iter = input_data.keywords.get(kwd, 20) - elif method.startswith("ccsd") and use_tce: - kwd = "tce__maxiter" - cur_iter = input_data.keywords.get(kwd, 100) - else: - raise ValueError(f'Method "{method}" is not yet supported') - - return {kwd: cur_iter * 4} - - -# List of all the known errors -all_errors = [GeomBinvrError, ConvergenceFailedError] diff --git a/qcengine/programs/nwchem/germinate.py b/qcengine/programs/nwchem/germinate.py deleted file mode 100644 index 2ccc62b40..000000000 --- a/qcengine/programs/nwchem/germinate.py +++ /dev/null @@ -1,204 +0,0 @@ -from typing import Any, Dict, Tuple - -from qcengine.exceptions import InputError - -# List of XC functionals known to NWChem -xc_functionals = [ - "acm", - "b3lyp", - "beckehandh", - "pbe0", - "becke97", - "becke97-1", - "becke97-2", - "becke97-3", - "becke97-d", - "becke98", - "hcth", - "hcth120", - "hcth147", - "hcth407", - "becke97gga1", - "hcth407p", - "mpw91", - "mpw1k", - "xft97", - "cft97", - "ft97", - "op", - "bop", - "pbeop", - "xpkzb99", - "cpkzb99", - "xtpss03", - "ctpss03", - "xctpssh", - "b1b95", - "bb1k", - "mpw1b95", - "mpwb1k", - "pw6b95", - "pwb6k", - "m05", - "m05-2x", - "vs98", - "m06", - "m06-hf", - "m06-L", - "m06-2x", - "hfexch", - "becke88", - "xperdew91", - "xpbe96", - "gill96", - "lyp", - "perdew81", - "perdew86", - "perdew91", - "cpbe96", - "pw91lda", - "slater", - "vwn_1", - "vwn_2", - "vwn_3", - "vwn_4", - "vwn_5", - "vwn_1_rpa", - "xtpss03", - "ctpss03", - "bc95", - "xpw6b95", - "xpwb6k", - "xm05", - "xm05-2x", - "cpw6b95", - "cpwb6k", - "cm05", - "cm05-2x", - "xvs98", - "cvs98", - "xm06-L", - "xm06-hf", - "xm06", - "xm06-2x", - "cm06-L", - "cm06-hf", - "cm06", - "cm06-2x", -] - - -def muster_modelchem(method: str, derint: int, use_tce: bool) -> Tuple[str, Dict[str, Any]]: - """Converts the QC method into NWChem keywords - - Args: - method (str): Name of the QC method to use - derint (str): Index of the run type - use_tce (bool): Whether to use the Tensor Contraction Engine - Returns: - (str): Task command for NWChem - (dict): Any options for NWChem - """ - - # Standardize the method name - method = method.lower() - opts = {} - - # Map the run type to - runtyp = {"energy": "energy", "gradient": "gradient", "hessian": "hessian", "properties": "property"}[derint] - - # Write out the theory directive - if method == "nwchem": - mdccmd = "" - - elif method in ["scf", "hf"]: - mdccmd = f"task scf {runtyp}\n\n" - - elif method == "mp2": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__mp2"] = True - else: - mdccmd = f"task mp2 {runtyp}\n\n" - - elif method == "mp3": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__mp3"] = True - - elif method == "mp4": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__mp4"] = True - - elif method == "ccd": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__ccd"] = True - - elif method == "ccsd": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__ccsd"] = True - else: - mdccmd = f"task ccsd {runtyp}\n\n" - - elif method == "ccsd+t(ccsd)": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__ccsd(t)"] = True - else: - mdccmd = f"task ccsd+t(ccsd) {runtyp}\n\n" - - elif method == "ccsd(t)": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__ccsd(t)"] = True - else: - mdccmd = f"task ccsd(t) {runtyp}\n\n" - - elif method == "ccsdt": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__ccsdt"] = True - else: - mdccmd = f"task ccsdt {runtyp}\n\n" - - elif method == "tddft": - mdccmd = f"task tddft {runtyp}\n\n" - - elif method in ["sodft", "direct_mp2", "rimp2", "mcscf", "selci", "md", "pspw", "band"]: - raise InputError(f'Method "{method}" not yet supported by QCEngine') - - elif method == "tce": - raise InputError( - f"Do not specify TCE as a method. Instead specify the desired method " f'as a keyword and "qc_module=True".' - ) - - elif method.split()[0] in xc_functionals: - opts["dft__xc"] = method - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__"] = "dft" - else: - mdccmd = f"task dft {runtyp}\n\n" - - elif method == "pbe": - opts["dft__xc"] = "xpbe96 cpbe96" - mdccmd = f"task dft {runtyp}\n\n" - - elif method == "b3lyp5": - opts["dft__xc"] = "hfexch 0.2 slater 0.8 becke88 nonlocal 0.72 vwn_5 0.190 lyp 0.81" - mdccmd = f"task dft {runtyp}\n\n" - - elif method == "dft": - if use_tce: - mdccmd = f"task tce {runtyp}\n\n" - opts["tce__"] = "dft" - else: - mdccmd = f"task dft {runtyp}\n\n" - - else: - raise InputError(f"Method not recognized: {method}") - - return mdccmd, opts diff --git a/qcengine/programs/nwchem/harvester.py b/qcengine/programs/nwchem/harvester.py deleted file mode 100644 index 94425ba9a..000000000 --- a/qcengine/programs/nwchem/harvester.py +++ /dev/null @@ -1,1232 +0,0 @@ -import json -import logging -import re -from decimal import Decimal -from typing import Optional, Tuple - -import numpy as np -import qcelemental as qcel -from qcelemental.models import Molecule -from qcelemental.molparse import regex - -from ..util import PreservingDict - -logger = logging.getLogger(__name__) - - -def harvest_output(outtext: str) -> Tuple[PreservingDict, Molecule, list, str, str]: - """Function to read an entire NWChem output file. - - Reads all of the different "line search" segments of a file and returns - values from the last segment for which a geometry was written. - - Args: - outtext (str): Output written to stdout - Returns: - - (PreservingDict) Variables extracted from the output file in the last complete step - - (Molecule): Molecule from the last complete step - - (list): Gradient from the last complete step - - (str): Version string - - (str): Error message, if any - """ - - # Loop over all steps - # TODO (wardlt): Is it only necessary to read the last two steps? - pass_psivar = [] - pass_coord = [] - pass_grad = [] - for outpass in re.split(r" Line search:", outtext, re.MULTILINE): - psivar, nwcoord, nwgrad, version, module, error = harvest_outfile_pass(outpass) - pass_psivar.append(psivar) - pass_coord.append(nwcoord) - pass_grad.append(nwgrad) - - # Determine which segment contained the last geometry - retindx = -1 if pass_coord[-1] else -2 - - return pass_psivar[retindx], pass_coord[retindx], pass_grad[retindx], version, module, error - - -def harvest_outfile_pass(outtext): - """Function to read NWChem output file *outtext* and parse important - quantum chemical information from it in - - """ - psivar = PreservingDict() - psivar_coord = None - psivar_grad = None - version = "" - module = None - error = "" # TODO (wardlt): The error string is never used. - - NUMBER = r"(?x:" + regex.NUMBER + ")" - - # Process version - mobj = re.search( - r"^\s+" + r"Northwest Computational Chemistry Package (NWChem)" + r"\s+" + r"(?:\d+.\d+)" + r"\s*$", - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched version") - version = mobj.group("version") - - # Process SCF - # 1)Fail to converge - mobj = re.search(r"^\s+" + r"(?:Calculation failed to converge)" + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("failed to converge") - - # 2)Calculation converged - else: - mobj = re.search(r"^\s+" + r"(?:Total SCF energy)" + r"\s+=\s*" + NUMBER + r"s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched HF") - psivar["HF TOTAL ENERGY"] = mobj.group(1) - - # Process Effective nuclear repulsion energy (a.u.) - mobj = re.search( - r"^\s+" + r"Effective nuclear repulsion energy \(a\.u\.\)" + r"\s+" + NUMBER + r"\s*$", - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched NRE") - # logger.debug (mobj.group(1)) - psivar["NUCLEAR REPULSION ENERGY"] = mobj.group(1) - - # Process DFT dispersion energy (a.u.) - mobj = re.search(r"^\s+" + r"(?:Dispersion correction)" + r"\s+=\s*" + NUMBER + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched Dispersion") - logger.debug(mobj.group(1)) - psivar["DISPERSION CORRECTION ENERGY"] = mobj.group(1) - - # Process DFT (RDFT, RODFT,UDFT, SODFT [SODFT for nwchem versions before nwchem 6.8]) - - mobj = re.search(r"^\s+" + r"(?:Total DFT energy)" + r"\s+=\s*" + NUMBER + r"\s*$", outtext, re.MULTILINE) - if mobj: - logger.debug("matched DFT") - logger.debug(mobj.group(1)) - psivar["DFT TOTAL ENERGY"] = mobj.group(1) - - # SODFT [for nwchem 6.8+] - mobj = re.search( - # fmt: off - r'^\s+' + r'Total SO-DFT energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Nuclear repulsion energy' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched DFT") - # print (mobj.group(1)) - psivar["DFT TOTAL ENERGY"] = mobj.group(1) - psivar["NUCLEAR REPULSION ENERGY"] = mobj.group(2) - - # MCSCF - mobj = re.search( - # fmt: off - r'^\s+' + r'Total MCSCF energy' + r'\s+=\s+' + NUMBER + r'\s*$', - # fmt: off - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched mcscf 2") # MCSCF energy calculation - psivar["MCSCF TOTAL ENERGY"] = mobj.group(1) - - mobj = re.findall( - # fmt: off - r'^\s+' + r'Total SCF energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'One-electron energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Two-electron energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total MCSCF energy' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - - # for mobj_list in mobj: - - if mobj: # Need to change to accommodate find all instances - logger.debug("matched mcscf") # MCSCF energy calculation - psivar["HF TOTAL ENERGY"] = mobj.group(1) - psivar["ONE-ELECTRON ENERGY"] = mobj.group(2) - psivar["TWO-ELECTRON ENERGY"] = mobj.group(3) - psivar["MCSCF TOTAL ENERGY"] = mobj.group(4) - # for mobj_list in mobj: - # for i in mobj_list: - # count += 0 - # logger.debug('matched mcscf iteration %i', count) - # psivar['HF TOTAL ENERGY'] = mobj.group(1) - # psivar['ONE-ELECTRON ENERGY'] = mobj.group(2) - # psivar['TWO-ELECTRON ENERGY'] = mobj.group(3) - # psivar['MCSCF TOTAL ENERGY'] = mobj.group(4) - - # Process MP2 (Restricted, Unrestricted(RO n/a)) - # 1)SCF-MP2 - mobj = re.search( - # fmt: off - r'^\s+' + r'SCF energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Correlation energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Singlet pairs' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Triplet pairs' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total MP2 energy' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) # MP2 - if mobj: - logger.debug("matched scf-mp2") - module = "mp2grad" - psivar["HF TOTAL ENERGY"] = mobj.group(1) - psivar["MP2 CORRELATION ENERGY"] = mobj.group(2) - psivar["MP2 TOTAL ENERGY"] = mobj.group(5) - # SCS-MP2 - mobj = re.search( - # fmt: off - r'^\s+' + r'Same spin pairs' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Same spin scaling factor' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Opposite spin pairs' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Opposite spin scaling fact.' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'SCS-MP2 correlation energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total SCS-MP2 energy' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched scs-mp2", mobj.groups()) - psivar["MP2 SAME-SPIN CORRELATION ENERGY"] = mobj.group(1) - psivar["MP2 OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(3) - - logger.debug(mobj.group(1)) # ess - logger.debug(mobj.group(2)) # fss - logger.debug(mobj.group(3)) # eos - logger.debug(mobj.group(4)) # fos - logger.debug(mobj.group(5)) # scs corl - logger.debug(mobj.group(6)) # scs-mp2 - - # 2) DFT-MP2 - mobj = re.search( - # fmt: off - r'^\s+' + r'DFT energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Unscaled MP2 energy' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total DFT\+MP2 energy' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched dft-mp2") - psivar.pop("DFT TOTAL ENERGY") # remove previously defined DFT energy w/o DH contribution - psivar["DFT FUNCTIONAL TOTAL ENERGY"] = mobj.group(1) - psivar["CURRENT REFERENCE ENERGY"] = mobj.group(1) - psivar["DFT TOTAL ENERGY"] = mobj.group(3) - - # 3) MP2 with CCSD or CCSD(T) calculation (through CCSD(T) directive) - mobj = re.search( - # fmt: off - r'^\s+' + r'MP2 Energy \(coupled cluster initial guess\)' + r'\s*' + - r'^\s+' + r'------------------------------------------' + r'\s*' + - r'^\s+' + r'Reference energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'MP2 Corr\. energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total MP2 energy:' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - logger.debug("matched coupled cluster-mp2") - psivar["MP2 CORRELATION ENERGY"] = mobj.group(2) - psivar["MP2 TOTAL ENERGY"] = mobj.group(3) - - mobj3 = re.search(r"Final RHF results", outtext) - if mobj3: - psivar["MP2 DOUBLES ENERGY"] = mobj.group(2) - - # 4) Direct MP2 - - mobj = re.search( - # fmt: off - r'^\s+' + r'SCF energy' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'^\s+' + r'Correlation energy' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'^\s+' + r'Total MP2 energy' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - mobj2 = re.search(r"Direct MP2", outtext) - if mobj and mobj2: - logger.debug("matched direct-mp2") - module = "directmp2" - psivar["HF TOTAL ENERGY"] = mobj.group("hf") - psivar["MP2 CORRELATION ENERGY"] = mobj.group("mp2corl") - psivar["MP2 TOTAL ENERGY"] = mobj.group("mp2") - # direct-mp2 is RHF only - psivar["MP2 DOUBLES ENERGY"] = mobj.group("mp2corl") - - # 5) RI-MP2 - - # Process calculation through tce [dertype] command - tce_cumm_corl = 0.0 - for cc_name in [r"MBPT\(2\)", r"MBPT\(3\)", r"MBPT\(4\)"]: - mobj = re.search( - # fmt: off - r'^\s+' + cc_name + r'\s+' + r'correlation energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*' + - r'^\s+' + cc_name + r'\s+' + r'total energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - - mobj3 = re.search(r"Wavefunction type : Restricted open-shell Hartree-Fock", outtext, re.MULTILINE) - - if mobj: - mbpt_plain = cc_name.replace("\\", "").replace("MBPT", "MP").replace("(", "").replace(")", "") - logger.debug(f"matched tce mbpt {mbpt_plain}", mobj.groups()) - tce_cumm_corl += float(mobj.group(1)) - - if mbpt_plain == "MP2": - mobj3 = re.search(r"Wavefunction type : Restricted open-shell Hartree-Fock", outtext, re.MULTILINE) - if mobj3: - psivar[f"{mbpt_plain} DOUBLES ENERGY"] = mobj.group(1) - psivar[f"CURRENT CORRELATION ENERGY"] = mobj.group(1) - psivar[f"CURRENT ENERGY"] = Decimal(mobj.group(1)) + psivar[f"HF TOTAL ENERGY"] - else: - psivar[f"{mbpt_plain} DOUBLES ENERGY"] = mobj.group(1) - psivar[f"{mbpt_plain} CORRELATION ENERGY"] = mobj.group(1) - else: - psivar[f"{mbpt_plain} CORRECTION ENERGY"] = mobj.group(1) - if not mobj3 and mbpt_plain not in ["MP4"]: - psivar[f"{mbpt_plain} DOUBLES ENERGY"] = tce_cumm_corl - psivar[f"{mbpt_plain} TOTAL ENERGY"] = mobj.group(2) - module = "tce" - - # TCE dipole- MBPT(n) - mobj2 = re.search( - # fmt: off - r"^\s*" + cc_name + r"\s+" + r"dipole moments \/ hartree & Debye" + r"\s*" + - r"^\s*" + r"-+" + r"\s*" + - r'^\s*' + r'X' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Y' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Z' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Total' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - - if mobj2: - mbpt_plain = cc_name.replace("\\", "").replace("MBPT", "MP").replace("(", "").replace(")", "") - logger.debug(f"matched tce {mbpt_plain} dipole moment") - # only pulling Debye - dipole = np.array([float(mobj2.group(2)), float(mobj2.group(4)), float(mobj2.group(6))]) - psivar[f"{mbpt_plain} DIPOLE"] = dipole - - # TCE with () or [] - for cc_name in [ - r"CCSD\(T\)", - r"CCSD\[T\]", - r"CCSD\(2\)_T", - r"CCSD\(2\)", - r"CCSDT\(2\)_Q", - r"CR-CCSD\[T\]", - r"CR-CCSD\(T\)", - r"LR-CCSD\(T\)", - r"LR-CCSD\(TQ\)-1", - r"CREOMSD\(T\)", - ]: - mobj = re.search( - # fmt: off - r'^\s+' + cc_name + r'\s+' + r'correction energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*' + - r'^\s+' + cc_name + r'\s+' + r'correlation energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*' + - r'^\s+' + cc_name + r'\s+' + r'total energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj: - cc_plain = cc_name.replace("\\", "") - cc_plain.replace("CCSD", "") - logger.debug(f"matched tce cc {cc_plain}") - - if cc_plain == "CCSD[T]": - psivar[f"CCSD+T(CCSD) CORRELATION ENERGY"] = mobj.group(2) - psivar[f"CCSD+T(CCSD) TOTAL ENERGY"] = mobj.group(3) - else: - # psivar[f"{cc_corr} CORRECTION ENERGY"] = mobj.group(1) - psivar[f"{cc_plain} CORRELATION ENERGY"] = mobj.group(2) - psivar[f"{cc_plain} TOTAL ENERGY"] = mobj.group(3) - module = "tce" - - # TCE dipole with () or [] - mobj2 = re.search( - # fmt: off - r"^\s*" + cc_name + r"\s+" + r"dipole moments \/ hartree & Debye" + r"\s*" + - r"^\s*" + r"-+" + r"\s*" + - r'^\s*' + r'X' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Y' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Z' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Total' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - - if mobj2: - cc_plain = cc_name.replace("\\", "") - cc_plain.replace("CCSD", "") - logger.debug(f"matched tce {cc_plain} dipole moment") - - # only pulling Debye - dipole = np.array([float(mobj2.group(2)), float(mobj2.group(4)), float(mobj2.group(6))]) - psivar[f"{cc_plain} DIPOLE"] = dipole - - # Process other TCE cases - for cc_name in [ - r"CISD", - r"QCISD", - r"CISDT", - r"CISDTQ", - r"CCD", - r"CC2", - r"CCSD", - r"CCSDT", - r"CCSDTQ", - r"LCCSD", - r"LCCD", - r"CCSDTA", - ]: - mobj = re.search( - # fmt: off - r'^\s+' + r'Iterations converged' + r'\s*' + - r'^\s+' + cc_name + r'\s+' + r'correlation energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*' + - r'^\s+' + cc_name + r'\s+' + r'total energy / hartree' + r'\s+=\s*' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - - if mobj: - mobj3 = re.search(r"Wavefunction type : Restricted open-shell Hartree-Fock", outtext, re.MULTILINE) - logger.debug(f"matched {cc_name}", mobj.groups()) - if mobj3: - pass - else: - if cc_name in ["MP2", "MP3", "LCCD", "LCCSD", "CCD", "CCSD"]: - psivar[f"{cc_name} DOUBLES ENERGY"] = mobj.group(1) - psivar[f"{cc_name} CORRELATION ENERGY"] = mobj.group(1) - psivar[f"{cc_name} TOTAL ENERGY"] = mobj.group(2) - module = "tce" - - # TCE dipole - mobj2 = re.search( - # fmt: off - r"^\s*" + cc_name + r"\s+" + r"dipole moments \/ hartree & Debye" + r"\s*" + - r"^\s*" + r"-+" + r"\s*" + - r'^\s*' + r'X' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Y' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Z' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*' + - r'^\s*' + r'Total' + r'\s+' + NUMBER + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - if mobj2: - logger.debug(f"matched tce dipole moment") - - # only pulling Debye - dipole = np.array([float(mobj2.group(2)), float(mobj2.group(4)), float(mobj2.group(6))]) - psivar[f"{cc_name} DIPOLE"] = dipole - psivar[f"CURRENT DIPOLE"] = dipole - - # Process CCSD/CCSD(T) using nwchem CCSD/CCSD(T) [dertype] command - - mobj = re.search( - # fmt: off - r'^\s+' + r'-----------' + r'\s*' + - r'^\s+' + r'CCSD Energy' + r'\s*' + - r'^\s+' + r'-----------' + r'\s*' + - r'^\s+' + r'Reference energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'CCSD corr\. energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total CCSD energy:' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - - if mobj: - logger.debug("matched ccsd") - psivar["CCSD CORRELATION ENERGY"] = mobj.group(2) - psivar["CCSD TOTAL ENERGY"] = mobj.group(3) - module = "cc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'T\(CCSD\) corr\. energy:' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*' + - r'^\s+' + r'Total CCSD\+T\(CCSD\) energy:' + r'\s+' + r"(?P" + NUMBER + r")" + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - if mobj: - logger.debug("matched ccsd+t(ccsd)") - psivar["T(CCSD) CORRECTION ENERGY"] = mobj.group("tccsdcorr") - psivar["CCSD+T(CCSD) CORRELATION ENERGY"] = Decimal(mobj.group("tccsdtot")) - psivar["HF TOTAL ENERGY"] - psivar["CCSD+T(CCSD) TOTAL ENERGY"] = mobj.group("tccsdtot") - module = "cc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'--------------' + r'\s*' + - r'^\s+' + r'CCSD\(T\) Energy' + r'\s*' + - r'^\s+' + r'--------------' + r'\s*' + r'(?:.*?)' + - r'^\s+' + r'\(T\) corr\. energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total CCSD\(T\) energy:' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - - if mobj: - logger.debug("matched ccsd(t)") - psivar["(T) CORRECTION ENERGY"] = mobj.group(1) - psivar["CCSD(T) CORRELATION ENERGY"] = Decimal(mobj.group(2)) - psivar["HF TOTAL ENERGY"] - psivar["CCSD(T) TOTAL ENERGY"] = mobj.group(2) - module = "cc" - - mobj = re.search( - # fmt: off - r'^\s+' + r'Spin Component Scaled \(SCS\) CCSD' + r'\s*' + - r'^\s+' + r'-*' + r'\s*' + - r'^\s+' + r'Same spin contribution:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Same spin scaling factor:' + r'\s+' + NUMBER + r'\s*' - r'^\s+' + r'Opposite spin contribution:' + r'\s+' + NUMBER + r'\s*' + - #r'^\s+' + r'Opposite spin scaling factor' + r'\s+' + NUMBER + r'\s*' - r'^\s+' + r'Opposite spin scaling fact.:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'SCS-CCSD correlation energy:' + r'\s+' + NUMBER + r'\s*' + - r'^\s+' + r'Total SCS-CCSD energy:' + r'\s+' + NUMBER + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - # SCS-CCSD included - if mobj: - logger.debug("matched scs-ccsd", mobj.groups()) - psivar["CCSD SAME-SPIN CORRELATION ENERGY"] = mobj.group(1) - psivar["CCSD OPPOSITE-SPIN CORRELATION ENERGY"] = mobj.group(3) - # psivar['CCSD SAME-SPIN CORRELATION ENERGY'] = psivar['SCS-CCSD SAME-SPIN CORRELATION ENERGY'] = ( - # Decimal(mobj.group(1)) * Decimal(mobj.group(2))) - # psivar['CCSD OPPOSITE-SPIN CORRELATION ENERGY'] = psivar['SCS-CCSD OPPOSITE-SPIN CORRELATION ENERGY'] = ( - # Decimal(mobj.group(4)) * Decimal(mobj.group(3))) - # psivar['SCS-CCSD CORRELATION ENERGY'] = mobj.group(5) - # psivar['SCS-CCSD TOTAL ENERGY'] = mobj.group(6) - # psivar['CUSTOM SCS-CCSD CORRELATION ENERGY'] = 0.5 * (float( - # psivar['CCSD SAME-SPIN CORRELATION ENERGY']) + float(psivar['CCSD OPPOSITE-SPIN CORRELATION ENERGY'])) - # psivar['CUSTOM SCS-CCSD TOTAL ENERGY'] = float(mobj.group(6)) + float( - # psivar['CUSTOM SCS-CCSD CORRERLATION ENERGY']) - - # Process EOM-[cc_name] #nwchem_tce_dipole = false - # Parsed information: each symmetry, root excitation energy in eV and total energy in hartree - # psivar name might need to be fixed - # each root excitation energy is extracted from the last iteration of right hand side - mobj = re.findall( - # fmt: off - r'^\s+(?:Excited-state calculation \( )(.*)\s+(?:symmetry\))\s+(?:.*\n)*^\s+EOM-' + cc_name + - # (..) captures symmetry - r'right-hand side iterations\s+(?:.*\n)*(?:Excited state root)\s+' + NUMBER + #root - r'\s*(?:Excitation energy / hartree)\s+.\s+' + NUMBER + #excitation energy hartree - r'\s*(?:/ eV)\s+.\s+' + NUMBER + r'\s*$', - # excitation energy eV - # fmt: on - outtext, - re.MULTILINE | re.DOTALL, - ) - # regex should be more dynamic in finding values, need to revisit - # mobj.group(0) = symmetry value - # mobj.group(1) = cc_name - # mobj.group(2) = root number - # mobj.group(3) = excitation energy (hartree) - # mobj.group(4) = excitation energy (eV) - - if mobj: - logger.debug(mobj) - ext_energy = {} # dic - - ext_energy_list = [] - logger.debug(f"matched eom-{cc_name}") - for mobj_list in mobj: - logger.debug("matched EOM-%s - %s symmetry" % (cc_name, mobj_list[0])) # cc_name, symmetry - logger.debug(mobj_list) - count = 0 - for line in mobj_list[1].splitlines(): - lline = line.split() - logger.debug(lline[1]) # in hartree - logger.debug(lline[2]) # in eV - count += 1 - - logger.debug("matched excitation energy #%d - %s symmetry" % (count, mobj_list[0])) - - ext_energy_list.append(lline[1]) # Collect all excitation energies - - sym = str(mobj_list[0]) - ext_energy.setdefault(sym, []) - ext_energy[sym].append(lline[1]) # Dictionary: symmetries(key), energies(value) - - ext_energy_list.sort(key=float) - - for nroot in range(len(ext_energy_list)): - for k, e_val in ext_energy.items(): - if ext_energy_list[nroot] in e_val: - symm = k - # in hartree - psivar[ - f"EOM-{cc_name} ROOT 0 -> ROOT {nroot + 1} EXCITATION ENERGY - {symm} SYMMETRY" - ] = ext_energy_list[nroot] - psivar[f"EOM-{cc_name} ROOT 0 -> ROOT {nroot + 1} TOTAL ENERGY - {symm} SYMMETRY"] = psivar[ - f"{cc_name} TOTAL ENERGY" - ] + Decimal(ext_energy_list[nroot]) - gssym = "" - gs = re.search(r"^\s+" + r"Ground-state symmetry is" + gssym + r"\s*$", outtext, re.MULTILINE) - - if gs: - logger.debug("matched ground-state symmetry") - psivar["GROUND-STATE SYMMETRY"] = gssym.group(1) - - # Process TDDFT - # 1) Spin allowed - mobj = re.findall( - # fmt: off - r'^\s+(?:Root)\s+(\d+)\s+(.*?)\s+' + NUMBER + r'\s+(?:a\.u\.)\s+' + NUMBER + r"\s+eV\s*" + - r"^\s+" + r"\s+=\s+" + NUMBER + r"\s*" - #Root | symmetry | a.u. | eV - # unkn units for dip/quad - + r'\s+(?:.*\n)\s+Transition Moments\s+X\s+'+ NUMBER + r'\s+Y\s+'+ NUMBER+ r'\s+Z\s+'+ NUMBER #dipole - + r'\s+Transition Moments\s+XX\s+'+ NUMBER + r'\s+XY\s+'+ NUMBER+ r'\s+XZ\s+'+ NUMBER #quadrople - + r'\s+Transition Moments\s+YY\s+'+ NUMBER + r'\s+YZ\s+'+ NUMBER+ r'\s+ZZ\s+'+ NUMBER #quadrople - + r"\s+" + r"Dipole Oscillator Strength" + r"\s+" + NUMBER + r"\s*$", - # fmt: on - outtext, - re.MULTILINE, - ) - - if mobj: - logger.debug("matched TDDFT with transition moments") - for mobj_list in mobj: - logger.debug(mobj_list) - iroot = mobj_list[0] - sym = mobj_list[1] - - # in eV - psivar[f"TDDFT ROOT {iroot} EXCITATION ENERGY - {sym} SYMMETRY"] = mobj_list[2] - psivar[f"TDDFT ROOT {iroot} EXCITED STATE ENERGY - {sym} SYMMETRY"] = psivar[ - "DFT TOTAL ENERGY" - ] + Decimal(mobj_list[2]) - psivar[f"TDDFT ROOT 0 -> ROOT {iroot} DIPOLE"] = [ - float(mobj_list[5]), - float(mobj_list[6]), - float(mobj_list[7]), - ] - psivar[f"TDDFT ROOT 0 -> ROOT {iroot} QUADRUPOLE"] = [ - float(mobj_list[8]), - float(mobj_list[9]), - float(mobj_list[10]), - float(mobj_list[9]), - float(mobj_list[11]), - float(mobj_list[12]), - float(mobj_list[10]), - float(mobj_list[12]), - float(mobj_list[13]), - ] - psivar[f"TDDFT ROOT 0 -> ROOT {iroot} OSCILLATOR STRENGTH (LEN)"] = mobj_list[14] - - # 2) Spin forbidden - mobj = re.findall( - # fmt: off - r'^\s+(?:Root)\s+(\d+)\s+(.*?)\s+' + NUMBER + r'\s(?:a\.u\.)\s+' + NUMBER + r'\s+(?:\w+)' # Root | symmetry | a.u. | eV - + r'\s+(?:.\w+.\s+.\s+\d+.\d+)' # s2 value - + r'\s+Transition Moments\s+(?:Spin forbidden)' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE, - ) - # mobj.group(0) = Root - # mobj.group(1) = symmetry - # mobj.group(2) a.u. - # mobj.group(3) e.V - # mobj.group(4) Excitation energy - # mobj.group(5) Excited state energy - - if mobj: - logger.debug("matched TDDFT - spin forbidden") - for mobj_list in mobj: - #### temporary psivars #### - # in eV - psivar[f"TDDFT ROOT {mobj_list[0]} EXCITATION ENERGY - {mobj_list[2]} SYMMETRY"] = mobj_list[4] - psivar[f"TDDFT ROOT {mobj_list[0]} EXCITED STATE ENERGY - {mobj_list[2]} SYMMETRY"] = psivar[ - "DFT TOTAL ENERGY" - ] + qcel.constants.converstion_factor("eV", "hartree") * Decimal(mobj_list[4]) - - # psivar['TDDFT ROOT %s %s %s EXCITATION ENERGY' % - # (mobj_list[0], mobj_list[1], mobj_list[2])] = mobj_list[3] # in a.u. - # psivar['TDDFT ROOT %s %s %s EXCITED STATE ENERGY' %(mobj_list[0], mobj_list[1], mobj_list[2])] = \ - # psivar['DFT TOTAL ENERGY'] + Decimal(mobj_list[3]) - if mobj: - logger.debug("Non-variation initial energy") # prints out energy, 5 counts - - # Process geometry - # 1) CHARGE - # Read charge from SCF module - mobj = re.search( - r"^\s+" + r"charge =" + r"\s+" + NUMBER + r"\s*$", outtext, re.MULTILINE | re.IGNORECASE - ) - - if mobj: - logger.debug("matched charge") - out_charge = int(float(mobj.group(1))) - - # Read charge from General information (not scf module) - mobj = re.search( - r"^\s+" + r"Charge :" + r"\s+" + r"(-?\d+)" + r"\s*$", outtext, re.MULTILINE | re.IGNORECASE - ) - - if mobj: - logger.debug("matched charge") - out_charge = int(float(mobj.group(1))) - - # 2) MULTIPLICITY - # Read multiplicity from SCF module - mobj = re.search( - r"^\s+" + r"open shells =" + r"\s+" + r"(\d+)" + r"\s*$", outtext, re.MULTILINE | re.IGNORECASE - ) - - calcinfo = False - - if mobj: - logger.debug("matched multiplicity") - out_mult = int(mobj.group(1)) + 1 - - # Read multiplicity from SCF module through alpha, beta electrons - mobj = re.search( - # fmt: off - r'^\s+' + r'alpha electrons =' + r'\s+' + r'(\d+)' + r'\s*' + - r'^\s+' + r'beta electrons =' + r'\s+' + r'(\d+)' + r'\s*$', - # fmt: on - outtext, - re.MULTILINE | re.IGNORECASE, - ) - if mobj: - logger.debug("matched multiplicity via alpha and beta electrons 0") - out_mult = int(mobj.group(1)) - int(mobj.group(2)) + 1 # nopen + 1 - psivar["N ALPHA ELECTRONS"] = mobj.group(1) - psivar["N BETA ELECTRONS"] = mobj.group(2) - - mobj = re.search( - # fmt: off - r"^\s+" + r"Basis functions" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Molecular orbitals" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Frozen core" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Frozen virtuals" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Active alpha occupied" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Active beta occupied" + r"\s+=\s+" + r"(?P\d+)" + r"\s*" + - r"^\s+" + r"Active alpha virtual" + r"\s+=\s+" + r"(?P