/ concept-collection / turing-surface-cache
Sign in
concept-collection / turing-surface-cache
turing-surface-cache / README.md
146 lines · 6.8 KBCodeBlameHistory
3Reaction-diffusion systems (Turing patterns) on curved closed surfaces,
4evaluated at a chosen end time, with the solutions shared between all visitors
5through a cloud cache.
7This is a trimmed fork of
8[turing-surface](https://github.com/concept-collection/turing-surface), which
9solves the same systems live and freely tunable. What this app changes is the
10contract: every setting is a choice from a short list, so each combination of
11choices names exactly one solution. Solutions already in the shared cache load
12by themselves as the choices are browsed; a combination nobody has computed
13shows empty surfaces, and nothing runs until the user presses **Compute
14solution**, which runs the solver locally (in the browser, on WebGPU),
15watching the pattern form and stopping at exactly the requested time. Users
16who hold an upload API key contribute their locally-computed solutions back,
17so the next visitor who asks for the same combination gets it in a second
18rather than a minute.
20## The discrete parameter space
22One model ships (Schnakenberg, in turing-surface's 6-transform flux form) on
23three geometries (sphere, ellipsoid, peanut). The choices, defined in
24[`src/cache/options.ts`](src/cache/options.ts):
26| setting | choices |
27|---|---|
28| a | 0.05, **0.1**, 0.15, 0.2 |
29| b | 0.7, **0.9**, 1.1, 1.3 |
30| D₁ | 1.6e-4, **4e-4**, 1e-3 |
31| D₂ | 3.2e-3, **8e-3**, 2e-2 |
32| dt | 0.02, **0.05**, 0.1 |
33| geometry | sphere, **ellipsoid** (axes each 0.6/1/1.5), peanut (waist 0.4/0.6/0.8, stretch 0/0.6/1.2) |
34| seed | **1**–5 |
35| end time | **100**, 200, 400, 800, 1600 |
37(Defaults in bold.) The numerical-scheme settings are fixed — lmax 63, 8
38solve iterations, seed wavelength λ = 0.5 — but are recorded in every cache
39key, so offering them as choices later invalidates nothing.
41The whole selection is mirrored into the URL fragment, every value written
42explicitly, so reloading returns to the same combination and a shared link
43opens on the same spec (and, when cached, the same solution) for whoever
44follows it. A "Reset to defaults" button puts every choice back.
46Every end time is an exact multiple of every dt, so a run to T = 800 passes
47exactly through t = 100, 200 and 400. Those intermediate states are captured
48as the run passes them and, for an uploading user, encoded and uploaded in
49the background while the run continues: one long run populates four cache
50entries, and the earlier ones are already shared before the run finishes. The same structure works in the other
51direction: the state is Markovian in the spectral coefficients, so before
52computing anything the app looks for the longest cached shorter run of the
53same spec and continues from its final state, computing only the remainder.
54Asking for T = 1600 when T = 800 is cached costs half the run, and the t = 0
55initial state travels inside every file of the chain, so a continuation
56writes files identical in kind to a from-scratch run.
58## How the cache works
60The page's whole state is one small spec object (model, parameters, geometry,
61seed, end time, scheme settings, plus the app name and a format version). Its
62canonical JSON — keys sorted at every level — is hashed with SHA-256, and the
63hash is the object name:
65```
66https://tempory.net/tmpbucket/turing-surface-cache/v1/schnakenberg/<sha256>.h5
67```
69A lookup is therefore a single GET with no index or API, and a 404 means a
70miss. The path carries the app name, format version and model in the clear so
71that future cleanup (lifecycle rules, prefix deletes) never has to open a file
72to know what it belongs to; the hash input includes the version string, so a
73format change moves every object rather than silently colliding with the old
74ones.
76Uploads go through the [tmpbucket](https://github.com/scratchrealm/tmpbucket)
77Worker: the client presents the API key and a file name, receives a presigned
78R2 PUT URL, and uploads directly. Only holders of the key can write; everyone
79can read. The key is entered in the page and kept in localStorage.
81## The cache file
83Cache files are HDF5, written in the browser with
84[h5wasm](https://github.com/usnistgov/h5wasm) and readable from Python with
85h5py. The layout is turing-surface's reference-file layout (see
86`docs/ellipsoid-reference-spec.md` there) extended with the cache's identity
87at the root, so a cache file is *also* a valid reference file — it can be
88loaded straight into turing-surface's "Compare against uploaded data" mode:
90```
91/ attrs: app, format_version, spec_json, model, species,
92 created_utc, adapter
93├─ backend/ attrs: adapter, runtime, precision
94├─ spec/ attrs: geometry, lmax, seed, steps, niter, lam3, t_end
95│ ├─ params/ attrs: a, b, D1, D2, dt
96│ └─ geometry_params/ attrs: the geometry's params
97├─ grid/ attrs: lmax, mmax, nlat, nphi, nlm
98├─ geometry/ Gx, Gy, Gz float32[2·nlm]
99├─ initial/ U, V (spectral state at t = 0) float32[2·nlm]
100└─ final/ U, V (at the end time) float32[2·nlm]
101```
103`spec_json` is the exact string that was hashed into the object name, and the
104reader verifies it matches what was asked for. The initial state is included
105so a file fully defines its run; the coefficients are the spherical-harmonic
106convention documented in turing-surface (orthonormal + Condon-Shortley,
107m-major, [re, im] interleaved). At lmax 63 a file is about 90 KB.
109Note that the solver is deterministic given the spec only to fp32 round-off:
110different GPUs round differently, so a cached solution and a local recompute
111agree closely but not bit-for-bit. The cache stores whichever trusted user
112computed a combination first, and the file records which adapter that was.
114## Development
116```
117npm install
118npm run dev # local dev server
119npm run build # type-check + production build to dist/
120```
122numbl is a local `file:../../numbl` dependency, exactly as in turing-surface —
123a sibling checkout of [numbl](https://github.com/flatironinstitute/numbl) is
124required, reached through the `numbl-src` alias in
125[`vite.config.ts`](vite.config.ts). See turing-surface's README for the
126details; nothing about the arrangement changed here.
128Checks:
130- `node scripts/check-app.mjs` — end-to-end in headless Chrome (SwiftShader
131 WebGPU) with the cloud cache mocked: a miss computes locally and produces
132 the .h5 (verified with h5py), a fresh page loads that .h5 as a hit, and a
133 third page asking for a longer end time warm-starts from it. The pages use
134 the `?tend=` query hook, which substitutes short test end times for the
135 UI's list. This is what CI runs.
136- `node scripts/check-live.mjs [url]` — smoke-check a deployed URL against
137 the real cache.
138- `node scripts/screenshot.mjs out.png [light|dark] [tEnd]` — screenshot
139 after the boot-time solve.
141Deployed to GitHub Pages by `.github/workflows/deploy.yml` on push to `main`.
143## License
145CECILL-2.1 (inherited from SHTNS via shtns-webgpu, whose sources are
146vendored under `src/sht/`).
moveopenescclose