Add per-layer roughness support and fix macOS live-plot/worker-memory bugs - #38
Merged
Conversation
Roughness was global per-grating; a single RoughnessSpec.sigma_nm was applied to every interface. This adds per-layer roughness, where each layer carries the roughness of its top interface, falling back to the grating-level sigma when unset (backward compatible). - LayerSpec gains roughness_sigma_nm; SingleLayerStack/MultilayerStack/ CustomStack expose per-layer/per-material roughness arguments. - BaseStack.interface_roughness_sigmas_bottom_up() / has_per_layer_roughness() build a per-interface sigma array used by both roughness kinds. - gratings._roughness_interface_offset() perturbs each interface by its own sigma; the underresolved warning uses the max per-interface sigma. - Per-layer debye-waller sigmas combine in quadrature into an effective scalar; a bare RoughnessSpec is unchanged. - Fixed-angle roughness example uses the per-layer system (uniform sigma across layers); tutorial + CHANGELOG updated; new test_stacks.py plus per-layer/quadrature tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
Add per-interface roughness fields to the grating form and a run-time roughness-kind dropdown, wiring the existing per-layer roughness core through the Flask app. - stacks: add BaseStack.substrate_roughness_sigma_nm so the substrate boundary (interface 0) can be set explicitly; wire it into interface_roughness_sigmas_bottom_up() / has_per_layer_roughness() and the build_* helpers. - persistence: serialize/deserialize substrate, layer/material, and top cap roughness sigmas; single-layer gratings now carry a coating stack so the sigmas round-trip. Bump SCHEMA_VERSION to 2; old specs without roughness keys still load (default None). - grating form: roughness sigma input for substrate, single layer, material A, material B, and top cap; parsed via _optional_float. - run form: None / Debye-Waller / Random interface dropdown. Attached to the grating in _execute_run_job via _attach_roughness(), covering every workflow and resume (run resolution clones preserve .roughness). - tests: substrate-override stacks tests; persistence round-trip, backward-compat load, and _attach_roughness web tests; schema v2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
Add a geometry-only example that builds one laminar grating with an extreme random-interface roughness (sigma = 100 nm) on the internal Cr/C interface and exports a close-up image of the distorted profile. The internal interface is used (rather than the top surface) so the full excursion is visible instead of clipping at the top of the z-grid. Also restrict the fixed_angle_roughness close-up export to the geometry-distorting random-interface runs; Debye-Waller close-ups (which do not change the geometry) are skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
Set substrate_roughness_sigma_nm to the same per-layer sigma so the substrate/coating boundary (interface 0) is roughened too, instead of falling back to the flat default. Previously only each layer's top interface carried roughness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
Replace the per-sample white-noise interface with a periodic Gaussian random field with a Gaussian autocorrelation, characterised by a lateral correlation length. White noise put power at every spatial frequency up to the grid Nyquist, giving unphysical per-pixel spikes; real metrology shows a clear characteristic lateral scale. - RoughnessSpec.correlation_length_nm: autocorrelation length in nm. None -> period/10 (visible per-period structure at any pitch); 0.0 -> legacy white noise; >0 -> explicit length. - gratings: synthesise the offset from a Gaussian power spectrum via rFFT on the periodic domain, then normalise to sigma (per-layer sigma semantics unchanged). Deterministic per seed+interface. - Tests: correlated field is far smoother than white noise, default correlation length tracks the period, 0.0 reproduces white noise, plus validation. Docs + changelog updated, incl. the ~10 um metrology reference and the note that long correlations suit debye-waller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEdvY6MWmJDeEm22pwbTRA
…amples Library: RoughnessSpec gains num_supercells so random-interface roughness can span multiple grating periods as one continuous correlated field; the RCWA solver's fundamental period scales accordingly and diffraction orders are reported in physical (possibly fractional) units end to end. Examples: split the fixed-angle roughness example into a roughness-kind comparison and a roughness-correlation-length comparison, each with their own results directory, whole-grating (not close-up) geometry plots, and a bash runner. The correlation-length script now also sweeps num_supercells=5 and overlays it on the same comparison plot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The parallel BatchSimulationRunner path serializes case results through JSON records; deserialization cast the orders array back with dtype=int, truncating fractional supercell orders (e.g. -1.2, -1.4, -1.6, -1.8 all collapsed onto -1). The all-orders CSV then contained several near-zero rows labeled order -1 per energy alongside the real one, which made the comparison plot zigzag to zero between energy points for supercell runs. Deserialize orders as float, apply the integer-preserving CSV order formatting to the theta-search sweep writer too, and align the empty placeholder arrays. Adds a round-trip regression test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
roughness_kind_comparison.py now saves, for each run, a diffraction-order-vs- intensity plot at the energy closest to the middle of the sweep, showing the full order spectrum (specular plus any diffuse satellite orders from supercell roughness). Across both roughness example scripts, all figure outputs (grating previews, order-spectrum plots, comparison plots) now live in plots_roughness_* folders instead of being mixed into results_roughness_*, which now hold only CSV data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both roughness_kind_comparison.py and roughness_correlation.py accept
--family {baseline,debye-waller,random-interface} to run only that family
instead of the full sweep. The final comparison plot now scans the results
directory for whatever CSVs already exist on disk (rather than only this
invocation's runs), so running families one at a time still builds up a
complete comparison once all of them have been run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rent config Library: RoughnessSpec gains num_realizations (default 8 for random-interface, forced to 1 for debye-waller) to average incoherent disorder realizations per simulated point, and seed now accepts None to draw real entropy once at construction time. run_simulation transparently averages efficiencies across realizations while keeping orders/angles (which don't depend on the specific roughness draw) from a single solve. Examples: both roughness scripts stop hardcoding a fixed seed (None draws real, non-reproducible randomness) and expose ROUGHNESS_NUM_REALIZATIONS. The roughness-kind comparison plot (inline and standalone) now only includes CSVs matching the script's current run configuration instead of globbing every matching file in the results directory, so stale sigma/supercell sweeps left over from earlier configs no longer leak into the plot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _refresh_interactive_figure only calls plt.show() once per figure instead of on every redraw, so the macosx backend stops re-raising the window and the live-plot window can be minimized/backgrounded during a run. - Force the spawn multiprocessing start method on macOS (fork is unsafe once BLAS/Numba/Cocoa native state is initialized) and size auto worker counts from each solve's peak RSS high-water mark, not just steady-state RSS. - Re-run the roughness-kind comparison example with supercells=10 and let its comparison plot always try supercell counts 1/3/5/10 regardless of the script's current sweep config. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
random-interfaceroughness kind, and corresponding Web UI fields.live_plotbatch runs kept re-raising/focusing the plot window on every point, making it impossible to minimize.spawninstead offork) and improvesautoworker-count memory calibration to use each solve's peak RSS instead of only steady-state RSS.CHANGELOG.md(Unreleased section) with all of the above.Test plan
pytest tests/unit -q— 319 passed_refresh_interactive_figureonly callsplt.show()once per figure across repeated refreshes🤖 Generated with Claude Code