qcopt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcopt-0.1.0/.github/workflows/ci.yaml +23 -0
- qcopt-0.1.0/.github/workflows/publish-to-pypi.yaml +53 -0
- qcopt-0.1.0/.gitignore +175 -0
- qcopt-0.1.0/.pre-commit-config.yaml +62 -0
- qcopt-0.1.0/.python-version +1 -0
- qcopt-0.1.0/AGENTS.md +37 -0
- qcopt-0.1.0/CHANGELOG.md +43 -0
- qcopt-0.1.0/CLAUDE.md +1 -0
- qcopt-0.1.0/LICENSE +21 -0
- qcopt-0.1.0/PKG-INFO +122 -0
- qcopt-0.1.0/README.md +93 -0
- qcopt-0.1.0/benchmarks/README.md +30 -0
- qcopt-0.1.0/benchmarks/batch_demo.py +118 -0
- qcopt-0.1.0/benchmarks/irc_summary.py +61 -0
- qcopt-0.1.0/benchmarks/run_set.py +124 -0
- qcopt-0.1.0/benchmarks/summarize.py +116 -0
- qcopt-0.1.0/benchmarks/ts_bench.py +485 -0
- qcopt-0.1.0/pyproject.toml +92 -0
- qcopt-0.1.0/src/qcopt/__init__.py +28 -0
- qcopt-0.1.0/src/qcopt/algorithms.py +802 -0
- qcopt-0.1.0/src/qcopt/api.py +339 -0
- qcopt-0.1.0/src/qcopt/codec.py +96 -0
- qcopt-0.1.0/src/qcopt/constraints.py +131 -0
- qcopt-0.1.0/src/qcopt/coordinates.py +204 -0
- qcopt-0.1.0/src/qcopt/driver.py +310 -0
- qcopt-0.1.0/src/qcopt/evaluators.py +388 -0
- qcopt-0.1.0/src/qcopt/geometry.py +111 -0
- qcopt-0.1.0/src/qcopt/hessians.py +206 -0
- qcopt-0.1.0/src/qcopt/internal.py +748 -0
- qcopt-0.1.0/src/qcopt/keywords.py +202 -0
- qcopt-0.1.0/src/qcopt/primitives.py +113 -0
- qcopt-0.1.0/src/qcopt/py.typed +0 -0
- qcopt-0.1.0/src/qcopt/space.py +108 -0
- qcopt-0.1.0/src/qcopt/steps.py +219 -0
- qcopt-0.1.0/src/qcopt/vibrations.py +78 -0
- qcopt-0.1.0/tests/__init__.py +0 -0
- qcopt-0.1.0/tests/data/rqm_rxn_0000006257_ts.xyz +16 -0
- qcopt-0.1.0/tests/surfaces.py +38 -0
- qcopt-0.1.0/tests/test_axtb.py +109 -0
- qcopt-0.1.0/tests/test_codec.py +52 -0
- qcopt-0.1.0/tests/test_constraints.py +75 -0
- qcopt-0.1.0/tests/test_core.py +148 -0
- qcopt-0.1.0/tests/test_hessian_program.py +111 -0
- qcopt-0.1.0/tests/test_internal.py +244 -0
- qcopt-0.1.0/tests/test_muller_brown.py +245 -0
- qcopt-0.1.0/typos.toml +2 -0
- qcopt-0.1.0/uv.lock +1361 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: uv sync --all-groups --all-extras
|
|
20
|
+
- run: uv run ruff check .
|
|
21
|
+
- run: uv run ruff format --check .
|
|
22
|
+
- run: uv run mypy
|
|
23
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# The file name must stay publish-to-pypi.yaml: PyPI's trusted publisher for this
|
|
4
|
+
# project is registered against it.
|
|
5
|
+
# Pushing a version tag (e.g. 0.16.0) on main publishes to PyPI and creates the
|
|
6
|
+
# GitHub release. Publishing waits for approval on the protected `pypi` environment.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["*"]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
name: Build and publish to PyPI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/project/qcopt/
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
id-token: write # trusted publishing
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
- uses: astral-sh/setup-uv@v7
|
|
24
|
+
- name: Tag must match the package version
|
|
25
|
+
run: |
|
|
26
|
+
version="$(sed -n 's/^version *= *"\(.*\)"/\1/p' pyproject.toml | head -n1)"
|
|
27
|
+
if [ "$version" != "$GITHUB_REF_NAME" ]; then
|
|
28
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match pyproject version $version."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
- name: Build
|
|
32
|
+
run: uv build
|
|
33
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
34
|
+
|
|
35
|
+
github-release:
|
|
36
|
+
name: Create GitHub release
|
|
37
|
+
needs: publish
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
permissions:
|
|
40
|
+
contents: write
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v7
|
|
43
|
+
- name: Extract release notes from CHANGELOG.md
|
|
44
|
+
run: |
|
|
45
|
+
awk -v v="$GITHUB_REF_NAME" '
|
|
46
|
+
$0 ~ "^## \\[" v "\\]" {f=1; next}
|
|
47
|
+
f && /^## \[/ {exit}
|
|
48
|
+
f {print}
|
|
49
|
+
' CHANGELOG.md > RELEASE_NOTES.md
|
|
50
|
+
- name: Create release
|
|
51
|
+
env:
|
|
52
|
+
GH_TOKEN: ${{ github.token }}
|
|
53
|
+
run: gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-file RELEASE_NOTES.md --verify-tag
|
qcopt-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
|
|
112
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
113
|
+
__pypackages__/
|
|
114
|
+
|
|
115
|
+
# Celery stuff
|
|
116
|
+
celerybeat-schedule
|
|
117
|
+
celerybeat.pid
|
|
118
|
+
|
|
119
|
+
# SageMath parsed files
|
|
120
|
+
*.sage.py
|
|
121
|
+
|
|
122
|
+
# Environments
|
|
123
|
+
.env
|
|
124
|
+
.venv
|
|
125
|
+
env/
|
|
126
|
+
venv/
|
|
127
|
+
ENV/
|
|
128
|
+
env.bak/
|
|
129
|
+
venv.bak/
|
|
130
|
+
|
|
131
|
+
# Spyder project settings
|
|
132
|
+
.spyderproject
|
|
133
|
+
.spyproject
|
|
134
|
+
|
|
135
|
+
# Rope project settings
|
|
136
|
+
.ropeproject
|
|
137
|
+
|
|
138
|
+
# mkdocs documentation
|
|
139
|
+
/site
|
|
140
|
+
|
|
141
|
+
# mypy
|
|
142
|
+
.mypy_cache/
|
|
143
|
+
.dmypy.json
|
|
144
|
+
dmypy.json
|
|
145
|
+
|
|
146
|
+
# Pyre type checker
|
|
147
|
+
.pyre/
|
|
148
|
+
|
|
149
|
+
# pytype static type analyzer
|
|
150
|
+
.pytype/
|
|
151
|
+
|
|
152
|
+
# Cython debug symbols
|
|
153
|
+
cython_debug/
|
|
154
|
+
|
|
155
|
+
# PyCharm
|
|
156
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
157
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
158
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
159
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
160
|
+
#.idea/
|
|
161
|
+
|
|
162
|
+
# Ignore all .xyz files
|
|
163
|
+
*.xyz
|
|
164
|
+
|
|
165
|
+
# Allow .xyz files in tests/data and its subdirectories
|
|
166
|
+
!tests/data/**/*.xyz
|
|
167
|
+
|
|
168
|
+
# rdkit artifacts
|
|
169
|
+
nul
|
|
170
|
+
run.out
|
|
171
|
+
|
|
172
|
+
# Convention for testing scripts
|
|
173
|
+
fake*.py
|
|
174
|
+
# benchmark run logs
|
|
175
|
+
benchmarks/*.out
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Every hook runs the project's own locked tools through `uv run`, so local hooks,
|
|
2
|
+
# CI, and your editor use identical versions. Install once with
|
|
3
|
+
# `uv run pre-commit install`; see CONTRIBUTING.md.
|
|
4
|
+
default_install_hook_types: [pre-commit]
|
|
5
|
+
default_stages: [pre-commit]
|
|
6
|
+
repos:
|
|
7
|
+
- repo: local
|
|
8
|
+
hooks:
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
name: check yaml
|
|
11
|
+
entry: uv run check-yaml
|
|
12
|
+
language: system
|
|
13
|
+
types: [yaml]
|
|
14
|
+
exclude: ^mkdocs\.yml$
|
|
15
|
+
- id: check-toml
|
|
16
|
+
name: check toml
|
|
17
|
+
entry: uv run check-toml
|
|
18
|
+
language: system
|
|
19
|
+
types: [toml]
|
|
20
|
+
- id: check-merge-conflict
|
|
21
|
+
name: check for merge conflicts
|
|
22
|
+
entry: uv run check-merge-conflict
|
|
23
|
+
language: system
|
|
24
|
+
types: [text]
|
|
25
|
+
- id: check-added-large-files
|
|
26
|
+
name: check for added large files
|
|
27
|
+
entry: uv run check-added-large-files --maxkb=250
|
|
28
|
+
language: system
|
|
29
|
+
exclude: ^(tests/data/|docs/)
|
|
30
|
+
- id: detect-secrets
|
|
31
|
+
name: detect secrets
|
|
32
|
+
entry: uv run detect-secrets-hook
|
|
33
|
+
language: system
|
|
34
|
+
types: [text]
|
|
35
|
+
exclude: (\.lock|\.ipynb)$|^tests/data/
|
|
36
|
+
- id: uv-lock
|
|
37
|
+
name: uv.lock is up to date
|
|
38
|
+
entry: uv lock --check
|
|
39
|
+
language: system
|
|
40
|
+
files: ^(pyproject\.toml|uv\.lock)$
|
|
41
|
+
pass_filenames: false
|
|
42
|
+
- id: ruff-check
|
|
43
|
+
name: ruff check
|
|
44
|
+
entry: uv run ruff check --fix --exit-non-zero-on-fix
|
|
45
|
+
language: system
|
|
46
|
+
types_or: [python, pyi]
|
|
47
|
+
- id: ruff-format
|
|
48
|
+
name: ruff format
|
|
49
|
+
entry: uv run ruff format
|
|
50
|
+
language: system
|
|
51
|
+
types_or: [python, pyi]
|
|
52
|
+
- id: typos
|
|
53
|
+
name: typos
|
|
54
|
+
entry: uv run typos --force-exclude
|
|
55
|
+
language: system
|
|
56
|
+
types: [text]
|
|
57
|
+
- id: mypy
|
|
58
|
+
name: mypy
|
|
59
|
+
entry: uv run mypy
|
|
60
|
+
language: system
|
|
61
|
+
types_or: [python, pyi]
|
|
62
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
qcopt-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for AI coding agents (and a quick reference for people) working in `qcopt`.
|
|
4
|
+
|
|
5
|
+
## This package
|
|
6
|
+
|
|
7
|
+
`qcopt` optimizes structures: minima, transition states, and IRCs. It is AFH's own optimizer, meant to replace Sella, geomeTRIC, and ASE's optimizers in our stack. Inputs are qcdata `ProgramInput`s with `program="qcopt"` and gradient (and optionally Hessian) subprograms; outputs are qcdata `ProgramOutput`s holding `OptimizationData` or `PathData`.
|
|
8
|
+
|
|
9
|
+
- **Algorithms are generators** (`qcopt/algorithms.py`): they yield the geometries where they need gradients or Hessians and receive the answers. `qcopt.driver.drive` runs many of them in lockstep and hands each round's requests to an evaluator in one call. Never call a program from inside an algorithm.
|
|
10
|
+
- **Evaluators** (`qcopt/evaluators.py`) compute requests: axtb in-process (our own codes come first: no subprocess, wavefunction reuse, threads), qccompute for every other program, or a Python function.
|
|
11
|
+
- **Atomic units everywhere** (bohr, Hartree); qcref constants as `from qcref import constants as c` and `c.NAME`, never copied into module variables.
|
|
12
|
+
- **Fail loudly**: unknown keywords are errors (pydantic models in `qcopt/keywords.py`). A run that fails or does not converge returns `success=False` with its frames kept.
|
|
13
|
+
- **Our own implementation.** Sella (LGPL-3.0), geomeTRIC (BSD-3 with a non-AI clause), OptKing (BSD-3) and pysisyphus may be read as guides; implement from the papers and cite them in docstrings. Never copy their code.
|
|
14
|
+
|
|
15
|
+
## The qc* suite
|
|
16
|
+
|
|
17
|
+
Dependencies point one way: qcref -> qcdata -> {qcinf, qccodec} -> qccompute -> qcopt. qccompute is optional (only the `ComputeEvaluator` needs it).
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
uv sync --all-groups # install
|
|
23
|
+
uv pip install <axtb wheel> # axtb is not on PyPI yet; its tests skip without it
|
|
24
|
+
uv run pytest # tests
|
|
25
|
+
uv run ruff check --fix . && uv run ruff format .
|
|
26
|
+
uv run mypy
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Benchmarks against Sella and geomeTRIC live in `benchmarks/` with their own venv (see `benchmarks/README.md`). Benchmark data and results stay outside the repo.
|
|
30
|
+
|
|
31
|
+
## Code conventions
|
|
32
|
+
|
|
33
|
+
- Type annotations on all functions; Google-style docstrings with units.
|
|
34
|
+
- Performance matters: with axtb a gradient can take under a millisecond, so the optimizer's own time per step is part of the result. Profile before optimizing; keep per-step work vectorized NumPy.
|
|
35
|
+
- New behavior has tests; bug fixes have a test that failed before the fix.
|
|
36
|
+
- User-visible changes go under `## [unreleased]` in CHANGELOG.md.
|
|
37
|
+
- Do not commit, push, tag, or release unless asked to.
|
qcopt-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- First version: minimizations (RS-RFO), transition-state searches (RS-P-RFO with
|
|
14
|
+
TS-BFGS updates) and IRCs (local quadratic approximation, both branches together) in
|
|
15
|
+
Cartesian coordinates, as `ProgramInput(program="qcopt")` -> `ProgramOutput` with
|
|
16
|
+
`OptimizationData` or `PathData`.
|
|
17
|
+
- Lockstep runs of many calculations with one evaluator call per round (`qcopt.run(list)`).
|
|
18
|
+
- Evaluators: axtb in-process, qccompute (any program with a codec), Python functions;
|
|
19
|
+
finite-difference Hessians from gradients computed together.
|
|
20
|
+
- Lindh model Hessian; BFGS, SR1, PSB, Bofill and TS-BFGS updates; harmonic frequency
|
|
21
|
+
check of the final structure.
|
|
22
|
+
- Internal coordinates, the default: TRIC (per-fragment translation and rotation, with
|
|
23
|
+
analytic rotation derivatives) and delocalized internals; rebuilt when they degrade,
|
|
24
|
+
Cartesians when they cannot describe the structure.
|
|
25
|
+
- Constraints on distances, angles and dihedrals; saddle escape for minimizations
|
|
26
|
+
(`escape_saddles`).
|
|
27
|
+
- A qccodec codec, so `qccompute.compute(ProgramInput(program="qcopt", ...))` works.
|
|
28
|
+
- IRCs by Gonzalez-Schlegel steps on the quadratic model (default, step 0.3) or the
|
|
29
|
+
local quadratic approximation; overshoots retried at half the step; an exact Hessian
|
|
30
|
+
decides when a small-gradient branch shows negative curvature; `minimize_ends`.
|
|
31
|
+
- Internal-coordinate values and B matrices are cached per geometry, so a TRIC step
|
|
32
|
+
computes each once (8.0 -> 4.6 ms per step on a 16-atom minimization).
|
|
33
|
+
- Hessians from another method than the gradients (`hessian=` a different program or
|
|
34
|
+
model, such as GFN2-xTB Hessians for g-xTB): every Hessian request also gets the
|
|
35
|
+
gradient program's energy and gradient (reused when just computed), and frames keep
|
|
36
|
+
only the gradient program's results. On 100 Reaction-QM transition states at g-xTB,
|
|
37
|
+
GFN2 starting Hessians halved the total time.
|
|
38
|
+
- The starting-Hessian keyword is `initial_hessian`; `hessian=` in `minimize`,
|
|
39
|
+
`find_ts` and `irc` names the Hessian program only, and other values are errors
|
|
40
|
+
(a string was ignored).
|
|
41
|
+
|
|
42
|
+
[unreleased]: https://github.com/atomsforhumanity/qcopt/compare/0.1.0...HEAD
|
|
43
|
+
[0.1.0]: https://github.com/atomsforhumanity/qcopt/releases/tag/0.1.0
|
qcopt-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
qcopt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Colton Hicks
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
qcopt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: qcopt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Geometry optimization, transition-state searches, and IRCs that return qcdata results.
|
|
5
|
+
Project-URL: Homepage, https://github.com/atomsforhumanity/qcopt
|
|
6
|
+
Project-URL: Source, https://github.com/atomsforhumanity/qcopt
|
|
7
|
+
Project-URL: Changelog, https://github.com/atomsforhumanity/qcopt/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/atomsforhumanity/qcopt/issues
|
|
9
|
+
Author-email: Colton Hicks <github@coltonhicks.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: numpy>=1.24.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Requires-Dist: qcdata>=0.23.1
|
|
24
|
+
Requires-Dist: qcref>=0.5.1
|
|
25
|
+
Provides-Extra: qccompute
|
|
26
|
+
Requires-Dist: qccodec>=0.21.0; extra == 'qccompute'
|
|
27
|
+
Requires-Dist: qccompute>=0.22.0; extra == 'qccompute'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# qcopt
|
|
31
|
+
|
|
32
|
+
Minimizations, transition-state searches, and IRCs for the qc* stack. Inputs and outputs
|
|
33
|
+
are [qcdata](https://github.com/atomsforhumanity/qcdata) objects; gradients and Hessians
|
|
34
|
+
come from [axtb](https://github.com/atomsforhumanity/axtb) in-process or from any program
|
|
35
|
+
qccompute runs.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import qcopt
|
|
39
|
+
from qcdata import Structure
|
|
40
|
+
|
|
41
|
+
# accuracy 0.01: at axtb's default SCF accuracy, g-xTB gradients carry ~1e-5 Eh/bohr
|
|
42
|
+
# of noise, the size of qcopt's convergence threshold (12% more time per gradient).
|
|
43
|
+
gxtb = {"program": "axtb", "model": {"method": "gxtb-v2.0.1"}, "keywords": {"accuracy": 0.01}}
|
|
44
|
+
guess = Structure.open("ts_guess.xyz")
|
|
45
|
+
|
|
46
|
+
ts = qcopt.find_ts(guess, gxtb, hessian=True, frequencies=True) # ProgramOutput
|
|
47
|
+
ts.results.extras["n_imaginary"] # 1
|
|
48
|
+
path = qcopt.irc(ts.results.final_structure, gxtb, hessian=True) # PathData
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`hessian` names the program computing Hessians: `True` for the gradient program, or another
|
|
52
|
+
one, often a cheaper method. Frames keep the gradient program's energies and gradients:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
gfn2 = {"program": "axtb", "model": {"method": "gfn2"}, "keywords": {"accuracy": 0.01}}
|
|
56
|
+
ts = qcopt.find_ts(guess, gxtb, hessian=gfn2) # g-xTB saddle from a GFN2 starting Hessian
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Keyword arguments are qcopt's keywords (`qcopt.keywords`), such as `maxiter`,
|
|
60
|
+
`initial_hessian` (`"model"`, `"exact"`, ...) or `hessian_every`.
|
|
61
|
+
|
|
62
|
+
Or as a qcdata input, alone or many at once (they advance together, and each round's
|
|
63
|
+
gradients from all of them are computed in one call):
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from qcdata import ProgramInput, ProgramSpec
|
|
67
|
+
|
|
68
|
+
inp = ProgramInput(
|
|
69
|
+
program="qcopt",
|
|
70
|
+
calctype="transition_state",
|
|
71
|
+
structure=guess,
|
|
72
|
+
keywords={"maxiter": 300},
|
|
73
|
+
subprograms=[
|
|
74
|
+
ProgramSpec(program="axtb", calctype="gradient", model={"method": "gxtb-v2.0.1"},
|
|
75
|
+
keywords={"accuracy": 0.01}),
|
|
76
|
+
ProgramSpec(program="axtb", calctype="hessian", model={"method": "gxtb-v2.0.1"},
|
|
77
|
+
keywords={"accuracy": 0.01}),
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
out = qcopt.run(inp) # or qcopt.run([inp1, inp2, ...])
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## What it does
|
|
84
|
+
|
|
85
|
+
| Calculation | Method |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `optimization` | RS-RFO in TRIC coordinates (Wang and Song, 2016: each molecule's internal coordinates plus its translation and rotation), or delocalized internals, or Cartesians; Lindh model Hessian or the program's exact one; BFGS updates (Bofill while the Hessian is indefinite); optional frequency check that steps off saddle points |
|
|
88
|
+
| `transition_state` | RS-P-RFO climbing the lowest mode (or one following a guide direction); exact (or a cheaper method's) or finite-difference starting Hessian; TS-BFGS updates; recomputed when the negative mode is lost |
|
|
89
|
+
| `irc` | Mass-weighted steepest descent by Gonzalez-Schlegel steps (or the local quadratic approximation), both branches together, frames in the input's frame; overshoots retried at half the step; optionally both ends minimized together (`minimize_ends`) |
|
|
90
|
+
|
|
91
|
+
Distances, angles and dihedrals can be held at target values (`constraints`), atoms
|
|
92
|
+
frozen (`frozen_atoms`). Every frame is a qcdata `Frame` with the energy and gradient
|
|
93
|
+
computed there.
|
|
94
|
+
|
|
95
|
+
With qccompute installed, qcopt is also a program it runs:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from qccompute import compute
|
|
99
|
+
out = compute(inp) # the ProgramInput above
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Benchmarks
|
|
103
|
+
|
|
104
|
+
`benchmarks/` compares qcopt with Sella, geomeTRIC and OptKing on the same axtb
|
|
105
|
+
gradients; results are summarized in AFH's findings.
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
pip install qcopt # or: pip install "qcopt[qccompute]" for any program qccompute runs
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
axtb (in-process g-xTB and GFN2-xTB with analytic Hessians) is installed from its
|
|
114
|
+
[repository](https://github.com/atomsforhumanity/axtb) until it is on PyPI.
|
|
115
|
+
|
|
116
|
+
## Status
|
|
117
|
+
|
|
118
|
+
0.1: the API may still change between minor versions.
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT.
|
qcopt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# qcopt
|
|
2
|
+
|
|
3
|
+
Minimizations, transition-state searches, and IRCs for the qc* stack. Inputs and outputs
|
|
4
|
+
are [qcdata](https://github.com/atomsforhumanity/qcdata) objects; gradients and Hessians
|
|
5
|
+
come from [axtb](https://github.com/atomsforhumanity/axtb) in-process or from any program
|
|
6
|
+
qccompute runs.
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import qcopt
|
|
10
|
+
from qcdata import Structure
|
|
11
|
+
|
|
12
|
+
# accuracy 0.01: at axtb's default SCF accuracy, g-xTB gradients carry ~1e-5 Eh/bohr
|
|
13
|
+
# of noise, the size of qcopt's convergence threshold (12% more time per gradient).
|
|
14
|
+
gxtb = {"program": "axtb", "model": {"method": "gxtb-v2.0.1"}, "keywords": {"accuracy": 0.01}}
|
|
15
|
+
guess = Structure.open("ts_guess.xyz")
|
|
16
|
+
|
|
17
|
+
ts = qcopt.find_ts(guess, gxtb, hessian=True, frequencies=True) # ProgramOutput
|
|
18
|
+
ts.results.extras["n_imaginary"] # 1
|
|
19
|
+
path = qcopt.irc(ts.results.final_structure, gxtb, hessian=True) # PathData
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`hessian` names the program computing Hessians: `True` for the gradient program, or another
|
|
23
|
+
one, often a cheaper method. Frames keep the gradient program's energies and gradients:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
gfn2 = {"program": "axtb", "model": {"method": "gfn2"}, "keywords": {"accuracy": 0.01}}
|
|
27
|
+
ts = qcopt.find_ts(guess, gxtb, hessian=gfn2) # g-xTB saddle from a GFN2 starting Hessian
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Keyword arguments are qcopt's keywords (`qcopt.keywords`), such as `maxiter`,
|
|
31
|
+
`initial_hessian` (`"model"`, `"exact"`, ...) or `hessian_every`.
|
|
32
|
+
|
|
33
|
+
Or as a qcdata input, alone or many at once (they advance together, and each round's
|
|
34
|
+
gradients from all of them are computed in one call):
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from qcdata import ProgramInput, ProgramSpec
|
|
38
|
+
|
|
39
|
+
inp = ProgramInput(
|
|
40
|
+
program="qcopt",
|
|
41
|
+
calctype="transition_state",
|
|
42
|
+
structure=guess,
|
|
43
|
+
keywords={"maxiter": 300},
|
|
44
|
+
subprograms=[
|
|
45
|
+
ProgramSpec(program="axtb", calctype="gradient", model={"method": "gxtb-v2.0.1"},
|
|
46
|
+
keywords={"accuracy": 0.01}),
|
|
47
|
+
ProgramSpec(program="axtb", calctype="hessian", model={"method": "gxtb-v2.0.1"},
|
|
48
|
+
keywords={"accuracy": 0.01}),
|
|
49
|
+
],
|
|
50
|
+
)
|
|
51
|
+
out = qcopt.run(inp) # or qcopt.run([inp1, inp2, ...])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What it does
|
|
55
|
+
|
|
56
|
+
| Calculation | Method |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `optimization` | RS-RFO in TRIC coordinates (Wang and Song, 2016: each molecule's internal coordinates plus its translation and rotation), or delocalized internals, or Cartesians; Lindh model Hessian or the program's exact one; BFGS updates (Bofill while the Hessian is indefinite); optional frequency check that steps off saddle points |
|
|
59
|
+
| `transition_state` | RS-P-RFO climbing the lowest mode (or one following a guide direction); exact (or a cheaper method's) or finite-difference starting Hessian; TS-BFGS updates; recomputed when the negative mode is lost |
|
|
60
|
+
| `irc` | Mass-weighted steepest descent by Gonzalez-Schlegel steps (or the local quadratic approximation), both branches together, frames in the input's frame; overshoots retried at half the step; optionally both ends minimized together (`minimize_ends`) |
|
|
61
|
+
|
|
62
|
+
Distances, angles and dihedrals can be held at target values (`constraints`), atoms
|
|
63
|
+
frozen (`frozen_atoms`). Every frame is a qcdata `Frame` with the energy and gradient
|
|
64
|
+
computed there.
|
|
65
|
+
|
|
66
|
+
With qccompute installed, qcopt is also a program it runs:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from qccompute import compute
|
|
70
|
+
out = compute(inp) # the ProgramInput above
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Benchmarks
|
|
74
|
+
|
|
75
|
+
`benchmarks/` compares qcopt with Sella, geomeTRIC and OptKing on the same axtb
|
|
76
|
+
gradients; results are summarized in AFH's findings.
|
|
77
|
+
|
|
78
|
+
## Install
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
pip install qcopt # or: pip install "qcopt[qccompute]" for any program qccompute runs
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
axtb (in-process g-xTB and GFN2-xTB with analytic Hessians) is installed from its
|
|
85
|
+
[repository](https://github.com/atomsforhumanity/axtb) until it is on PyPI.
|
|
86
|
+
|
|
87
|
+
## Status
|
|
88
|
+
|
|
89
|
+
0.1: the API may still change between minor versions.
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
Scripts that compare qcopt with Sella and geomeTRIC on the same gradients (axtb,
|
|
4
|
+
in-process, one thread per run). They need their own environment:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
cd benchmarks
|
|
8
|
+
uv venv -p 3.12 .venv
|
|
9
|
+
VIRTUAL_ENV=.venv uv pip install -e .. <axtb wheel> sella==2.6.0 ase==3.29.0 geometric==1.1.1 networkx
|
|
10
|
+
VIRTUAL_ENV=.venv uv pip install "optking @ git+https://github.com/psi-rking/optking.git@0.5.0"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
axtb runs at SCF accuracy 0.01 (`--accuracy`, passed explicitly): at axtb's default (1.0) g-xTB
|
|
14
|
+
gradients carry ~1e-5 Eh/bohr of noise, the size of the 0.001 eV/A convergence threshold. Rounds
|
|
15
|
+
before 2026-11-06 evening ran at the default.
|
|
16
|
+
|
|
17
|
+
Pin BLAS to one thread before numpy loads (the scripts do), or small eigendecompositions run
|
|
18
|
+
~100x slower next to axtb's threads. Freeze a copy of these scripts and of the code under test
|
|
19
|
+
for each round: every job imports them when it starts.
|
|
20
|
+
|
|
21
|
+
- `ts_bench.py OPTIMIZER XYZ OUT.json`: one optimization, counted and checked with a fresh
|
|
22
|
+
analytic Hessian (frequencies, energy, final gradient).
|
|
23
|
+
- `run_set.py OUTDIR --xyz-glob ...`: many, in parallel processes (`--task ts|min|irc`,
|
|
24
|
+
`--extra OPT=JSON`, `--tag OPT=NAME`, `--pairs` for IRC connectivity checks).
|
|
25
|
+
- `summarize.py RESULTS`: minima and transition states; `irc_summary.py RESULTS`: IRCs, with the
|
|
26
|
+
cost of minimizing their ends.
|
|
27
|
+
- OptKing runs through its own driver (`optking.optimize.optimize`) with a computer that calls
|
|
28
|
+
axtb: its step-by-step helper breaks when it rebuilds its coordinates.
|
|
29
|
+
|
|
30
|
+
Data (Reaction-QM structures, Jan's F0001 tables) and results live outside the repo.
|