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.
Files changed (47) hide show
  1. qcopt-0.1.0/.github/workflows/ci.yaml +23 -0
  2. qcopt-0.1.0/.github/workflows/publish-to-pypi.yaml +53 -0
  3. qcopt-0.1.0/.gitignore +175 -0
  4. qcopt-0.1.0/.pre-commit-config.yaml +62 -0
  5. qcopt-0.1.0/.python-version +1 -0
  6. qcopt-0.1.0/AGENTS.md +37 -0
  7. qcopt-0.1.0/CHANGELOG.md +43 -0
  8. qcopt-0.1.0/CLAUDE.md +1 -0
  9. qcopt-0.1.0/LICENSE +21 -0
  10. qcopt-0.1.0/PKG-INFO +122 -0
  11. qcopt-0.1.0/README.md +93 -0
  12. qcopt-0.1.0/benchmarks/README.md +30 -0
  13. qcopt-0.1.0/benchmarks/batch_demo.py +118 -0
  14. qcopt-0.1.0/benchmarks/irc_summary.py +61 -0
  15. qcopt-0.1.0/benchmarks/run_set.py +124 -0
  16. qcopt-0.1.0/benchmarks/summarize.py +116 -0
  17. qcopt-0.1.0/benchmarks/ts_bench.py +485 -0
  18. qcopt-0.1.0/pyproject.toml +92 -0
  19. qcopt-0.1.0/src/qcopt/__init__.py +28 -0
  20. qcopt-0.1.0/src/qcopt/algorithms.py +802 -0
  21. qcopt-0.1.0/src/qcopt/api.py +339 -0
  22. qcopt-0.1.0/src/qcopt/codec.py +96 -0
  23. qcopt-0.1.0/src/qcopt/constraints.py +131 -0
  24. qcopt-0.1.0/src/qcopt/coordinates.py +204 -0
  25. qcopt-0.1.0/src/qcopt/driver.py +310 -0
  26. qcopt-0.1.0/src/qcopt/evaluators.py +388 -0
  27. qcopt-0.1.0/src/qcopt/geometry.py +111 -0
  28. qcopt-0.1.0/src/qcopt/hessians.py +206 -0
  29. qcopt-0.1.0/src/qcopt/internal.py +748 -0
  30. qcopt-0.1.0/src/qcopt/keywords.py +202 -0
  31. qcopt-0.1.0/src/qcopt/primitives.py +113 -0
  32. qcopt-0.1.0/src/qcopt/py.typed +0 -0
  33. qcopt-0.1.0/src/qcopt/space.py +108 -0
  34. qcopt-0.1.0/src/qcopt/steps.py +219 -0
  35. qcopt-0.1.0/src/qcopt/vibrations.py +78 -0
  36. qcopt-0.1.0/tests/__init__.py +0 -0
  37. qcopt-0.1.0/tests/data/rqm_rxn_0000006257_ts.xyz +16 -0
  38. qcopt-0.1.0/tests/surfaces.py +38 -0
  39. qcopt-0.1.0/tests/test_axtb.py +109 -0
  40. qcopt-0.1.0/tests/test_codec.py +52 -0
  41. qcopt-0.1.0/tests/test_constraints.py +75 -0
  42. qcopt-0.1.0/tests/test_core.py +148 -0
  43. qcopt-0.1.0/tests/test_hessian_program.py +111 -0
  44. qcopt-0.1.0/tests/test_internal.py +244 -0
  45. qcopt-0.1.0/tests/test_muller_brown.py +245 -0
  46. qcopt-0.1.0/typos.toml +2 -0
  47. 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.
@@ -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.