Skip to content

fix(genx): SiO2 under the film in the exp05-07 fitting engines - #16

Merged
NaNIsaac merged 1 commit into
mainfrom
fix/genx-oxide-order
Oct 27, 2026
Merged

NaNIsaac merged 1 commit into
mainfrom
fix/genx-oxide-order

Conversation

@NaNIsaac

Copy link
Copy Markdown
Owner

Needs a physics check: this changes the GenX fitting model (layer order) of the exp05–07 engines and exp07's fit stages.

The GenX fitting engines now use the stack the training data were simulated with: Si / SiO2 / film / air, the native oxide under the film (simul_genx.build_sample). This is item 2 of #9. Item 1, the background, was fixed in #11.

  • runs/exp05_1layer_mask/fitting_engine.py, runs/exp06_1lay_6out/fitting_engine.py, runs/exp07_fourier_hires/engine/script_builder.py: Stack(Layers=[SiO2, Film]). GenX lists a stack from the substrate up, so the old [Film, SiO2] was Si / film / SiO2.
  • runs/exp07_fourier_hires/engine/fitting.py: the final stage also frees set_s_sig. The first stage fits it against the unrefined film, and leaving it fixed afterwards keeps the fit in a local minimum even with the order fixed (genx_fit: oxide roughness fixed after stage 1 keeps the fit off the true minimum NaNIsaac-Research/ReflectoLearn#58 has the numbers). exp05 and exp06 already free every oxide parameter in their second stage, so they are unchanged there.
  • src/reflecto_exp/simulate/simul_genx.py: the docstring said "Si Substrate -> Film -> Surface SiO2", the opposite of what the code builds. Only the docstring changes.

Not changed: the notebooks. runs/exp05_1layer_mask/notebooks/ml_fitting_real.ipynb has its own copy of the old [Film, SiO2] script, and its saved outputs came from it. The fits recorded in the exp04–exp06 notebooks were made with the old order and were not rerun.

Tests (tests/test_genx_layer_order.py)

  • The simulator and the three fitting engines all give thicknesses [0, 15 (SiO2), 300 (film), 0] from the substrate up.
  • test_exp07_fit_recovers_a_film_on_buried_oxide: a curve of air / film (200 Å, σ 4, SLD 40) / SiO2 (20 Å, σ 3, SLD 18.8) / Si. It comes from an independent float64 Parratt recursion in the test, smeared with the script's resolution. exp07's GenXFitter starts from a guess 3% off and must return thickness within 1%, roughness within 10%, film SLD within 5%, oxide thickness within 6 Å and oxide SLD within 1. About 23 s, pop_size=20.

Mutation check: with the old order in the three engines, the three engine tests and the refit test fail, and the simulator test still passes.

local-ci: 0c21d76 | pytest 32 passed, 1 skipped (genx3 3.8.11, bumps 1.0.5, CPU torch, Python 3.13), ruff 0.17.1 check and format, conventions.py check 0 findings | notebooks not rerun

…engines

GenX lists a stack from the substrate up, so Stack(Layers=[Film, SiO2]) was Si / film / SiO2,
while simul_genx, which made the training data, builds Si / SiO2 / film. exp07's final stage also
left s_sig at its first-stage value (NaNIsaac-Research/ReflectoLearn#58). The simul_genx docstring
named the opposite order.

Fixes #9
@NaNIsaac
NaNIsaac merged commit 8a9d161 into main Oct 27, 2026
6 checks passed
@NaNIsaac
NaNIsaac deleted the fix/genx-oxide-order branch October 27, 2026 17:30
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