Skip to content

Restructure into headless solver + viewer, TOML config, VTKHDF run files, Quarto docs - #1

Merged
pastewka merged 10 commits into
mainfrom
restructure
Oct 22, 2026
Merged

pastewka merged 10 commits into
mainfrom
restructure

Conversation

@pastewka

Copy link
Copy Markdown
Member

Implements RESTRUCTURING_PLAN.md: the flat simulator becomes a headless solver package and a separate viewer, connected by a run file, with TOML configuration, a CLI, restartable runs and a Quarto book as documentation.

Numerics are unchanged. Two reference runs (test/test_reference.py) were recorded on the original code before any refactoring and reproduce bit-identically after every commit: a sessile drop with global split and collapse, and a quasi-static sphere run with local remeshing and wetting.

What changes

  • Two distributions (Python ≥ 3.12):
    • DropletRelaxer, the solver: numpy, scipy and h5py only; a test makes sure it never imports PyVista.
    • DropletViewer (viewer/): PyVista viewer.
  • Driver with frame sinks (DropletRelaxer/driver.py, io/frame.py): the remesh loop from main.py. Every output is a sink: HDF5 writer, console, live window. Only a stop request flows back, so attaching a window or writer does not change results (tested).
  • TOML scenarios with strict checking ("did you mean" hints) and --set section.key=value overrides. The field comments became attribute docstrings; a test enforces that every parameter is documented, and droplet-relax show-config prints them.
  • Run file: HDF5 in the VTKHDF layout (temporal PolyData, topology may change per frame), checked with vtkHDFReader of VTK 9.3.1 and 9.7.1, so ParaView opens it.
    • Written in SWMR mode, so a running job can be watched.
    • Checkpoints in two alternating slots; a restart after a simulated kill ends bit-identically to the uninterrupted run, also for the quasi-static sphere scenario.
  • CLIs:
    • droplet-relax run|restart|info|show-config; SIGTERM and Ctrl-C stop cleanly with a checkpoint.
    • droplet-view run.h5 [--follow | --video out.mp4].
    • droplet-view live scenario.toml: in-process live window with a Stop button. It always writes the run file and replays it after the run.
  • Docs: docs/mathematical_background.tex converted to a Quarto book, with a new "Using the Code" part.
    • The parameter reference and the file-format tables are generated from the code; a test checks that every code name in the equation-to-code table imports.
    • The README design log moved to an appendix; the README is now short.
  • Removed: run.sh, run_tests.sh, main.py, requirements.txt, the selftest, and the .npy export (the Save button now records the replay video). The vtk<9.5 cap is gone: the GLX crash it guarded against is handled by disabling multisampling.
  • CI:
    • tests.yml: Python 3.12–3.14; viewer tests under xvfb with the latest VTK; slow tests nightly or with the slow label.
    • flake8.yml: pre-commit.
    • documentation.yml: renders the PDF as an artifact and attaches it to releases.
    • publish.yml: uploads to PyPI via trusted publishing on v* tags.

Where the implementation deviates from the plan

Section 8 of RESTRUCTURING_PLAN.md lists these:

  • The package is flat, with the old module names; the planned geometry/, energies/ and optimizer/ subpackages would only have added import cycles.
  • VTK constraints found by testing: the file must use the HDF5 1.10 format, and FieldDataSizes is ordered [components, tuples].
  • Live reading relies on datasets only (SWMR readers cannot refresh attributes): the run status is a small dataset, and frames are counted by Steps/Values.

Testing

  • Locally on macOS with Python 3.14: pytest -m "" passes (154 tests), and the reference runs are bit-identical with DROPLET_REFERENCE_HASH=1.
  • The docs PDF builds with no unresolved references.
  • Both distributions build and pass twine check.
  • A real window was opened for a live run and the replay that follows.
  • This PR is the first CI run of the new workflows.

Not covered

  • Not tested interactively: closing the window during a live run, and the right-drag pan.
  • Needs a check on Linux: the uncapped VTK, ideally on the machine that originally crashed with VTK 9.7.1.
  • No release tag yet: publish.yml needs the PyPI projects and a pypi environment set up for trusted publishing first.

🤖 Generated with Claude Code

