symplecta 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.
- symplecta-0.1.0/.github/workflows/ci.yml +52 -0
- symplecta-0.1.0/.github/workflows/release.yml +62 -0
- symplecta-0.1.0/.gitignore +25 -0
- symplecta-0.1.0/LICENSE +21 -0
- symplecta-0.1.0/PKG-INFO +207 -0
- symplecta-0.1.0/README.md +183 -0
- symplecta-0.1.0/docs/constraints.md +312 -0
- symplecta-0.1.0/docs/implicit_midpoint.md +101 -0
- symplecta-0.1.0/docs/introduction.md +151 -0
- symplecta-0.1.0/docs/methods.md +198 -0
- symplecta-0.1.0/pyproject.toml +48 -0
- symplecta-0.1.0/src/symplecta/__init__.py +24 -0
- symplecta-0.1.0/src/symplecta/ivp.py +598 -0
- symplecta-0.1.0/src/symplecta/methods.py +327 -0
- symplecta-0.1.0/tests/helpers.py +109 -0
- symplecta-0.1.0/tests/test_constraints.py +129 -0
- symplecta-0.1.0/tests/test_convergence.py +139 -0
- symplecta-0.1.0/tests/test_driver.py +255 -0
- symplecta-0.1.0/tests/test_energy.py +120 -0
- symplecta-0.1.0/tests/test_import.py +5 -0
- symplecta-0.1.0/tests/test_systems.py +153 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
# the matrix is what actually backs requires-python = ">=3.10"
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest]
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
include:
|
|
19
|
+
- os: windows-latest
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Install
|
|
30
|
+
run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Lint
|
|
33
|
+
run: ruff check .
|
|
34
|
+
|
|
35
|
+
- name: Test
|
|
36
|
+
run: pytest -q
|
|
37
|
+
|
|
38
|
+
build:
|
|
39
|
+
# a release that does not build is worse than one that does not lint
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v7
|
|
43
|
+
|
|
44
|
+
- uses: actions/setup-python@v7
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.12"
|
|
47
|
+
|
|
48
|
+
- name: Build and check the distributions
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip build twine
|
|
51
|
+
python -m build
|
|
52
|
+
python -m twine check --strict dist/*
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishing a GitHub Release goes to the real index. Running this workflow by
|
|
4
|
+
# hand from the Actions tab goes to TestPyPI, so a dry run cannot reach PyPI
|
|
5
|
+
# even by accident.
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v7
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Build and check
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip build twine
|
|
24
|
+
python -m build
|
|
25
|
+
python -m twine check --strict dist/*
|
|
26
|
+
|
|
27
|
+
- uses: actions/upload-artifact@v7
|
|
28
|
+
with:
|
|
29
|
+
name: dist
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
testpypi:
|
|
33
|
+
if: github.event_name == 'workflow_dispatch'
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: testpypi
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # this is what lets PyPI verify the run; no token stored
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/download-artifact@v8
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
|
|
45
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
46
|
+
with:
|
|
47
|
+
repository-url: https://test.pypi.org/legacy/
|
|
48
|
+
|
|
49
|
+
pypi:
|
|
50
|
+
if: github.event_name == 'release'
|
|
51
|
+
needs: build
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: pypi
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v8
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
|
|
62
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# Editors / OS
|
|
22
|
+
.idea/
|
|
23
|
+
.vscode/
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
symplecta-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Román Pérez Santalla
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
symplecta-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: symplecta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Symplectic and geometric integrators for Hamiltonian systems, complementing scipy.integrate.solve_ivp
|
|
5
|
+
Project-URL: Homepage, https://github.com/RomanPerezSantalla/symplecta
|
|
6
|
+
Project-URL: Issues, https://github.com/RomanPerezSantalla/symplecta/issues
|
|
7
|
+
Author: Roman
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: RATTLE,SHAKE,hamiltonian,integrator,leapfrog,ode,symplectic,verlet
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: numpy>=1.24
|
|
19
|
+
Requires-Dist: scipy>=1.10
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# symplecta
|
|
26
|
+
|
|
27
|
+
Fixed-step symplectic and geometric integrators for Hamiltonian systems, in pure NumPy and SciPy.
|
|
28
|
+
|
|
29
|
+
If you have integrated an orbit or a pendulum for a long time with a general-purpose solver, you will have seen
|
|
30
|
+
the energy slowly climb or decay even though there is no driving or dampening. That is not a bug in the
|
|
31
|
+
solver, and it does not go away by taking smaller steps, it is what a method that does not respect the structure
|
|
32
|
+
of Hamilton's equations does. The methods here respect the structure, and their energy error stays in a band instead of
|
|
33
|
+
drifting, however long you run them.
|
|
34
|
+
|
|
35
|
+
`scipy.integrate.solve_ivp` ships only general-purpose adaptive solvers (RK45, RK23, DOP853, Radau, BDF, LSODA)
|
|
36
|
+
and none of them are symplectic. We try to fill that gap while keeping a similar structure to scipy for ease of use.
|
|
37
|
+
|
|
38
|
+
## Status
|
|
39
|
+
|
|
40
|
+
Pre-alpha. All eight methods and `solve_symplectic_ivp` work, and the test suite checks convergence order, energy
|
|
41
|
+
behavior and constraint satisfaction for each of them. The API may still move.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install symplecta
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Using it
|
|
50
|
+
|
|
51
|
+
Describe the Hamiltonian and hand it to the solver. How you describe it decides which methods you can use.
|
|
52
|
+
|
|
53
|
+
For a separable system, $H = T(p) + V(q)$, give a force and a mass:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import numpy as np
|
|
57
|
+
from symplecta import solve_symplectic_ivp
|
|
58
|
+
|
|
59
|
+
res = solve_symplectic_ivp(
|
|
60
|
+
t_span=(0.0, 100.0),
|
|
61
|
+
q0=np.array([1.0]),
|
|
62
|
+
p0=np.array([0.0]),
|
|
63
|
+
force=lambda t, q: -q,
|
|
64
|
+
potential=lambda t, q: 0.5 * q @ q, # optional, only to report energy
|
|
65
|
+
method="verlet",
|
|
66
|
+
h=0.01,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
res.t, res.q, res.p, res.energy
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For a non-separable one, give the two partials of $H$, or just $H$ itself and let the partials be taken
|
|
73
|
+
numerically:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
res = solve_symplectic_ivp(
|
|
77
|
+
t_span=(0.0, 100.0),
|
|
78
|
+
q0=q0,
|
|
79
|
+
p0=p0,
|
|
80
|
+
hamiltonian=lambda t, q, p: 0.5 * np.sum(p**2 * (1 + q**2)) + 0.5 * np.sum(q**2),
|
|
81
|
+
method="implicit_midpoint",
|
|
82
|
+
h=0.01,
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For a constrained system, add the constraint and its Jacobian:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
res = solve_symplectic_ivp(
|
|
90
|
+
t_span=(0.0, 50.0),
|
|
91
|
+
q0=q0,
|
|
92
|
+
p0=p0,
|
|
93
|
+
force=lambda t, q: np.array([0.0, -9.81]),
|
|
94
|
+
constraints={"g": lambda q: np.array([q @ q - 1.0]),
|
|
95
|
+
"jac": lambda q: 2 * q[None, :]},
|
|
96
|
+
method="rattle",
|
|
97
|
+
h=0.005,
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The result is shaped after scipy's, with `.t`, `.q`, `.p`, `.y`, `.energy`, `.success` and `.message`, and uses
|
|
102
|
+
the same `(n_dof, n_points)` array layout, so `res.q[0]` is the trajectory of the first coordinate.
|
|
103
|
+
|
|
104
|
+
## Methods
|
|
105
|
+
|
|
106
|
+
| `method=` | order | needs | cost per step |
|
|
107
|
+
| --- | --- | --- | --- |
|
|
108
|
+
| `symplectic_euler` | 1 | force, mass | 1 force evaluation |
|
|
109
|
+
| `verlet` | 2 | force, mass | 2 force evaluations |
|
|
110
|
+
| `yoshida4` | 4 | force, mass | 3 force evaluations |
|
|
111
|
+
| `yoshida6` | 6 | force, mass | 7 force evaluations |
|
|
112
|
+
| `yoshida8` | 8 | force, mass | 15 force evaluations |
|
|
113
|
+
| `shake` | 2 | force, mass, constraints | 2 force evaluations, one nonlinear solve |
|
|
114
|
+
| `rattle` | 2 | force, mass, constraints | as SHAKE plus one linear solve |
|
|
115
|
+
| `implicit_midpoint` | 2 | dHdq and dHdp, or hamiltonian | several gradient evaluations |
|
|
116
|
+
|
|
117
|
+
`verlet` is the sensible default for a separable system. `yoshida4` costs one more force evaluation and buys
|
|
118
|
+
about three orders of magnitude in the energy band, and `yoshida6` is worth its seven for a long run. Given a
|
|
119
|
+
fixed budget of force evaluations rather than a fixed step, the gains are smaller than that table suggests;
|
|
120
|
+
[the methods document][methods-doc] compares them at equal cost. `rattle` is preferable to `shake` in basically
|
|
121
|
+
every scenario.
|
|
122
|
+
|
|
123
|
+
The solver refuses combinations that would quietly give you the wrong answer, rather than running them.
|
|
124
|
+
Unallowed combinations are: an explicit method on a non-separable Hamiltonian, an unconstrained method on a
|
|
125
|
+
constrained system, or a mass alongside the general form.
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
The documents in [`docs/`][docs-dir] are written to be read in order, and are meant to explain the methods rather
|
|
130
|
+
than just list the API. I wrote them because I found SHAKE and RATTLE genuinely hard to pick up from the original
|
|
131
|
+
papers.
|
|
132
|
+
|
|
133
|
+
- [Introduction][intro-doc] — equations of motion, separable Hamiltonians, and why a regular method
|
|
134
|
+
loses energy while a symplectic one does not.
|
|
135
|
+
- [General methods][methods-doc] — symplectic Euler, velocity Verlet and the Yoshida compositions, all
|
|
136
|
+
built out of drifts and kicks.
|
|
137
|
+
- [Constraint methods][constraints-doc] — Lagrange multipliers from the action, then SHAKE and RATTLE and
|
|
138
|
+
what separates them.
|
|
139
|
+
- [Implicit midpoint][midpoint-doc] — what to do when the Hamiltonian does not split.
|
|
140
|
+
|
|
141
|
+
## Design decisions
|
|
142
|
+
|
|
143
|
+
- **Fixed step only.** Adaptive stepping breaks the fixed-step map that the energy behavior relies on, so there
|
|
144
|
+
is no adaptive control and no plan to add it.
|
|
145
|
+
- **Structure-aware inputs.** The solver takes `q`, `p` and a force rather than a flattened `fun(t, y)`, because
|
|
146
|
+
the methods need the two halves of $H$ separately.
|
|
147
|
+
- **`t_eval` by interpolation.** Output at arbitrary times comes from interpolating the fixed grid afterwards,
|
|
148
|
+
not from dense output per step.
|
|
149
|
+
- **Small and medium systems.** Few-body mechanics and small molecular toys. Force fields, GPU kernels and
|
|
150
|
+
large-scale MD are [OpenMM's](https://openmm.org/) job, not this one.
|
|
151
|
+
|
|
152
|
+
## Further reading
|
|
153
|
+
|
|
154
|
+
The two standard texts, in rough order of how gently they start:
|
|
155
|
+
|
|
156
|
+
- Leimkuhler, B. & Reich, S. (2005). *Simulating Hamiltonian Dynamics*. Cambridge University Press.
|
|
157
|
+
[doi:10.1017/CBO9780511614118](https://doi.org/10.1017/CBO9780511614118)
|
|
158
|
+
- Hairer, E., Lubich, C. & Wanner, G. (2006). *Geometric Numerical Integration*, 2nd ed. Springer.
|
|
159
|
+
[doi:10.1007/3-540-30666-8](https://doi.org/10.1007/3-540-30666-8) — the reference for backward error
|
|
160
|
+
analysis and why the energy error stays bounded.
|
|
161
|
+
- Sanz-Serna, J. M. (1992). Symplectic integrators for Hamiltonian problems: an overview. *Acta Numerica* **1**,
|
|
162
|
+
243–286. [doi:10.1017/S0962492900002282](https://doi.org/10.1017/S0962492900002282) — a shorter survey if a
|
|
163
|
+
whole book is too much.
|
|
164
|
+
|
|
165
|
+
The original papers for the methods here:
|
|
166
|
+
|
|
167
|
+
- Verlet, L. (1967). Computer "experiments" on classical fluids. *Physical Review* **159**, 98–103.
|
|
168
|
+
[doi:10.1103/PhysRev.159.98](https://doi.org/10.1103/PhysRev.159.98)
|
|
169
|
+
- Ryckaert, J.-P., Ciccotti, G. & Berendsen, H. J. C. (1977). Numerical integration of the cartesian equations of
|
|
170
|
+
motion of a system with constraints: molecular dynamics of n-alkanes. *Journal of Computational Physics* **23**,
|
|
171
|
+
327–341. [doi:10.1016/0021-9991(77)90098-5](https://doi.org/10.1016/0021-9991%2877%2990098-5) — SHAKE.
|
|
172
|
+
- Andersen, H. C. (1983). RATTLE: a "velocity" version of the SHAKE algorithm for molecular dynamics
|
|
173
|
+
calculations. *Journal of Computational Physics* **52**, 24–34.
|
|
174
|
+
[doi:10.1016/0021-9991(83)90014-1](https://doi.org/10.1016/0021-9991%2883%2990014-1)
|
|
175
|
+
- Yoshida, H. (1990). Construction of higher order symplectic integrators. *Physics Letters A* **150**, 262–268.
|
|
176
|
+
[doi:10.1016/0375-9601(90)90092-3](https://doi.org/10.1016/0375-9601%2890%2990092-3)
|
|
177
|
+
|
|
178
|
+
## Development
|
|
179
|
+
|
|
180
|
+
Clone the repository, then install it in editable mode with the test and lint tools:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
pip install -e ".[dev]"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pytest
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
ruff check .
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## AI Usage
|
|
195
|
+
AI has been used to design a scaffolding for the project, design tests, correct bugs and revise the documents. Most of
|
|
196
|
+
the code and the text from the docs is original.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
201
|
+
|
|
202
|
+
<!-- PyPI renders the README outside the repo, so these must be absolute -->
|
|
203
|
+
[docs-dir]: https://github.com/RomanPerezSantalla/symplecta/tree/main/docs
|
|
204
|
+
[intro-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/introduction.md
|
|
205
|
+
[methods-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/methods.md
|
|
206
|
+
[constraints-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/constraints.md
|
|
207
|
+
[midpoint-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/implicit_midpoint.md
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# symplecta
|
|
2
|
+
|
|
3
|
+
Fixed-step symplectic and geometric integrators for Hamiltonian systems, in pure NumPy and SciPy.
|
|
4
|
+
|
|
5
|
+
If you have integrated an orbit or a pendulum for a long time with a general-purpose solver, you will have seen
|
|
6
|
+
the energy slowly climb or decay even though there is no driving or dampening. That is not a bug in the
|
|
7
|
+
solver, and it does not go away by taking smaller steps, it is what a method that does not respect the structure
|
|
8
|
+
of Hamilton's equations does. The methods here respect the structure, and their energy error stays in a band instead of
|
|
9
|
+
drifting, however long you run them.
|
|
10
|
+
|
|
11
|
+
`scipy.integrate.solve_ivp` ships only general-purpose adaptive solvers (RK45, RK23, DOP853, Radau, BDF, LSODA)
|
|
12
|
+
and none of them are symplectic. We try to fill that gap while keeping a similar structure to scipy for ease of use.
|
|
13
|
+
|
|
14
|
+
## Status
|
|
15
|
+
|
|
16
|
+
Pre-alpha. All eight methods and `solve_symplectic_ivp` work, and the test suite checks convergence order, energy
|
|
17
|
+
behavior and constraint satisfaction for each of them. The API may still move.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install symplecta
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Using it
|
|
26
|
+
|
|
27
|
+
Describe the Hamiltonian and hand it to the solver. How you describe it decides which methods you can use.
|
|
28
|
+
|
|
29
|
+
For a separable system, $H = T(p) + V(q)$, give a force and a mass:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import numpy as np
|
|
33
|
+
from symplecta import solve_symplectic_ivp
|
|
34
|
+
|
|
35
|
+
res = solve_symplectic_ivp(
|
|
36
|
+
t_span=(0.0, 100.0),
|
|
37
|
+
q0=np.array([1.0]),
|
|
38
|
+
p0=np.array([0.0]),
|
|
39
|
+
force=lambda t, q: -q,
|
|
40
|
+
potential=lambda t, q: 0.5 * q @ q, # optional, only to report energy
|
|
41
|
+
method="verlet",
|
|
42
|
+
h=0.01,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
res.t, res.q, res.p, res.energy
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For a non-separable one, give the two partials of $H$, or just $H$ itself and let the partials be taken
|
|
49
|
+
numerically:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
res = solve_symplectic_ivp(
|
|
53
|
+
t_span=(0.0, 100.0),
|
|
54
|
+
q0=q0,
|
|
55
|
+
p0=p0,
|
|
56
|
+
hamiltonian=lambda t, q, p: 0.5 * np.sum(p**2 * (1 + q**2)) + 0.5 * np.sum(q**2),
|
|
57
|
+
method="implicit_midpoint",
|
|
58
|
+
h=0.01,
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For a constrained system, add the constraint and its Jacobian:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
res = solve_symplectic_ivp(
|
|
66
|
+
t_span=(0.0, 50.0),
|
|
67
|
+
q0=q0,
|
|
68
|
+
p0=p0,
|
|
69
|
+
force=lambda t, q: np.array([0.0, -9.81]),
|
|
70
|
+
constraints={"g": lambda q: np.array([q @ q - 1.0]),
|
|
71
|
+
"jac": lambda q: 2 * q[None, :]},
|
|
72
|
+
method="rattle",
|
|
73
|
+
h=0.005,
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The result is shaped after scipy's, with `.t`, `.q`, `.p`, `.y`, `.energy`, `.success` and `.message`, and uses
|
|
78
|
+
the same `(n_dof, n_points)` array layout, so `res.q[0]` is the trajectory of the first coordinate.
|
|
79
|
+
|
|
80
|
+
## Methods
|
|
81
|
+
|
|
82
|
+
| `method=` | order | needs | cost per step |
|
|
83
|
+
| --- | --- | --- | --- |
|
|
84
|
+
| `symplectic_euler` | 1 | force, mass | 1 force evaluation |
|
|
85
|
+
| `verlet` | 2 | force, mass | 2 force evaluations |
|
|
86
|
+
| `yoshida4` | 4 | force, mass | 3 force evaluations |
|
|
87
|
+
| `yoshida6` | 6 | force, mass | 7 force evaluations |
|
|
88
|
+
| `yoshida8` | 8 | force, mass | 15 force evaluations |
|
|
89
|
+
| `shake` | 2 | force, mass, constraints | 2 force evaluations, one nonlinear solve |
|
|
90
|
+
| `rattle` | 2 | force, mass, constraints | as SHAKE plus one linear solve |
|
|
91
|
+
| `implicit_midpoint` | 2 | dHdq and dHdp, or hamiltonian | several gradient evaluations |
|
|
92
|
+
|
|
93
|
+
`verlet` is the sensible default for a separable system. `yoshida4` costs one more force evaluation and buys
|
|
94
|
+
about three orders of magnitude in the energy band, and `yoshida6` is worth its seven for a long run. Given a
|
|
95
|
+
fixed budget of force evaluations rather than a fixed step, the gains are smaller than that table suggests;
|
|
96
|
+
[the methods document][methods-doc] compares them at equal cost. `rattle` is preferable to `shake` in basically
|
|
97
|
+
every scenario.
|
|
98
|
+
|
|
99
|
+
The solver refuses combinations that would quietly give you the wrong answer, rather than running them.
|
|
100
|
+
Unallowed combinations are: an explicit method on a non-separable Hamiltonian, an unconstrained method on a
|
|
101
|
+
constrained system, or a mass alongside the general form.
|
|
102
|
+
|
|
103
|
+
## Documentation
|
|
104
|
+
|
|
105
|
+
The documents in [`docs/`][docs-dir] are written to be read in order, and are meant to explain the methods rather
|
|
106
|
+
than just list the API. I wrote them because I found SHAKE and RATTLE genuinely hard to pick up from the original
|
|
107
|
+
papers.
|
|
108
|
+
|
|
109
|
+
- [Introduction][intro-doc] — equations of motion, separable Hamiltonians, and why a regular method
|
|
110
|
+
loses energy while a symplectic one does not.
|
|
111
|
+
- [General methods][methods-doc] — symplectic Euler, velocity Verlet and the Yoshida compositions, all
|
|
112
|
+
built out of drifts and kicks.
|
|
113
|
+
- [Constraint methods][constraints-doc] — Lagrange multipliers from the action, then SHAKE and RATTLE and
|
|
114
|
+
what separates them.
|
|
115
|
+
- [Implicit midpoint][midpoint-doc] — what to do when the Hamiltonian does not split.
|
|
116
|
+
|
|
117
|
+
## Design decisions
|
|
118
|
+
|
|
119
|
+
- **Fixed step only.** Adaptive stepping breaks the fixed-step map that the energy behavior relies on, so there
|
|
120
|
+
is no adaptive control and no plan to add it.
|
|
121
|
+
- **Structure-aware inputs.** The solver takes `q`, `p` and a force rather than a flattened `fun(t, y)`, because
|
|
122
|
+
the methods need the two halves of $H$ separately.
|
|
123
|
+
- **`t_eval` by interpolation.** Output at arbitrary times comes from interpolating the fixed grid afterwards,
|
|
124
|
+
not from dense output per step.
|
|
125
|
+
- **Small and medium systems.** Few-body mechanics and small molecular toys. Force fields, GPU kernels and
|
|
126
|
+
large-scale MD are [OpenMM's](https://openmm.org/) job, not this one.
|
|
127
|
+
|
|
128
|
+
## Further reading
|
|
129
|
+
|
|
130
|
+
The two standard texts, in rough order of how gently they start:
|
|
131
|
+
|
|
132
|
+
- Leimkuhler, B. & Reich, S. (2005). *Simulating Hamiltonian Dynamics*. Cambridge University Press.
|
|
133
|
+
[doi:10.1017/CBO9780511614118](https://doi.org/10.1017/CBO9780511614118)
|
|
134
|
+
- Hairer, E., Lubich, C. & Wanner, G. (2006). *Geometric Numerical Integration*, 2nd ed. Springer.
|
|
135
|
+
[doi:10.1007/3-540-30666-8](https://doi.org/10.1007/3-540-30666-8) — the reference for backward error
|
|
136
|
+
analysis and why the energy error stays bounded.
|
|
137
|
+
- Sanz-Serna, J. M. (1992). Symplectic integrators for Hamiltonian problems: an overview. *Acta Numerica* **1**,
|
|
138
|
+
243–286. [doi:10.1017/S0962492900002282](https://doi.org/10.1017/S0962492900002282) — a shorter survey if a
|
|
139
|
+
whole book is too much.
|
|
140
|
+
|
|
141
|
+
The original papers for the methods here:
|
|
142
|
+
|
|
143
|
+
- Verlet, L. (1967). Computer "experiments" on classical fluids. *Physical Review* **159**, 98–103.
|
|
144
|
+
[doi:10.1103/PhysRev.159.98](https://doi.org/10.1103/PhysRev.159.98)
|
|
145
|
+
- Ryckaert, J.-P., Ciccotti, G. & Berendsen, H. J. C. (1977). Numerical integration of the cartesian equations of
|
|
146
|
+
motion of a system with constraints: molecular dynamics of n-alkanes. *Journal of Computational Physics* **23**,
|
|
147
|
+
327–341. [doi:10.1016/0021-9991(77)90098-5](https://doi.org/10.1016/0021-9991%2877%2990098-5) — SHAKE.
|
|
148
|
+
- Andersen, H. C. (1983). RATTLE: a "velocity" version of the SHAKE algorithm for molecular dynamics
|
|
149
|
+
calculations. *Journal of Computational Physics* **52**, 24–34.
|
|
150
|
+
[doi:10.1016/0021-9991(83)90014-1](https://doi.org/10.1016/0021-9991%2883%2990014-1)
|
|
151
|
+
- Yoshida, H. (1990). Construction of higher order symplectic integrators. *Physics Letters A* **150**, 262–268.
|
|
152
|
+
[doi:10.1016/0375-9601(90)90092-3](https://doi.org/10.1016/0375-9601%2890%2990092-3)
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
Clone the repository, then install it in editable mode with the test and lint tools:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pip install -e ".[dev]"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pytest
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
ruff check .
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## AI Usage
|
|
171
|
+
AI has been used to design a scaffolding for the project, design tests, correct bugs and revise the documents. Most of
|
|
172
|
+
the code and the text from the docs is original.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
|
177
|
+
|
|
178
|
+
<!-- PyPI renders the README outside the repo, so these must be absolute -->
|
|
179
|
+
[docs-dir]: https://github.com/RomanPerezSantalla/symplecta/tree/main/docs
|
|
180
|
+
[intro-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/introduction.md
|
|
181
|
+
[methods-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/methods.md
|
|
182
|
+
[constraints-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/constraints.md
|
|
183
|
+
[midpoint-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/implicit_midpoint.md
|