Skip to content

feat(pl): phonon, pourbaix, neb, rdf_msd, wulff and chempot renderers - #24

Merged
Starlitnightly merged 2 commits into
mainfrom
feat/pl-phonon-pourbaix-neb-md-wulff-chempot
Sep 12, 2026
Merged

Starlitnightly merged 2 commits into
mainfrom
feat/pl-phonon-pourbaix-neb-md-wulff-chempot

Conversation

@Starlitnightly

Copy link
Copy Markdown
Contributor

Six plots the analysis namespaces produced data for and mv.pl could not draw

Agents fell back to raw matplotlib for these. Each renderer is @registered with a requires contract the probe battery checks by deletion, takes md / level / ax, returns the Axes, puts units on the axes and the level of theory in the title, uses Okabe–Ito for series (viridis for the one surface), and raises a ValueError naming the producer when its slot is absent.

function reads
mv.pl.phonon obsm['phonon_dos_{level}'], uns['grids']['phonon_dos'] (method, supercell), obs['n_imaginary_modes_{level}']; dispersion= takes the mv.prop.dispersion bands object on a shared frequency axis
mv.pl.pourbaix uns['pourbaix']maps[name], ph_grid, potential_grid, ph, potential; obs['pourbaix_decomposition']
mv.pl.neb obsm['neb_profile_{level}'] + grid neb_profile, obs['neb_converged_{level}']
mv.pl.rdf_msd obsm['rdf_md_{rdf_level}'], obsm['coordination_md_{rdf_level}'], obs['first_shell_{rdf_level}'], obsm['md_msd_trace_{level}'] + grid, obs['diffusivity_{level}']
mv.pl.wulff uns['wulff'][level][name]vertices, face_index, face_miller, area_fractions, anisotropy
mv.pl.chempot uns['chempot_diagram'] (elements, domains[*].vertices); kind='window' reads uns['chempot_limits']

Three producers deposited nothing drawable, extended minimally

  • mv.md.run keeps the MSD trace it already fitted D to, as obsm['md_msd_trace_<level>'] on the same picosecond grid as the temperature trace (replaced together on a rerun of a different length). Both traces are now claimed in produces.
  • mv.surf.wulff stores the polyhedron as three h5ad-writable arrays (vertices (n,3), face_index (n,), face_miller) plus per-family area_fractions. The bulk.uns['wulff'] claim the README recorded as "never written" is restored — it now is, and the plot depends on it. README row updated.
  • mv.thermo.pourbaix evaluates the decomposition energy over a pH/E mesh (ph_range=, potential_range=, n_grid=) on the diagram it already fetched per material and stores it in uns['pourbaix']['maps']. Still network-gated, still in UNPROBEABLE.

Tests

  • tests/test_pl_{phonon,pourbaix,neb,rdf_msd,wulff,chempot}.py — real EMT producers where they run offline; the Pourbaix map is synthetic in the producer's own shape.
  • Probe cases added to tests/_contract_cases.py (aqueous, barred, diffusing, chempot_mapped, wulffed), so test_no_claim_goes_unprobed stays green.
  • One tutorial code cell per function (nb_getting_started, nb_screening, nb_defects_and_diffusion — replaces the mv.pl.spectra NEB workaround —, nb_dynamics, nb_surfaces_and_adsorption), so test_notebook_coverage stays green.
  • Full suite: 946 passed, 144 skipped, 0 failed (baseline on main: 914 passed, 143 skipped; +32 new tests, +1 mp-api-gated skip).

3D collections bypass matplotlib ≥ 3.10's autolim, which produced infinite limits on the NaN-padded ragged face array under pytest; both callers set limits themselves.

🤖 Generated with Claude Code

Starlitnightly and others added 2 commits September 12, 2026 06:19
Six analysis namespaces deposited data mv.pl could not draw, so agents fell
back to raw matplotlib. Each renderer reads the slot its producer writes,
takes md/level/ax, returns the Axes, puts units on the axes and the level of
theory in the title, uses Okabe-Ito for series and viridis for the one
surface, and raises a ValueError naming the producer when its slot is absent.

- mv.pl.phonon   reads obsm['phonon_dos_{level}'] + uns['grids']['phonon_dos']
                 (method, supercell) and obs['n_imaginary_modes_{level}'];
                 dispersion= takes the mv.prop.dispersion bands object and
                 shares the frequency axis (DOS drawn sideways).
- mv.pl.pourbaix reads uns['pourbaix'] maps/ph_grid/potential_grid and
                 obs['pourbaix_decomposition']; water window, threshold
                 contour (0.5 eV/atom), evaluated point.
- mv.pl.neb      reads obsm['neb_profile_{level}'] + grid 'neb_profile',
                 obs['neb_converged_{level}']; barrier annotated, unconverged
                 bands dashed.
- mv.pl.rdf_msd  reads obsm['rdf_md_{rdf_level}'], ['coordination_md_..'],
                 obs['first_shell_..'], obsm['md_msd_trace_{level}'] + grid,
                 obs['diffusivity_{level}']; 6Dt slope drawn where fitted.
- mv.pl.wulff    reads uns['wulff'][level][name] vertices/face_index/
                 face_miller/area_fractions; 3D polyhedron by Miller family.
- mv.pl.chempot  reads uns['chempot_diagram'] (segments for a binary,
                 polygons for a ternary, artificial floor cut where the
                 title says); kind='window' reads uns['chempot_limits'].

Three producers deposited nothing drawable and are extended minimally:

- mv.md.run keeps the MSD trace it already fitted D to, as
  obsm['md_msd_trace_<level>'] on the same ps grid as the temperature
  trace (replaced together on a rerun of different length); both traces
  are now claimed in produces.
- mv.surf.wulff stores the polyhedron as three h5ad-writable arrays plus
  the per-family area fractions, and the bulk.uns['wulff'] claim the README
  recorded as "never written" is restored because it now is.
- mv.thermo.pourbaix evaluates the decomposition energy over a pH/E mesh
  (ph_range=, potential_range=, n_grid=) on the diagram it already fetched
  and stores it in uns['pourbaix']['maps'].

Tests: tests/test_pl_<name>.py for each (real EMT producers where they run
offline, synthetic deposits in the producer's own shape for Pourbaix),
probe cases in tests/_contract_cases.py so every new requires claim is
checked by deletion, and a tutorial code cell per function so
test_notebook_coverage stays green. 3D collections bypass matplotlib's
autolim, which produced infinite limits on NaN-padded ragged faces.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pymatgen's elemental price table is read through bibtexparser's v1 API
(bibtexparser.bparser); v2 removed it and pymatgen does not constrain the
version, so the Python 3.10 job resolved 2.0.1 and eight pre-existing
tests (mv.prop.cost, prototype matching) failed at import while 3.11/3.12
happened to resolve 1.x. Pin it in the dev extra the workflow installs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Starlitnightly
Starlitnightly merged commit 5145447 into main Sep 12, 2026
6 checks passed
@Starlitnightly
Starlitnightly deleted the feat/pl-phonon-pourbaix-neb-md-wulff-chempot branch September 12, 2026 17:35
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