pastewka and others added 10 commits October 22, 2026 13:09
Commits the existing simulator as-is (plus the VTK pin relaxation and
RESTRUCTURING_PLAN.md) as the starting point for the restructuring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/reference_run.py records two short headless scenarios (sessile drop
with global split + collapse; quasi-static sphere with local remeshing and
wetting) into tests/reference/*.json. tests/test_reference.py (slow)
compares final scalars tightly and, with DROPLET_REFERENCE_HASH=1, the
SHA-256 of the final mesh. main() now takes an optional cfg and returns a
summary of the final state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Move the solver modules into the flat package DropletRelaxer/ (git mv,
  relative imports); surface_free_energy moves to DropletRelaxer.analysis.
- pyproject.toml (hatchling + hatch-vcs, Python >= 3.12, test extra);
  pytest settings move there. tests/ -> test/.
- Drop run_tests.sh, pytest.ini, the fast marker, the sys.path hack and the
  selftest. Slow tests are deselected by conftest unless -m is given or the
  test is named by node id.
- flake8 (.flake8) via pre-commit; tests.yml (3.12-3.14, slow nightly or
  on the 'slow' label) and flake8.yml workflows.

Reference runs reproduce bit-identically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ckage

- DropletRelaxer.driver.Driver: the remesh/restart loop formerly in
  main.py. It emits read-only Frames (energy, force, COM, gap, curvature
  computed by the driver), Events (remesh, diverged, log) and ticks to
  FrameSinks and honours a StopRequest.
- DropletRelaxer.io.frame: Frame, Event, RunInfo, RunStatus, StopRequest.
- DropletRelaxer.console.ConsoleSink: log lines + optional progress bar.
- config: OutputConfig (every = ex VisConfig.render_every) and
  apply_volume_scale(), shared by the driver and test/runhelpers.py.
- viewer/ (distribution DropletViewer): ViewerConfig (ex VisConfig),
  scene.py (ex visualize.py, plus a Scene class), charts, interaction,
  history slider, video export, and LiveWindow, a frame sink with Stop
  button, throttled event handling and stop-on-close.
- Save records the replay video (+ frames.csv in quasi-static mode); the
  .npy export (export.py) is gone.
- main.py only wires Driver + ConsoleSink + LiveWindow.
- Tests: solver imports without pyvista; sinks/live window do not change
  results; frame/event contents; stop requests. Reference runs reproduce
  bit-identically, with and without the live window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- config: load()/loads() read a scenario TOML strictly (unknown keys and
  wrong types are errors, with 'did you mean' hints), --set style
  overrides parse TOML values, dumps() writes a complete config (exact
  float round trip), optionally with documentation as comments.
- Field comments become attribute docstrings (read by field_docs());
  a test fails if a field is undocumented. Same for ViewerConfig, which
  reads the [viewer] table.
- Runtime state leaves the config: Config.roughness_field and the CONFIG
  global are gone; surface_free_energy takes roughness= explicitly.
- mesh.target_volume is a float; 0 means no rescaling.
- droplet-relax run|restart|info|show-config (restart/info need the run
  file, next commit); SIGTERM/Ctrl-C end a run cleanly.
- examples/: default, sessile, anomaly scenarios and a SLURM job-array
  sweep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- io/schema.py: single definition of the file contents (per-frame arrays,
  groups); io/writer.py (H5Writer frame sink) and io/reader.py (RunFile).
- /VTKHDF holds temporal PolyData (one part per frame, topology may change
  between frames) with point, cell and per-step field data; verified with
  vtkHDFReader of VTK 9.3.1 and 9.7.1. /DropletRelaxer holds the config
  (as given and effective), start mesh, ragged contact-line rings, events,
  the sampled roughness field and two alternating checkpoint slots.
- The file is written in SWMR mode (HDF5 1.10 format, readable by VTK's
  bundled HDF5); a frame becomes visible only when complete, so
  RunFile(follow=True) can read a run in progress.
- The driver hands checkpoints (complete loop state incl. the quasi-static
  controller) to the sinks every output.checkpoint_every iterations and at
  the end, and can restart from one. droplet-relax restart/info.
- Tests: round trip, VTKHDF offsets, restart after a simulated kill equals
  the uninterrupted run (also for the quasi-static sphere reference,
  bit-identical), restart overrides are restricted, reading while another
  process writes, CLI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- droplet-view run.h5 replays a run (history slider, Play, Save = video
  into the run's folder); --follow keeps up with a run being written;
  --video renders the replay off screen; live scenario.toml runs the
  solver in-process with the live window, always writing a run file
  (default runs/<timestamp>/run.h5) that the window replays afterwards.
- --software-gl and display/Conda hints replace run.sh, which is removed
  together with main.py and requirements.txt.
- Tests: droplet-view live (off screen) writes and replays; video export;
  run file readable by vtkHDFReader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/mathematical_background.tex converted (pandoc + fix-ups) into a
  Quarto book: Theory, Implementation, Comparison and Outlook, plus a new
  part 'Using the Code' (getting started, configuration, troubleshooting).
  Cross-references are Quarto refs; the TikZ figure is compiled to
  PDF/PNG (source kept), the algorithm is a Markdown list, the
  bibliography is references.bib. The .tex/.txt/.pdf copies are removed.
- Chapters on program structure, output and verification describe the
  new architecture; the equation-to-code table uses dotted names that
  test/test_docs.py imports.
- Executable cells generate the parameter reference (from the field
  docstrings) and the run-file tables (from io/schema.py).
- The README design log moves to an appendix with an old-to-new name
  map; README shrinks to overview, install, quick start, docs, tests.
- documentation.yml renders the PDF on every push/PR (artifact) and
  attaches it to releases; docs extra in pyproject; PR template.
- CLAUDE.md rewritten for the new layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- publish.yml builds both distributions on v* tags and uploads them to
  PyPI via trusted publishing.
- CHANGELOG.md; RESTRUCTURING_PLAN.md records what was implemented and
  where the implementation deviates from the plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The writer sets the final status after the last frame, so refreshing the
status first guarantees that a 'finished' status comes with every frame.
Before, a reader could see 'finished' with a stale frame count (flaky
test under load; --follow could attach the history slider without the
last frames).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pastewka
pastewka merged commit 7f4684d into main Oct 22, 2026
8 checks passed
@pastewka
pastewka deleted the restructure branch October 22, 2026 12:36
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