Skip to content

Pure-client port — runs entirely in the browser, no server - #10

Draft
cakirmert wants to merge 7 commits into
5ir-Lancelot:masterfrom
cakirmert:client-side-port
Draft

Pure-client port — runs entirely in the browser, no server#10
cakirmert wants to merge 7 commits into
5ir-Lancelot:masterfrom
cakirmert:client-side-port

Conversation

@cakirmert

Copy link
Copy Markdown
Contributor

Hi Lukas,

I had a go at reworking the site so it can be served as plain static
files (no Flask/Dash, no Python backend, no database). Everything is in
static/; the Dash app at the repo root is untouched.

Live preview

A GitHub Actions workflow deploys the static/ folder to GitHub Pages on
every push. Live preview from my fork:

https://cakirmert.github.io/multipage_website/

When you merge, you can flip Settings → Pages → Source to "GitHub Actions"
once and your repo will deploy itself the same way.

How it works

  • PHREEQC compiled to WebAssembly via Emscripten — runs in the visitor's
    browser. Numerically validated against phreeqpython to 6+ significant
    figures on pH and dissolved-carbon totals (7 reference cases pass at
    0.1 % tolerance, 3 realistic 8-input water compositions pass at 5 %).
  • The seawater carbonate system uses a JS port of CO2SYS (Mehrbach 73 +
    Mucci 83 constants). Within 5 % of PyCO2SYS across 7 reference cases.
  • DIC vs pCO2 ships a precomputed 51 × 61 grid for instant interactivity.
  • Bjerrum, XRF, Charge balance, Forsterite, Lasaga are pure-JS
    ports of the existing math.
  • Markdown narrative + LaTeX equations preserved via marked + KaTeX.

Things you'll likely want to change before going public

  1. Impressum — I added a "Hosting" section mentioning Cloudflare Pages
    with DigitalOcean as alternative. Pick whichever you actually use and
    delete the other (and update wording if needed).
  2. Datenschutz — same; if the actual host logs more than IP/timestamp,
    the wording may need tightening.
  3. Background image / logo — copied from your existing assets/,
    no changes. Confirm they're the ones you want.
  4. References / equations — markdown copied verbatim from your
    assets/*.md. If any have updated since (Rimstidt 2012, Lasaga 1994,
    Mehrbach references, etc.), the source files in
    static/assets/markdown/ are what to edit.
  5. co2sys.js constants — uses Mehrbach 73 refit (PyCO2SYS
    opt_k_carbonic=4). Current PyCO2SYS default is Sulpis 2020
    (opt=16). If you want exact parity with current PyCO2SYS, the
    constant set in static/js/co2sys.js needs
    updating; I picked the older one because it's well-documented and
    gives clean closed-form pK1/pK2 expressions.

How to read this PR

  • static/README.md — overview + quickstart
  • static/BUILD.md — exactly how the WASM-PHREEQC
    binary was built (the build chain is gitignored to keep the diff
    reasonable; this doc is the recipe to reproduce)
  • static/test/ — three Node validators that diff the
    in-browser output against direct Python runs

Why a draft PR

So you can review at your pace; happy to address feedback or split it
into smaller PRs if that's easier.

cakirmert and others added 7 commits April 28, 2026 13:58
Reimplement the Dash app as plain HTML/CSS/JS that runs entirely in the
visitor's browser:

* PHREEQC compiled to WebAssembly via Emscripten (1.7 MB engine, 46 KB
  preloaded phreeqc.dat). Validated against phreeqpython on 7 reference
  cases at 0.1% tolerance, plus 3 realistic 8-input water compositions.
* Seawater carbonate system ported to vanilla JS (Mehrbach 73 + Mucci 83
  constants). Validated against PyCO2SYS within 5% across 7 cases.
* DIC vs pCO2 grid precomputed once with phreeqpython, shipped as JSON.
* All other pages (charge balance, forsterite, Lasaga, XRF, Bjerrum,
  legal) ported as static HTML/JS.
* Markdown narrative + LaTeX equations preserved via marked + KaTeX.

Build chain (emsdk, iphreeqc source, CMake build tree) is gitignored;
the full reproducible recipe is in static/BUILD.md.

GitHub Actions workflow added to deploy static/ to GitHub Pages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The carbonic-acid Bjerrum sweep was aborting on every pH step. Two bugs:

1. The PHASES Fix_H+ definition was placed AFTER an END separator, which
   means PHREEQC tried to evaluate the EQUILIBRIUM_PHASES block in
   simulation 1 before Fix_H+ was defined. That's an unrecoverable
   abort, and once the persistent IPhreeqc instance hit it, every
   subsequent RunString call also aborted.

2. EQUILIBRIUM_PHASES needs a reactant (HCl or NaOH) that PHREEQC can
   only DISSOLVE, never precipitate back. Fixing pH below natural
   requires HCl; above natural requires NaOH. The previous code always
   used HCl, so half the sweep had no usable proton source.

Fix:
- Get natural pH once at startup with a baseline solve.
- For each target pH, emit ONE simulation containing PHASES + SOLUTION +
  EQUILIBRIUM_PHASES + SELECTED_OUTPUT in that order (no END between).
- Pick HCl when target < natural, NaOH when target >= natural.

Verified end-to-end: 141 pH steps over [0, 14] all converge in 179 ms
on a single persistent IPhreeqc instance, fractions match textbook
Bjerrum (CO2/HCO3 50:50 at pH 6.3, HCO3/CO3 50:50 at pH 10.3).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant