quantum-playground 0.3.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.
- quantum_playground-0.3.0/LICENSE +21 -0
- quantum_playground-0.3.0/PKG-INFO +184 -0
- quantum_playground-0.3.0/README.md +156 -0
- quantum_playground-0.3.0/pyproject.toml +43 -0
- quantum_playground-0.3.0/setup.cfg +4 -0
- quantum_playground-0.3.0/src/qplay/__init__.py +29 -0
- quantum_playground-0.3.0/src/qplay/__main__.py +58 -0
- quantum_playground-0.3.0/src/qplay/animations.py +172 -0
- quantum_playground-0.3.0/src/qplay/atomic.py +86 -0
- quantum_playground-0.3.0/src/qplay/chaos.py +83 -0
- quantum_playground-0.3.0/src/qplay/circuits.py +75 -0
- quantum_playground-0.3.0/src/qplay/complexplane.py +41 -0
- quantum_playground-0.3.0/src/qplay/demos.py +464 -0
- quantum_playground-0.3.0/src/qplay/dynamics.py +71 -0
- quantum_playground-0.3.0/src/qplay/em.py +80 -0
- quantum_playground-0.3.0/src/qplay/hamiltonian.py +58 -0
- quantum_playground-0.3.0/src/qplay/integrators.py +67 -0
- quantum_playground-0.3.0/src/qplay/kinetic.py +163 -0
- quantum_playground-0.3.0/src/qplay/linalg.py +107 -0
- quantum_playground-0.3.0/src/qplay/mechanics.py +142 -0
- quantum_playground-0.3.0/src/qplay/nuclear.py +80 -0
- quantum_playground-0.3.0/src/qplay/numerics.py +85 -0
- quantum_playground-0.3.0/src/qplay/plotting.py +59 -0
- quantum_playground-0.3.0/src/qplay/quantum.py +294 -0
- quantum_playground-0.3.0/src/qplay/relativity.py +59 -0
- quantum_playground-0.3.0/src/qplay/statmech.py +53 -0
- quantum_playground-0.3.0/src/qplay/symbolic.py +57 -0
- quantum_playground-0.3.0/src/qplay/waves.py +87 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/PKG-INFO +184 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/SOURCES.txt +42 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/dependency_links.txt +1 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/entry_points.txt +2 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/requires.txt +17 -0
- quantum_playground-0.3.0/src/quantum_playground.egg-info/top_level.txt +1 -0
- quantum_playground-0.3.0/tests/test_demos.py +51 -0
- quantum_playground-0.3.0/tests/test_explorer.py +253 -0
- quantum_playground-0.3.0/tests/test_extensions.py +297 -0
- quantum_playground-0.3.0/tests/test_maths.py +127 -0
- quantum_playground-0.3.0/tests/test_mechanics.py +80 -0
- quantum_playground-0.3.0/tests/test_new_physics.py +282 -0
- quantum_playground-0.3.0/tests/test_notebooks.py +55 -0
- quantum_playground-0.3.0/tests/test_quantum_relativity.py +95 -0
- quantum_playground-0.3.0/tests/test_site.py +44 -0
- quantum_playground-0.3.0/tests/test_waves_em.py +84 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StativeSh
|
|
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.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantum-playground
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A physics and maths playground: simulations, symbolic maths and notebooks.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://stativesh.github.io/quantum-playground/
|
|
7
|
+
Project-URL: Documentation, https://stativesh.github.io/quantum-playground/api/
|
|
8
|
+
Project-URL: Source, https://github.com/StativeSh/quantum-playground
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: numpy
|
|
13
|
+
Requires-Dist: scipy
|
|
14
|
+
Requires-Dist: sympy
|
|
15
|
+
Requires-Dist: matplotlib
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
19
|
+
Requires-Dist: jupyterlab; extra == "dev"
|
|
20
|
+
Requires-Dist: ipykernel; extra == "dev"
|
|
21
|
+
Requires-Dist: pandas; extra == "dev"
|
|
22
|
+
Requires-Dist: nbconvert; extra == "dev"
|
|
23
|
+
Requires-Dist: pdoc; extra == "dev"
|
|
24
|
+
Requires-Dist: build; extra == "dev"
|
|
25
|
+
Provides-Extra: browser
|
|
26
|
+
Requires-Dist: playwright; extra == "browser"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# quantum-playground
|
|
30
|
+
|
|
31
|
+
[](https://github.com/StativeSh/quantum-playground/actions/workflows/ci.yml)
|
|
32
|
+
[](https://stativesh.github.io/quantum-playground/)
|
|
33
|
+
[](LICENSE)
|
|
34
|
+
|
|
35
|
+
A physics and maths playground in Python: simulations, symbolic mechanics and Jupyter notebooks, with tests that check every solver against known analytic results.
|
|
36
|
+
|
|
37
|
+
**[Website](https://stativesh.github.io/quantum-playground/)**: every figure and animation, the executed notebooks and lessons, the [API reference](https://stativesh.github.io/quantum-playground/api/), and an [interactive explorer](https://stativesh.github.io/quantum-playground/explorer/) with live experiments: the pendulum, quantum tunnelling, the double slit, Kepler orbits, the Lorenz attractor and the Ising model.
|
|
38
|
+
|
|
39
|
+
<table>
|
|
40
|
+
<tr>
|
|
41
|
+
<td align="center"><img src="docs/images/double-slit.gif" width="260" alt="Wavepacket passing through two slits"><br><sub>Double slit: a 2D Schrödinger simulation</sub></td>
|
|
42
|
+
<td align="center"><img src="docs/images/double-pendulum.gif" width="260" alt="Chaotic double pendulum"><br><sub>Chaotic double pendulum</sub></td>
|
|
43
|
+
<td align="center"><img src="docs/images/figure-eight.gif" width="260" alt="Three-body figure-eight orbit"><br><sub>Three-body figure-eight orbit</sub></td>
|
|
44
|
+
</tr>
|
|
45
|
+
</table>
|
|
46
|
+
|
|
47
|
+
<img src="docs/images/wavepacket.gif" width="100%" alt="Wavepacket tunnelling through a barrier">
|
|
48
|
+
|
|
49
|
+
| | |
|
|
50
|
+
|---|---|
|
|
51
|
+
|  |  |
|
|
52
|
+
| Hydrogen orbitals $\lvert\psi_{nlm}\rvert$ | Double-slit fringes against the two-source prediction |
|
|
53
|
+
|  |  |
|
|
54
|
+
| 2D Ising model against Onsager's exact result | Phase portraits with classified fixed points |
|
|
55
|
+
|  |  |
|
|
56
|
+
| Mandelbrot set | Domain colouring and the argument principle |
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
make install # venv + editable install + Jupyter kernel
|
|
62
|
+
make test # run the test suite
|
|
63
|
+
make demos # render every demo figure into ./figures
|
|
64
|
+
make animations # render every GIF into ./figures
|
|
65
|
+
make lab # open JupyterLab
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or without `make`:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python3 -m venv .venv
|
|
72
|
+
.venv/bin/pip install -r requirements.txt
|
|
73
|
+
.venv/bin/qplay list
|
|
74
|
+
.venv/bin/qplay run pendulum wavepacket mandelbrot
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## What is in the box
|
|
78
|
+
|
|
79
|
+
| Module | Topics |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `qplay.mechanics` | damped/driven oscillator, nonlinear and double pendulum, projectile with drag, N-body gravity (figure-eight orbit) |
|
|
82
|
+
| `qplay.waves` | 1D wave equation, heat equation (Crank-Nicolson), Fourier series, spectra |
|
|
83
|
+
| `qplay.em` | point-charge fields, Laplace solver, charged particle in $\mathbf E$ and $\mathbf B$ fields |
|
|
84
|
+
| `qplay.quantum` | Schrödinger eigenstates for any 1D potential, split-step dynamics in 1D and 2D with absorbing edges, tunnelling, the double-slit experiment, hydrogen orbitals $R_{nl}Y_l^m$, spin-1/2, oscillator eigenstates and coherent states |
|
|
85
|
+
| `qplay.atomic` | angular momentum matrices, spin-orbit coupling, Zeeman effect from Landé $g_J$ to Paschen-Back, hydrogen fine structure against the Dirac energy |
|
|
86
|
+
| `qplay.kinetic` | hard-sphere gas in a box: Maxwell-Boltzmann distribution emerging from collisions, pressure from wall impulses, $pV = Nk_BT$ with the hard-sphere correction |
|
|
87
|
+
| `qplay.nuclear` | half-life, Bateman decay chains, matrix-exponential chains, Monte Carlo decay |
|
|
88
|
+
| `qplay.circuits` | RC charging and discharging, series RLC circuit, impedance, resonance and $Q$ factor |
|
|
89
|
+
| `qplay.statmech` | 2D Ising model (Metropolis), Onsager's exact magnetisation and $T_c = 2/\ln(1+\sqrt2)$ |
|
|
90
|
+
| `qplay.relativity` | time dilation, length contraction, velocity addition, Lorentz boosts |
|
|
91
|
+
| `qplay.chaos` | Lorenz system, logistic map, Lyapunov exponent, Mandelbrot and Julia sets |
|
|
92
|
+
| `qplay.symbolic` | SymPy: Lagrangian $\to$ Euler-Lagrange equations $\to$ runnable simulation |
|
|
93
|
+
| `qplay.hamiltonian` | Legendre transform, Hamilton's equations, Hamiltonian $\to$ runnable simulation |
|
|
94
|
+
| `qplay.dynamics` | phase portraits with automatically classified fixed points (saddle, node, spiral, center) |
|
|
95
|
+
| `qplay.complexplane` | domain colouring, argument principle (zeros $-$ poles from a winding number) |
|
|
96
|
+
| `qplay.linalg` | Gram-Schmidt, power iteration, how a matrix maps the unit circle (SVD) |
|
|
97
|
+
| `qplay.numerics` | bisection, Newton, Simpson, finite differences, Monte Carlo |
|
|
98
|
+
| `qplay.integrators` | Euler, RK4, velocity Verlet, adaptive wrapper |
|
|
99
|
+
|
|
100
|
+
## Notebooks
|
|
101
|
+
|
|
102
|
+
| Notebook | Contents |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `01_classical_mechanics` | period vs amplitude, double-pendulum chaos, deriving equations of motion symbolically |
|
|
105
|
+
| `02_quantum_mechanics` | oscillator and double-well states, tunnelling, wavepackets, Rabi oscillations |
|
|
106
|
+
| `03_maths_and_chaos` | Newton's method, Gibbs phenomenon, bifurcation diagram, fractals |
|
|
107
|
+
| `04_orbitals_interference_and_phase_space` | hydrogen orbitals, double slit, Ising phase transition, Legendre transform, phase planes, complex functions, linear maps |
|
|
108
|
+
|
|
109
|
+
## Lessons for A-level and IB
|
|
110
|
+
|
|
111
|
+
Guided lessons in `lessons/`, each mapped to specification points, with exam-style questions that check your answers and worked solutions at the end. Every lesson and notebook has an **Open in Colab** button, so it runs in a browser with nothing to install.
|
|
112
|
+
|
|
113
|
+
Each lesson opens by saying which earlier lessons it builds on (and why) and which it leads to. Suggested routes:
|
|
114
|
+
|
|
115
|
+
| Route | Lessons, in order |
|
|
116
|
+
|---|---|
|
|
117
|
+
| Mechanics | 2 projectile motion → 3 circular motion and orbits → 1 simple harmonic motion |
|
|
118
|
+
| Waves to quantum | 1 simple harmonic motion → 4 stationary waves → 7 wave–particle duality |
|
|
119
|
+
| Fields | 3 circular motion and orbits → 5 electric fields → 6 charged particles in fields |
|
|
120
|
+
| Modern physics | 5 electric fields → 7 wave–particle duality → 8 special relativity |
|
|
121
|
+
| Thermal physics | 3 circular motion and orbits → 9 ideal gases and kinetic theory |
|
|
122
|
+
|
|
123
|
+
The prerequisites and routes live in `lessons/path.json`; `python scripts/lesson_path.py --write` copies them into the lesson headers, and the tests check that the two agree.
|
|
124
|
+
|
|
125
|
+
| Lesson | Covers |
|
|
126
|
+
|---|---|
|
|
127
|
+
| [`01_simple_harmonic_motion`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/01_simple_harmonic_motion.ipynb) | SHM equations and phase, spring and pendulum periods, energy, damping, resonance |
|
|
128
|
+
| [`02_projectile_motion`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/02_projectile_motion.ipynb) | independent horizontal and vertical motion, resolving velocity, time of flight, range and the 45° rule, launches from a height, air resistance |
|
|
129
|
+
| [`03_circular_motion_and_orbits`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/03_circular_motion_and_orbits.ipynb) | centripetal acceleration, orbital speed and period, geostationary orbits, Kepler's third law on a log-log plot, orbital energy and escape velocity |
|
|
130
|
+
| [`04_stationary_waves`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/04_stationary_waves.ipynb) | nodes and antinodes, harmonics on a string, why a plucked string is missing some harmonics, open and closed pipes, Fourier series |
|
|
131
|
+
| [`05_electric_fields`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/05_electric_fields.ipynb) | Coulomb's law, field lines and equipotentials, $E = -dV/dr$, uniform fields between plates and edge effects |
|
|
132
|
+
| [`06_charged_particles_in_fields`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/06_charged_particles_in_fields.ipynb) | parabolic paths in $E$, circles in $B$ with $r = mv/BQ$, cyclotron period, velocity selector, mass spectrometer, helices |
|
|
133
|
+
| [`07_wave_particle_duality`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/07_wave_particle_duality.ipynb) | photoelectric effect, Young's double slit, de Broglie wavelength, a simulated electron through two slits, building the pattern one particle at a time |
|
|
134
|
+
| [`08_special_relativity`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/08_special_relativity.ipynb) | postulates, time dilation and the muon experiment, length contraction, velocity addition, relativity of simultaneity, $E = \gamma mc^2$ |
|
|
135
|
+
| [`09_ideal_gases_and_kinetic_theory`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/09_ideal_gases_and_kinetic_theory.ipynb) | gas laws, a simulated gas relaxing to Maxwell-Boltzmann, pressure from collisions, temperature as molecular kinetic energy, when gases stop being ideal |
|
|
136
|
+
|
|
137
|
+
## Demos and animations
|
|
138
|
+
|
|
139
|
+
`qplay list` shows everything. Twenty-five static figures (`qplay run NAME`), including `hydrogen`, `double-slit`, `ising`, `maxwell-boltzmann`, `coherent-state`, `zeeman`, `decay-chain` and `rlc-resonance`, and six GIFs (`qplay animate NAME`): `double-pendulum`, `wavepacket`, `figure-eight`, `double-slit`, `ising` and `gas`.
|
|
140
|
+
|
|
141
|
+
## Example
|
|
142
|
+
|
|
143
|
+
Derive the equation of motion of a pendulum from its Lagrangian $L = \tfrac12 m\ell^2\dot\theta^2 + mg\ell\cos\theta$, then simulate it:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import sympy as sp
|
|
147
|
+
from qplay import symbolic, integrators
|
|
148
|
+
|
|
149
|
+
t, m, l, g = sp.symbols("t m l g", positive=True)
|
|
150
|
+
th = sp.Function("theta")(t)
|
|
151
|
+
L = m * l**2 * sp.diff(th, t)**2 / 2 + m * g * l * sp.cos(th)
|
|
152
|
+
|
|
153
|
+
symbolic.accelerations(L, [th], t) # [-g*sin(theta)/l]
|
|
154
|
+
rhs = symbolic.to_numeric_rhs(L, [th], t, {m: 1, l: 1, g: 9.81})
|
|
155
|
+
t_arr, y = integrators.solve(rhs, [1.0, 0.0], (0, 10))
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Adding a demo
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
# src/qplay/demos.py
|
|
162
|
+
@demo("my-demo")
|
|
163
|
+
def my_demo():
|
|
164
|
+
"""One-line description shown by `qplay list`."""
|
|
165
|
+
fig, ax = plt.subplots()
|
|
166
|
+
...
|
|
167
|
+
return fig
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
It appears in `qplay list` and is covered by the test suite automatically. Animations work the same way with `@animation("name")` in `src/qplay/animations.py`, returning `(fig, update, frames)`.
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
See `CLAUDE.md` for conventions.
|
|
175
|
+
|
|
176
|
+
- `make test` is the fast loop: it skips tests marked `slow` (the site build, the biggest simulations and the browser tests). `make test-all` runs everything, and `make coverage` adds a line-by-line coverage report that fails below 95%.
|
|
177
|
+
- `tests/test_explorer.py` drives the explorer in headless Chromium: it moves sliders and checks every readout against the Python package or an analytic formula, and lets the animations run to check what they converge to (orbital period, Lorenz separation, Ising magnetisation). Install with `pip install -e ".[browser]"` and `python -m playwright install chromium`, then `make test-browser`. Without Playwright these tests are skipped; CI installs it and fails if the browser is missing.
|
|
178
|
+
- CI runs every test with coverage on Python 3.10, 3.12 and 3.13, and executes the notebooks and lessons.
|
|
179
|
+
- The website is built by `.github/workflows/pages.yml` on every push to `main`: `scripts/build_site.py` renders all demos and animations, executes the notebooks to HTML, builds the API reference from the docstrings with pdoc, and copies the explorer from `site_src/`. Build it locally with `make site` (output in `_site/`), or just the API reference with `make api`. The README images live in `docs/images/` and are regenerated with `make readme-media`.
|
|
180
|
+
- Releases: the distribution is called `quantum-playground` (the import name is still `qplay`; the name `qplay` is taken on PyPI). Pushing a tag such as `v0.3.0` runs `.github/workflows/release.yml`, which checks the tag matches `qplay.__version__`, runs the tests, builds the wheel and publishes it to PyPI through a trusted publisher. `make dist` builds the same files locally.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT, see `LICENSE`.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# quantum-playground
|
|
2
|
+
|
|
3
|
+
[](https://github.com/StativeSh/quantum-playground/actions/workflows/ci.yml)
|
|
4
|
+
[](https://stativesh.github.io/quantum-playground/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A physics and maths playground in Python: simulations, symbolic mechanics and Jupyter notebooks, with tests that check every solver against known analytic results.
|
|
8
|
+
|
|
9
|
+
**[Website](https://stativesh.github.io/quantum-playground/)**: every figure and animation, the executed notebooks and lessons, the [API reference](https://stativesh.github.io/quantum-playground/api/), and an [interactive explorer](https://stativesh.github.io/quantum-playground/explorer/) with live experiments: the pendulum, quantum tunnelling, the double slit, Kepler orbits, the Lorenz attractor and the Ising model.
|
|
10
|
+
|
|
11
|
+
<table>
|
|
12
|
+
<tr>
|
|
13
|
+
<td align="center"><img src="docs/images/double-slit.gif" width="260" alt="Wavepacket passing through two slits"><br><sub>Double slit: a 2D Schrödinger simulation</sub></td>
|
|
14
|
+
<td align="center"><img src="docs/images/double-pendulum.gif" width="260" alt="Chaotic double pendulum"><br><sub>Chaotic double pendulum</sub></td>
|
|
15
|
+
<td align="center"><img src="docs/images/figure-eight.gif" width="260" alt="Three-body figure-eight orbit"><br><sub>Three-body figure-eight orbit</sub></td>
|
|
16
|
+
</tr>
|
|
17
|
+
</table>
|
|
18
|
+
|
|
19
|
+
<img src="docs/images/wavepacket.gif" width="100%" alt="Wavepacket tunnelling through a barrier">
|
|
20
|
+
|
|
21
|
+
| | |
|
|
22
|
+
|---|---|
|
|
23
|
+
|  |  |
|
|
24
|
+
| Hydrogen orbitals $\lvert\psi_{nlm}\rvert$ | Double-slit fringes against the two-source prediction |
|
|
25
|
+
|  |  |
|
|
26
|
+
| 2D Ising model against Onsager's exact result | Phase portraits with classified fixed points |
|
|
27
|
+
|  |  |
|
|
28
|
+
| Mandelbrot set | Domain colouring and the argument principle |
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
make install # venv + editable install + Jupyter kernel
|
|
34
|
+
make test # run the test suite
|
|
35
|
+
make demos # render every demo figure into ./figures
|
|
36
|
+
make animations # render every GIF into ./figures
|
|
37
|
+
make lab # open JupyterLab
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or without `make`:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python3 -m venv .venv
|
|
44
|
+
.venv/bin/pip install -r requirements.txt
|
|
45
|
+
.venv/bin/qplay list
|
|
46
|
+
.venv/bin/qplay run pendulum wavepacket mandelbrot
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## What is in the box
|
|
50
|
+
|
|
51
|
+
| Module | Topics |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `qplay.mechanics` | damped/driven oscillator, nonlinear and double pendulum, projectile with drag, N-body gravity (figure-eight orbit) |
|
|
54
|
+
| `qplay.waves` | 1D wave equation, heat equation (Crank-Nicolson), Fourier series, spectra |
|
|
55
|
+
| `qplay.em` | point-charge fields, Laplace solver, charged particle in $\mathbf E$ and $\mathbf B$ fields |
|
|
56
|
+
| `qplay.quantum` | Schrödinger eigenstates for any 1D potential, split-step dynamics in 1D and 2D with absorbing edges, tunnelling, the double-slit experiment, hydrogen orbitals $R_{nl}Y_l^m$, spin-1/2, oscillator eigenstates and coherent states |
|
|
57
|
+
| `qplay.atomic` | angular momentum matrices, spin-orbit coupling, Zeeman effect from Landé $g_J$ to Paschen-Back, hydrogen fine structure against the Dirac energy |
|
|
58
|
+
| `qplay.kinetic` | hard-sphere gas in a box: Maxwell-Boltzmann distribution emerging from collisions, pressure from wall impulses, $pV = Nk_BT$ with the hard-sphere correction |
|
|
59
|
+
| `qplay.nuclear` | half-life, Bateman decay chains, matrix-exponential chains, Monte Carlo decay |
|
|
60
|
+
| `qplay.circuits` | RC charging and discharging, series RLC circuit, impedance, resonance and $Q$ factor |
|
|
61
|
+
| `qplay.statmech` | 2D Ising model (Metropolis), Onsager's exact magnetisation and $T_c = 2/\ln(1+\sqrt2)$ |
|
|
62
|
+
| `qplay.relativity` | time dilation, length contraction, velocity addition, Lorentz boosts |
|
|
63
|
+
| `qplay.chaos` | Lorenz system, logistic map, Lyapunov exponent, Mandelbrot and Julia sets |
|
|
64
|
+
| `qplay.symbolic` | SymPy: Lagrangian $\to$ Euler-Lagrange equations $\to$ runnable simulation |
|
|
65
|
+
| `qplay.hamiltonian` | Legendre transform, Hamilton's equations, Hamiltonian $\to$ runnable simulation |
|
|
66
|
+
| `qplay.dynamics` | phase portraits with automatically classified fixed points (saddle, node, spiral, center) |
|
|
67
|
+
| `qplay.complexplane` | domain colouring, argument principle (zeros $-$ poles from a winding number) |
|
|
68
|
+
| `qplay.linalg` | Gram-Schmidt, power iteration, how a matrix maps the unit circle (SVD) |
|
|
69
|
+
| `qplay.numerics` | bisection, Newton, Simpson, finite differences, Monte Carlo |
|
|
70
|
+
| `qplay.integrators` | Euler, RK4, velocity Verlet, adaptive wrapper |
|
|
71
|
+
|
|
72
|
+
## Notebooks
|
|
73
|
+
|
|
74
|
+
| Notebook | Contents |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `01_classical_mechanics` | period vs amplitude, double-pendulum chaos, deriving equations of motion symbolically |
|
|
77
|
+
| `02_quantum_mechanics` | oscillator and double-well states, tunnelling, wavepackets, Rabi oscillations |
|
|
78
|
+
| `03_maths_and_chaos` | Newton's method, Gibbs phenomenon, bifurcation diagram, fractals |
|
|
79
|
+
| `04_orbitals_interference_and_phase_space` | hydrogen orbitals, double slit, Ising phase transition, Legendre transform, phase planes, complex functions, linear maps |
|
|
80
|
+
|
|
81
|
+
## Lessons for A-level and IB
|
|
82
|
+
|
|
83
|
+
Guided lessons in `lessons/`, each mapped to specification points, with exam-style questions that check your answers and worked solutions at the end. Every lesson and notebook has an **Open in Colab** button, so it runs in a browser with nothing to install.
|
|
84
|
+
|
|
85
|
+
Each lesson opens by saying which earlier lessons it builds on (and why) and which it leads to. Suggested routes:
|
|
86
|
+
|
|
87
|
+
| Route | Lessons, in order |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Mechanics | 2 projectile motion → 3 circular motion and orbits → 1 simple harmonic motion |
|
|
90
|
+
| Waves to quantum | 1 simple harmonic motion → 4 stationary waves → 7 wave–particle duality |
|
|
91
|
+
| Fields | 3 circular motion and orbits → 5 electric fields → 6 charged particles in fields |
|
|
92
|
+
| Modern physics | 5 electric fields → 7 wave–particle duality → 8 special relativity |
|
|
93
|
+
| Thermal physics | 3 circular motion and orbits → 9 ideal gases and kinetic theory |
|
|
94
|
+
|
|
95
|
+
The prerequisites and routes live in `lessons/path.json`; `python scripts/lesson_path.py --write` copies them into the lesson headers, and the tests check that the two agree.
|
|
96
|
+
|
|
97
|
+
| Lesson | Covers |
|
|
98
|
+
|---|---|
|
|
99
|
+
| [`01_simple_harmonic_motion`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/01_simple_harmonic_motion.ipynb) | SHM equations and phase, spring and pendulum periods, energy, damping, resonance |
|
|
100
|
+
| [`02_projectile_motion`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/02_projectile_motion.ipynb) | independent horizontal and vertical motion, resolving velocity, time of flight, range and the 45° rule, launches from a height, air resistance |
|
|
101
|
+
| [`03_circular_motion_and_orbits`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/03_circular_motion_and_orbits.ipynb) | centripetal acceleration, orbital speed and period, geostationary orbits, Kepler's third law on a log-log plot, orbital energy and escape velocity |
|
|
102
|
+
| [`04_stationary_waves`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/04_stationary_waves.ipynb) | nodes and antinodes, harmonics on a string, why a plucked string is missing some harmonics, open and closed pipes, Fourier series |
|
|
103
|
+
| [`05_electric_fields`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/05_electric_fields.ipynb) | Coulomb's law, field lines and equipotentials, $E = -dV/dr$, uniform fields between plates and edge effects |
|
|
104
|
+
| [`06_charged_particles_in_fields`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/06_charged_particles_in_fields.ipynb) | parabolic paths in $E$, circles in $B$ with $r = mv/BQ$, cyclotron period, velocity selector, mass spectrometer, helices |
|
|
105
|
+
| [`07_wave_particle_duality`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/07_wave_particle_duality.ipynb) | photoelectric effect, Young's double slit, de Broglie wavelength, a simulated electron through two slits, building the pattern one particle at a time |
|
|
106
|
+
| [`08_special_relativity`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/08_special_relativity.ipynb) | postulates, time dilation and the muon experiment, length contraction, velocity addition, relativity of simultaneity, $E = \gamma mc^2$ |
|
|
107
|
+
| [`09_ideal_gases_and_kinetic_theory`](https://colab.research.google.com/github/StativeSh/quantum-playground/blob/main/lessons/09_ideal_gases_and_kinetic_theory.ipynb) | gas laws, a simulated gas relaxing to Maxwell-Boltzmann, pressure from collisions, temperature as molecular kinetic energy, when gases stop being ideal |
|
|
108
|
+
|
|
109
|
+
## Demos and animations
|
|
110
|
+
|
|
111
|
+
`qplay list` shows everything. Twenty-five static figures (`qplay run NAME`), including `hydrogen`, `double-slit`, `ising`, `maxwell-boltzmann`, `coherent-state`, `zeeman`, `decay-chain` and `rlc-resonance`, and six GIFs (`qplay animate NAME`): `double-pendulum`, `wavepacket`, `figure-eight`, `double-slit`, `ising` and `gas`.
|
|
112
|
+
|
|
113
|
+
## Example
|
|
114
|
+
|
|
115
|
+
Derive the equation of motion of a pendulum from its Lagrangian $L = \tfrac12 m\ell^2\dot\theta^2 + mg\ell\cos\theta$, then simulate it:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import sympy as sp
|
|
119
|
+
from qplay import symbolic, integrators
|
|
120
|
+
|
|
121
|
+
t, m, l, g = sp.symbols("t m l g", positive=True)
|
|
122
|
+
th = sp.Function("theta")(t)
|
|
123
|
+
L = m * l**2 * sp.diff(th, t)**2 / 2 + m * g * l * sp.cos(th)
|
|
124
|
+
|
|
125
|
+
symbolic.accelerations(L, [th], t) # [-g*sin(theta)/l]
|
|
126
|
+
rhs = symbolic.to_numeric_rhs(L, [th], t, {m: 1, l: 1, g: 9.81})
|
|
127
|
+
t_arr, y = integrators.solve(rhs, [1.0, 0.0], (0, 10))
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Adding a demo
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
# src/qplay/demos.py
|
|
134
|
+
@demo("my-demo")
|
|
135
|
+
def my_demo():
|
|
136
|
+
"""One-line description shown by `qplay list`."""
|
|
137
|
+
fig, ax = plt.subplots()
|
|
138
|
+
...
|
|
139
|
+
return fig
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
It appears in `qplay list` and is covered by the test suite automatically. Animations work the same way with `@animation("name")` in `src/qplay/animations.py`, returning `(fig, update, frames)`.
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
See `CLAUDE.md` for conventions.
|
|
147
|
+
|
|
148
|
+
- `make test` is the fast loop: it skips tests marked `slow` (the site build, the biggest simulations and the browser tests). `make test-all` runs everything, and `make coverage` adds a line-by-line coverage report that fails below 95%.
|
|
149
|
+
- `tests/test_explorer.py` drives the explorer in headless Chromium: it moves sliders and checks every readout against the Python package or an analytic formula, and lets the animations run to check what they converge to (orbital period, Lorenz separation, Ising magnetisation). Install with `pip install -e ".[browser]"` and `python -m playwright install chromium`, then `make test-browser`. Without Playwright these tests are skipped; CI installs it and fails if the browser is missing.
|
|
150
|
+
- CI runs every test with coverage on Python 3.10, 3.12 and 3.13, and executes the notebooks and lessons.
|
|
151
|
+
- The website is built by `.github/workflows/pages.yml` on every push to `main`: `scripts/build_site.py` renders all demos and animations, executes the notebooks to HTML, builds the API reference from the docstrings with pdoc, and copies the explorer from `site_src/`. Build it locally with `make site` (output in `_site/`), or just the API reference with `make api`. The README images live in `docs/images/` and are regenerated with `make readme-media`.
|
|
152
|
+
- Releases: the distribution is called `quantum-playground` (the import name is still `qplay`; the name `qplay` is taken on PyPI). Pushing a tag such as `v0.3.0` runs `.github/workflows/release.yml`, which checks the tag matches `qplay.__version__`, runs the tests, builds the wheel and publishes it to PyPI through a trusted publisher. `make dist` builds the same files locally.
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT, see `LICENSE`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "quantum-playground"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "A physics and maths playground: simulations, symbolic maths and notebooks."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = ["numpy", "scipy", "sympy", "matplotlib"]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
dev = ["pytest", "pytest-cov", "jupyterlab", "ipykernel", "pandas", "nbconvert", "pdoc", "build"]
|
|
17
|
+
# Browser tests of the explorer (tests/test_explorer.py); also run `python -m playwright install chromium`.
|
|
18
|
+
browser = ["playwright"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://stativesh.github.io/quantum-playground/"
|
|
22
|
+
Documentation = "https://stativesh.github.io/quantum-playground/api/"
|
|
23
|
+
Source = "https://github.com/StativeSh/quantum-playground"
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
qplay = "qplay.__main__:main"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
|
30
|
+
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
testpaths = ["tests"]
|
|
33
|
+
# Slow tests (site build, big simulations) are skipped by default; `make test-all` or `pytest -m ""` runs them.
|
|
34
|
+
addopts = "-q -m 'not slow'"
|
|
35
|
+
markers = ["slow: takes more than a couple of seconds; skipped unless selected with -m \"\" or -m slow"]
|
|
36
|
+
|
|
37
|
+
[tool.coverage.run]
|
|
38
|
+
source = ["qplay"]
|
|
39
|
+
|
|
40
|
+
[tool.coverage.report]
|
|
41
|
+
fail_under = 95
|
|
42
|
+
show_missing = true
|
|
43
|
+
skip_covered = true
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""qplay: a small physics and maths playground.
|
|
2
|
+
|
|
3
|
+
Modules
|
|
4
|
+
-------
|
|
5
|
+
integrators time-stepping schemes (Euler, RK4, velocity Verlet, adaptive wrapper)
|
|
6
|
+
mechanics oscillators, pendulums, projectiles, N-body gravity
|
|
7
|
+
waves 1D wave and heat equations, Fourier series, spectra
|
|
8
|
+
em point-charge fields, Laplace solver, charged-particle motion
|
|
9
|
+
quantum Schroedinger solvers (1D and 2D), tunnelling, double slit, hydrogen orbitals, spin-1/2,
|
|
10
|
+
oscillator eigenstates and coherent states
|
|
11
|
+
statmech 2D Ising model and Onsager's exact solution
|
|
12
|
+
relativity Lorentz transformations and special-relativity helpers
|
|
13
|
+
kinetic hard-sphere gas, Maxwell-Boltzmann distribution, ideal-gas law
|
|
14
|
+
atomic angular momentum, spin-orbit coupling, Zeeman effect, hydrogen fine structure
|
|
15
|
+
nuclear radioactive decay, Bateman decay chains, Monte Carlo decay
|
|
16
|
+
circuits RC charging and discharging, series RLC resonance
|
|
17
|
+
chaos Lorenz system, logistic map, Mandelbrot/Julia sets
|
|
18
|
+
symbolic SymPy helpers: Euler-Lagrange equations, Lagrangian -> simulation
|
|
19
|
+
hamiltonian Legendre transform, Hamilton's equations, Hamiltonian -> simulation
|
|
20
|
+
dynamics phase portraits and fixed-point classification for 2D systems
|
|
21
|
+
complexplane domain colouring and the argument principle
|
|
22
|
+
linalg Gram-Schmidt, power iteration, how a matrix maps the unit circle
|
|
23
|
+
numerics root finding, quadrature, finite differences, Monte Carlo
|
|
24
|
+
plotting figure saving and GIF helpers
|
|
25
|
+
demos ready-made figures, run with ``python -m qplay run <name>``
|
|
26
|
+
animations GIFs, run with ``python -m qplay animate <name>``
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Command line.
|
|
2
|
+
|
|
3
|
+
qplay list show demos and animations
|
|
4
|
+
qplay run <name|all> [--out DIR] render demo figures (PNG)
|
|
5
|
+
qplay animate <name|all> [--frames N] [--fps F] [--out DIR] render animations (GIF)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
|
|
12
|
+
from . import __version__, animations, demos
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _summary(fn):
|
|
16
|
+
doc = (fn.__doc__ or "").strip().splitlines()
|
|
17
|
+
return doc[0] if doc else ""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main(argv=None):
|
|
21
|
+
parser = argparse.ArgumentParser(prog="qplay", description="Physics and maths playground")
|
|
22
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
23
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
24
|
+
sub.add_parser("list", help="list demos and animations")
|
|
25
|
+
run = sub.add_parser("run", help="render demos to PNG files")
|
|
26
|
+
run.add_argument("names", nargs="+", help="demo names, or 'all'")
|
|
27
|
+
run.add_argument("--out", default="figures", help="output directory (default: figures)")
|
|
28
|
+
anim = sub.add_parser("animate", help="render animations to GIF files")
|
|
29
|
+
anim.add_argument("names", nargs="+", help="animation names, or 'all'")
|
|
30
|
+
anim.add_argument("--out", default="figures", help="output directory (default: figures)")
|
|
31
|
+
anim.add_argument("--frames", type=int, default=120, help="number of frames (default: 120)")
|
|
32
|
+
anim.add_argument("--fps", type=int, default=24, help="frames per second (default: 24)")
|
|
33
|
+
args = parser.parse_args(argv)
|
|
34
|
+
|
|
35
|
+
if args.command == "list":
|
|
36
|
+
print("Demos (qplay run NAME):")
|
|
37
|
+
for name in sorted(demos.DEMOS):
|
|
38
|
+
print(f" {name:22s}{_summary(demos.DEMOS[name])}")
|
|
39
|
+
print("Animations (qplay animate NAME):")
|
|
40
|
+
for name in sorted(animations.ANIMATIONS):
|
|
41
|
+
print(f" {name:22s}{_summary(animations.ANIMATIONS[name])}")
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
registry = demos.DEMOS if args.command == "run" else animations.ANIMATIONS
|
|
45
|
+
names = sorted(registry) if args.names == ["all"] else args.names
|
|
46
|
+
for name in names:
|
|
47
|
+
try:
|
|
48
|
+
if args.command == "run":
|
|
49
|
+
print(demos.run(name, args.out))
|
|
50
|
+
else:
|
|
51
|
+
print(animations.render(name, args.out, n_frames=args.frames, fps=args.fps))
|
|
52
|
+
except KeyError as err:
|
|
53
|
+
parser.error(str(err.args[0]))
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
raise SystemExit(main())
|