Add three-stage multilayer-grating optimization workflow - #45
Merged
Merged
Conversation
Ports the d-spacing / gamma / blaze design workflow into graxPy as first-class package code, tests, one example and docs. New source: - multilayer_reflectivity.py: MultilayerReflectivity, a thin wrapper over XRT's dynamical-diffraction engine for planar-multilayer peak Bragg reflectivity vs energy (prescan, peak selection, FWHM in deg and eV). xrt (already a hard dependency) is imported lazily inside _xrt_materials, so `import grax` stays free of xrt and matplotlib.pyplot. - multilayer_optimization.py: frozen MultilayerOptimizationConfig plus run_d_spacing_study / run_gamma_study / run_blaze_study, each returning a typed result. Stages hand values forward only through optimization_state.json and only when a config value is "auto"; numeric values always win and no stage rewrites the config. Stage 0 derives the d-spacing from the grating geometry (grazing angle at the configured CFF, then the first-order Bragg law); stage 2 builds the multilayer-coated blazed grating and drives run_multilayer_theta_search_sweep per blaze angle. Both are re-exported from grax. Example: examples/simulation/multilayer_optimization_rub4c/ (ru_b4c_parameters.py builds the config; three numbered scripts run the stages; run_all.sh chains them), registered in examples/simulation/run_all.sh and EXAMPLE_SCRIPT_PATHS. Docs: new API page (docs/api/simulation/multilayer-optimization.md) and tutorial (docs/tutorials/multilayer-optimization.md), wired into the simulation API and sweep-recipes toctrees; choosing-a-solver table row; module guide entry; build_docs.sh image copy; CHANGELOG entry. Tests: test_multilayer_reflectivity.py (XRT stubbed), test_multilayer_optimization.py (reflectivity + sweep faked, modelled on test_grax_opt_resume.py), test_import_side_effects.py (import grax pulls in neither xrt nor pyplot), a small real stage-0 smoke run and an assets-exist check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
simonevadi
force-pushed
the
feature/multilayer-optimization-workflow
branch
from
September 4, 2026 12:28
73ed456 to
37141b0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Ports the d-spacing / gamma / blaze multilayer-grating design workflow from the
BESSY III beamline study into graxPy as first-class package code: source, tests,
one runnable Ru/B4C example, and docs.
The workflow sizes a periodic multilayer coating for a blazed grating
monochromator working in a chosen diffraction order at fixed CFF, in three
stages sharing one
MultilayerOptimizationConfig:the first-order Bragg law to a bilayer d-spacing; a practical 0.1 nm-rounded
candidate grid (guaranteed to contain the rounded geometry value) is scanned
with XRT planar-multilayer reflectivity. The geometry value is stored as
d_suggested_nm; the numerically best d is a separate diagnostic.keep the value with the highest peak reflectivity at the target energy.
angle, running
run_multilayer_theta_search_sweepper blaze angle, then keepthe angle with the highest selected-order efficiency at the target energy.
Stages hand values forward only through
optimization_state.json, and only whena config value is the string
"auto". A numeric config value always wins and nostage rewrites the config; gamma is not auto-propagated into stage 2.
New source
src/grax/multilayer_reflectivity.py—MultilayerReflectivity, a thinwrapper over XRT's dynamical-diffraction engine for planar-multilayer peak
Bragg reflectivity vs energy (prescan, peak selection, FWHM in deg and eV).
xrt(already a hard dependency) is imported lazily inside_xrt_materials,so
import graxstill pulls in neitherxrtnormatplotlib.pyplot.src/grax/multilayer_optimization.py— frozenMultilayerOptimizationConfigplusrun_d_spacing_study/run_gamma_study/run_blaze_study, each returning a typed result dataclass.Both are re-exported from
grax.Example
examples/simulation/multilayer_optimization_rub4c/—ru_b4c_parameters.pybuilds the config; three numbered scripts run the stages;
run_all.shchainsthem. Registered in
examples/simulation/run_all.shandEXAMPLE_SCRIPT_PATHS.No optical-constant data files needed (named materials + densities).
Docs
New API page (
docs/api/simulation/multilayer-optimization.md) and tutorial(
docs/tutorials/multilayer-optimization.md), wired into the simulation API andsweep-recipes toctrees; a
choosing-a-solvertable row; a module-guide entry; abuild_docs.shimage copy plus the committed stage-0 plot; a CHANGELOG entry.Tests
tests/unit/test_multilayer_reflectivity.py— XRT stubbed.tests/unit/test_multilayer_optimization.py— reflectivity and sweep faked,modelled on
test_grax_opt_resume.py; covers the pure helpers,resolve_configured_value,_rounded_d_grid, each stage, and the fullthree-stage state accretion.
tests/unit/test_import_side_effects.py—import graxpulls in neitherxrtnor
matplotlib.pyplot(subprocess check).tests/smoke/test_simulation_examples.py.Full
tests/unit+tests/smoke: green. Real end-to-end stage-0 run against XRTproduces geometry d ~= 3.82 nm -> suggested 3.8 nm.
Note
The first two commits (
afb1c79,5ac5d5c— progress-bardynamic_ncolsandits test-double fix) also appear in #44; they drop out once either PR merges.
Not addressed: two numerical conventions inherited unchanged from the original
workflow (the E->lambda constant differing from
monochromator_grazing_angles_degbelow the 0.1 nm rounding, and the stack-orientation asymmetry between the XRT
and graxPy paths) are documented in the tutorial rather than changed.
🤖 Generated with Claude Code