Skip to content

Add three-stage multilayer-grating optimization workflow - #45

Merged
simonevadi merged 1 commit into
developfrom
feature/multilayer-optimization-workflow
Sep 4, 2026
Merged

simonevadi merged 1 commit into
developfrom
feature/multilayer-optimization-workflow

Conversation

@simonevadi

Copy link
Copy Markdown
Contributor

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:

  1. D-spacing — grazing angle at the target energy and CFF, converted with
    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.
  2. Gamma — at the resolved d-spacing, scan the bilayer thickness ratio and
    keep the value with the highest peak reflectivity at the target energy.
  3. Blaze — build the multilayer-coated blazed grating and scan the blaze
    angle, running run_multilayer_theta_search_sweep per blaze angle, then keep
    the angle with the highest selected-order efficiency at the target energy.

Stages hand values forward only through optimization_state.json, and only when
a config value is the string "auto". A numeric config value always wins and no
stage rewrites the config; gamma is not auto-propagated into stage 2.

New source

  • src/grax/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 still pulls in neither xrt nor matplotlib.pyplot.
  • src/grax/multilayer_optimization.py — frozen
    MultilayerOptimizationConfig plus run_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.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.
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 and
sweep-recipes toctrees; a choosing-a-solver table row; a module-guide entry; a
build_docs.sh image 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 full
    three-stage state accretion.
  • tests/unit/test_import_side_effects.py — import grax pulls in neither xrt
    nor matplotlib.pyplot (subprocess check).
  • A small real stage-0 smoke run + an assets-exist check in
    tests/smoke/test_simulation_examples.py.

Full tests/unit + tests/smoke: green. Real end-to-end stage-0 run against XRT
produces geometry d ~= 3.82 nm -> suggested 3.8 nm.

Note

The first two commits (afb1c79, 5ac5d5c — progress-bar dynamic_ncols and
its 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_deg
below 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

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
simonevadi force-pushed the feature/multilayer-optimization-workflow branch from 73ed456 to 37141b0 Compare September 4, 2026 12:28
@simonevadi
simonevadi merged commit d09fc14 into develop Sep 4, 2026
1 check passed
@simonevadi
simonevadi deleted the feature/multilayer-optimization-workflow branch September 4, 2026 12: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