Repository navigation
feat(inelastic): refine band-bottom tables in lindhard run (#339) - #355
Conversation
The rate refinement now tests each cell at its quartiles and midpoint and, where those pass, at the vertex of the parabola through the largest sampled error and its two neighbours (parabolic interpolation of the peak). The midpoint alone (#351) missed in four tables because the error peaks off the midpoint; the quartiles alone still left the Al single pole at the se_yield.py grid at 1.0008 %. No constant is tuned: tolerance and window are #291's, the quartiles are the next bisection level, and the vertex is parameter-free. A cell exhausted to rounding is still an error. With it every refined table of the `rate` mode passes the dense 1 % gate (Al and Si, single pole and Mermin, 5 and 10 eV, 10 to 80 per decade), so `lindhard run` builds the inelastic table of every material with a band with RateRefinement::default(). The elastic table and the tables of bandless materials are unchanged. The table-cache key gains inelastic.rate_refinement (KEY_VERSION 4), a cached inelastic table must contain the run's grid in order, and electron_tables.csv matches elastic rows by energy. Committed δ(E) tables are stale pending #287. Closes #339 Loom-Issue: #339 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
Judge pass: still carries a fresh Stand-down passes against this claim: 3 of 3 (streak cap met); claim age 14m of 30m (age floor NOT met). The bounded fallback force-reclaims only once BOTH are met (#9927). This comment is edited in place on each pass rather than reposted (#5123, #6514). |
Judge verdict: approved (head 648744d)Reviewed against #339 (including the operator decision), CLAUDE.md and CONTRIBUTING.md. CI: all checks pass. Merge state: CLEAN. 1. Parabolic-vertex step and the operator's approvalThis is within the approval. The operator approved "several interior points per cell" that is stricter, non-empirical and has no tuned constants. Quartiles plus midpoint is a fixed dyadic set: each quartile is bitwise the midpoint the next bisection would test, and a unit test checks this. The vertex is the standard parabolic-interpolation step (Brent 1973, cited) and has no parameter. It only adds a test point, so it can split more cells but never fewer, which makes the criterion stricter. The stated reason holds. Quartiles alone left the Al SP 5 eV/10 ppd peak between Q1 (0.84 %) and the midpoint (0.99 %). Choosing a denser fixed set to catch that one cell would be choosing the count from the result. Correctness: the centre sample 2. Exhaustion and
|
Closes #339
Follow-up to #351, which added the library option (Part of #339). The operator approved replacing the midpoint-only test with a stricter test at several points per cell that uses no tuned constants (#339 comments).
Criterion
A cell overlapping E - E_F = 2..20 eV is bisected at its midpoint when the linear interpolation of the exact rate misses it by more than 1 % at any test point:
rate_test_points). Each quartile is formed as the midpoint of a half, so it is bitwise the point the next bisection would test. The set is fixed and dyadic, and no count was tuned.rate_error_peak). This is the vertex of the parabola through the largest sampled signed error and its two neighbours, with the error at the cell ends exactly zero. It is parabolic interpolation of an extremum (the step in Brent's method) and has no parameters. The neighbours never exceed the centre sample in magnitude, so the vertex lies within half a spacing of it.Why step 2 is needed: with quartiles alone (measured first), the Al single pole at the se_yield grid still missed at 1.0008 % (6.1 eV) and Al Mermin from 10 eV at 10 per decade at 1.031 %. In the first case the peak lies between the first quartile (0.84 %) and the midpoint (0.99 %) of the cell 5.3..7.2 eV. A denser fixed set would need sixteenths to catch it, which would be fitting the sample count to this case. The vertex finds the peak directly.
When a cell still to be tested has a midpoint that rounds to one of its ends, the build still errors (the message now says "failed at a test point"). The
max_rowserror now names the failing test point.Acceptance (
inelastic_low_energyratemode, dense 0.1 eV gate over 2..20 eV, 1 %)Largest error (E - E_F where it falls); rows = coarse bracketing rows + rows added:
The three non-acceptance grids that failed before:
All 32 refined tables of the mode pass (5 and 10 eV × 10/20/40/80 ppd × Al/Si × SP/Mermin). Inputs are the se_yield.py Al and Si
defaultinputs at 400 eV. Runs were done one at a time withRAYON_NUM_THREADS=2(Al about 8 min, Si about 1.3 min). No δ(E) runs.CLI switch (all cells pass, so it landed)
lindhard-cli/src/electron.rs: newpub fn rate_refinement(r, m)returnsSome(RateRefinement::default())on the band-bottom axis andNoneotherwise.inelastic_tableuses it.tables_csvlooks elastic rows up by energy, so the elastic column is empty at the added rows.lindhard-cli/src/table_cache.rs: newinelastic.rate_refinementkey field (Debug of the option,nullwithout a band), computed by the same function as the build.KEY_VERSIONis now 4. The inelasticlookupgrid check is now "the cached grid contains the run's grid in order, bit-exact" (contains_in_order); the elastic check stays exact.key_covers_every_table_input_fieldnow asserts the field, and there are new testskey_carries_the_rate_refinementandgrid_containment_is_in_order_and_bit_exact.lindhard-cli/tests/examples.rs: the reuse test asserts that the example's refined inelastic table has more energies than its elastic table. It already checks that built, stored and cached runs on 1, 4 and 8 threads are bit-identical. The misses test now covers an inelastic cache entry whose grid no longer contains the run's grid: it is refused ("does not contain"), and accepted again once restored.rate_refinementisNone), so they are byte-identical. The existing bandless Cu test still assertst.energy_ev() == r.table_energy_ev.band, and the committed δ(E) tables are stale until Re-run the secondary-yield tables and the electron oracle summaries with the band-bottom inelastic tables of #241 #287. The "Added" entry for feat(inelastic): rate refinement of band-bottom tables above E_F (part of #339) #351 is updated.docs/validation.md"Rate refinement (Resolve the inelastic rate from E_F upward on the band-bottom axis: extra table rows above the Fermi level #339)" has the new table and says this settles the Electron table grid: the default 10 points per decade under-resolves the inelastic rate just above the Fermi level on the band-bottom axis #291 grid question (the se_yield grid needs no change).docs/cli.mdcovers the tables, the cache key and lookup, andelectron_tables.csv.Notes for review
penn-fulland a band this adds build time; full Penn was not run (memory constraint).elf_min, inside the window. The refinement would then bisect toward that onset and fail with the "exhausted to rounding" error. No shipped ELF does this: the lowest energy is at most 0.5 eV (Si Yang 2019 and the synthetic example). I can file this as a follow-up if wanted.Verification
cargo fmt --all --check: okcargo clippy --workspace --all-targets -- -D warnings: okcargo test -p lindhard --lib: 326 passed (new:rate_test_points_are_the_quartiles_and_midpoint,rate_error_peak_is_the_vertex_of_the_parabola_at_the_largest_sample; the window test now checks every test point)cargo test -p lindhard --test penn_inelastic_table --test mermin_inelastic --test electron_secondaries --test golden --test electron_bench_determinism: 18 / 12 / 34 / 3 / 1 passedcargo test -p lindhard-cli --lib: 28 passed (including table_cache and the thread-count check inrate_refinement_is_the_same_on_any_thread_count)cargo test -p lindhard-cli --test examples: 38 passedpython3 validation/update_docs.py --check: matches🤖 Generated with Claude Code
loom dashboard