Skip to content

Optical wave field calculation, resolution correction, and CTR performance work - #65

Merged
tifuchs merged 38 commits into
masterfrom
DWBA
Jul 29, 2026
Merged

Optical wave field calculation, resolution correction, and CTR performance work#65
tifuchs merged 38 commits into
masterfrom
DWBA

Conversation

@tifuchs

@tifuchs tifuchs commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

Adds optical wave field calculation for CTR modeling, built around a new interface-based optical (delta/beta) profile — a building block toward distorted-wave Born approximation (DWBA) support, not yet a full DWBA implementation. Also includes a chain of correctness fixes, Wyckoff-parameter handling improvements, and performance work needed to make it practical.

Physics / scientific behavior

  • Add optical wave field calculation from an interface-based delta/beta profile (CTRoptics.py), replacing/reducing profile numerical complexity vs. the prior approach. This lays groundwork for full DWBA but does not itself implement the distorted-wave scattering amplitude.
  • Add simple CTR resolution correction (CTRresolution.py).
  • Add Poisson surface unit cell handling for roughness/occupancy modeling.
  • Add Wyckoff setters with compact symmetry metadata; fix Wyckoff parameters to be fitted as absolute (user-facing) while remaining relative internally — this changes fitted parameter values for existing Wyckoff-parameterized models, please re-check saved fits that rely on relative Wyckoff parameters.
  • Fix a gap in zDensity calculation.
  • Fix atomic coordinate stacking in split_in_layers().
  • Add epitaxial film coherence handling along z.

Performance

  • New C++ zDensity backend.
  • Cache atomic form factors and scattering factor lookups.
  • Statically link xxhash for binary releases (build system).

Other

  • Jupyter notebook visualization of atomistic models via py3dmol.
  • Expanded test coverage: test_CTRcalc.py (+1204 lines), new test_CTRoptical_profile.py, test_CTRresolution.py, test_scattering_factor_cache.py.
  • Documentation: new ctr_resolution.rst, updates to ctr_structure_factors.rst, wyckoff_fitting.rst, changelog/release notes.

Notes for reviewers

  • 2fa9e43 changes Wyckoff parameter fitting convention (absolute vs. relative) — flagged as user-facing behavior change per repo convention
  • The DWBA branch name is a working name; this PR delivers the wave field calculation piece, not the full distorted-wave scattering amplitude.
  • Branch already has master merged in (cf2a59b), so this should merge/rebase cleanly with 0 commits behind.

tifuchs added 30 commits July 12, 2026 15:56
The two-parameter PoissonSurface basis was only ever used in test
code, so no migration path is added — this just documents the
change in the Unreleased changelog entry.
UnitCell.F_bulk's semi-infinite geometric lattice sum used the raw,
untransformed l index instead of the refHKLTransform-converted index
in its attenuation phase, in both the numba/C++ and plain-Python
paths. This was only correct for the default case of a component
using its own bulk cell as the reference (no reference_uc set); any
explicit reference_uc whose out-of-plane axis differs from the
bulk's -- including a plain scale difference, not just rotation or
reindexing -- was affected. Present in all releases up to and
including v1.5.0.

Document the correct convention and the fix in
ctr_structure_factors.rst, note it as a breaking change alongside
the also-unreleased PoissonSurface and Wyckoff convention changes in
the changelog, and regenerate release_notes.rst.

BREAKING CHANGE: UnitCell.F_bulk now uses the refHKLTransform-
converted l index in its lattice-sum denominator. Bulk CTR
amplitudes computed with an explicit reference_uc whose out-of-plane
axis differs from the bulk's will change.
Wyckoff parameter fitting's delta->absolute convention change
(2fa9e43) was covered for newly-created parameters but not for
loading a pre-fix, value_kind="delta" parameter dict through
parametersFromDict/updateFromParameters. Add a round-trip regression
test that simulates the legacy dict shape (no reference_value,
stored value is the delta) and asserts it reproduces the same basis
as the current absolute-value convention.
test_legacy_xtal_uses_corrected_interface_support's fresh reference
values (added previously) disagree with the pre-existing
CTRs_reference.dat baseline by ~2.6%, and it's unclear yet which is
correct. Rather than picking one, keep both paths visible:

- Restore the original pre-beee738 test verbatim (minus two
  assertions on removed legacy internals unrelated to the physics
  question) as test_legacy_xtal_reconstructs_reference_interface,
  comparing against the unmodified CTRs_reference.dat. This fails
  now, intentionally, so the discrepancy stays visible instead of
  being silently dropped.
- Add test_simple_unitcell_bulk_structure_factor_matches_reference,
  reusing the already-validated testdata/0V12_calculated.{xpr,dat}
  fixtures (real fit-derived reference intensities, already used by
  TestCTRcalculationNumPy/Numba/Cpp) for the un-layered unit-cell +
  bulk case. This passes, which localizes the CTRs_reference.dat
  discrepancy to the layered Film/EpitaxyInterface/SkellamProfile
  path rather than the underlying unit-cell or semi-infinite bulk
  calculation.

Determining which of CTRs_reference.dat or the current code is
correct is deferred until other PR feedback is addressed.
_readDispersion_cached's key includes a continuous energy value, so
functools.cache could grow without limit over an energy scan in a
long-running batch job, unlike the new byte-budgeted C++
FormFactorCache in this PR. Switch it to functools.lru_cache with a
generous maxsize. _readWaasmaier_cached is left on plain @cache
since its key space (element/ion names) is inherently small and
bounded.
find_grid was a linear scan over all cached Q-grids; touch (called
on every cache hit) was a linear scan over all cached species across
every grid -- the bigger hotspot of the two. Add a hash-bucketed
index for find_grid (bucketed by the low 64 bits of the Q-grid's
hash, still verified by full 128-bit hash + memcmp within a bucket,
so a bucket collision can only cost extra comparisons, never a wrong
result) and store each CachedSpecies's own list iterator so touch
can splice it directly. Verified with a 500-call stress test at a
tiny cache budget (473 evictions) with no crashes and correct
hit/miss behavior, plus the existing cpp-backend test suite.
Install the extendedfilesupport and symmetry extras via cibuildwheel's
CIBW_TEST_EXTRAS (pip install <wheel>[extras]).
tifuchs added 8 commits July 28, 2026 01:07
Correct distributed bulk replacement and add strain_coupling and offset controls for transitions between independent and fully strain-coupled lattices.
Propagate the resulting geometry through films, stacked components, structure factors, density profiles, serialization, and fitting.
Preserve v1.5.0 compatibility by loading legacy two-parameter text and HDF5 records with strain_coupling=1 and offset=0.
Document the model with equations, interpretation guidance, RuO2/TiO2 density plots, and an executable CTR example notebook.
Preserve areal optical content under normal strain and prevent
transitive merging of layers beyond the z tolerance.
Solve angle arrays with NumPy-vectorized wavefield propagation and
preserve finite p-polarized behavior at exact critical angles.
Use ionic forward scattering factors for charged species, quote Meson
setup arguments correctly, and preserve multidimensional angle shapes.
@tifuchs
tifuchs merged commit 5b10b4f into master Jul 29, 2026
4 checks passed
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