Skip to content

Implement strict fixed-source-state calibration and synthetic calibration coverage#8

Merged
benjym merged 5 commits into
mainfrom
copilot/plan-stress-optic-coefficients
Mar 16, 2026
Merged

Implement strict fixed-source-state calibration and synthetic calibration coverage#8
benjym merged 5 commits into
mainfrom
copilot/plan-stress-optic-coefficients

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 16, 2026

The calibration flow now treats the incoming source polarisation S_i_hat as a fixed input rather than an optimised parameter, and adds a systematic synthetic test suite to validate the new behaviour across noiseless, noisy, linear, and circular-source cases.

  • Scope

    • Implements the calibration change requested in review feedback instead of only documenting a redesign.
    • Updates the fitter, config semantics, and calibration tests while preserving the existing saved profile shape for downstream compatibility.
    • Tightens the calibration interface so source-state configuration is explicit and there are no fallback or compatibility aliases.
  • Implemented model changes

    • Stop optimising S_i_hat during calibration.
    • Resolve S_i_hat from configuration and hold it fixed while fitting only C[λ].
    • Keep circular source states supported by treating them as known fixed acquisition inputs rather than inferred calibration variables.
    • Retain S_i_hat in the saved profile as the fixed source state used for the calibration run.
  • Config and API changes

    • Require fit.S_i_hat as the explicit fixed source-state input used during calibration.
    • Remove support for the legacy fit.initial_S_i_hat name; configs using it now fail with a direct error.
    • Remove default/no-load-derived S_i_hat fallback behaviour from the implemented calibration path.
    • Remove the fit.s3_identifiability_threshold default/config dependency from the implemented calibration path.
    • Keep initial_C as the optimiser seed input when provided.
  • Stress-optic coefficient handling

    • Fit only the per-wavelength stress-optic coefficients C.
    • Continue to support existing bounds and prior configuration for C through fit.c_relative_bounds and fit.prior_weight.
  • Pipeline and schema impact

    • Preserve the current profile schema so downstream consumers continue to read:
      • C
      • S_i_hat
      • blank correction
      • fit metrics / provenance
    • Remove the previous calibration fallback concepts from the implemented fitter path and its tests.
  • Synthetic calibration test coverage

    • Add a systematic synthetic calibration suite that:
      • generates disk and coupon calibration image stacks
      • optionally injects detector/image noise
      • runs the full calibration workflow end to end
      • validates fixed-source calibration for both linear and circular input states
      • verifies strict config validation for missing or legacy S_i_hat inputs
    • Extend coverage to verify diagnostics/profile outputs still write successfully.
  • Documentation

    • Update configuration docs to describe fit.S_i_hat as a required fixed source state and note that calibration now fits only C.
    • Remove legacy/backward-compatible wording from the calibration config documentation.
# implemented calibration semantics

params = {
    "initial_C": [2.0e-9, 2.0e-9, 2.0e-9],
    "S_i_hat": [0.0, 0.0, 1.0],  # required fixed source state, including circular input if known
}
Original prompt

have a detailed think about how calibration works. we need our guess for the stress optic coefficients at multiple points in the pipeline. we also don't measure the last stokes parameter at all, so trying to use a measured value is destined for failure. what is optimal here? make a plan only, no implementation. breaking changes, no backwards compatibility


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] Plan for optimal calibration of stress optic coefficients Define a breaking-change calibration redesign plan around identifiable linear Stokes inputs Mar 16, 2026
Copilot AI requested a review from benjym March 16, 2026 19:43
@benjym
Copy link
Copy Markdown
Owner

benjym commented Mar 16, 2026

@copilot that's a good start. This will primarily be used with circularly polarised input states, will that work?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 16, 2026

@copilot that's a good start. This will primarily be used with circularly polarised input states, will that work?

Yes, if the source state is known to be circular. The adjustment I’d make to the plan is: don’t remove circular input from the forward model, remove it from the fitted unknowns. In other words, keep S3 as fixed acquisition metadata (for example circular_left / circular_right or an explicit fixed source Stokes vector) and calibrate C against the measured S1/S2 response. What won’t work is trying to infer S3 itself from the 4-angle measurement. Current head: b47c857.

@benjym
Copy link
Copy Markdown
Owner

benjym commented Mar 16, 2026

@copilot ok let's just assume a fixed S_i_hat input that isn't optimised.

I also want to add a systematic test suite that generates synthetic images, optionally adds some noise, and runs the calibration.

Let's go ahead an implement please

Copilot AI and others added 2 commits March 16, 2026 20:07
Co-authored-by: benjym <3380296+benjym@users.noreply.github.com>
Co-authored-by: benjym <3380296+benjym@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 16, 2026

@copilot ok let's just assume a fixed S_i_hat input that isn't optimised.

I also want to add a systematic test suite that generates synthetic images, optionally adds some noise, and runs the calibrat...

Implemented. Calibration now treats S_i_hat as a fixed source input and only optimises C, with fit.S_i_hat supported (and fit.initial_S_i_hat kept as a legacy alias). I also added a systematic synthetic calibration suite that generates disk and coupon images, exercises fixed-source calibration with and without noise, and includes circular-source coverage. Full suite is green on f60b833.

Copilot AI changed the title Define a breaking-change calibration redesign plan around identifiable linear Stokes inputs Implement fixed-source-state calibration and synthetic calibration coverage Mar 16, 2026
@benjym
Copy link
Copy Markdown
Owner

benjym commented Mar 16, 2026

@copilot can you remove the fallback to a default s_i_hat everywhere and just make it fail without it? Also remove redundant legacy variable names, just one variable name per thing. No fallbacks or backwards compatibility, just a clean interface

Copilot AI and others added 2 commits March 16, 2026 20:17
Co-authored-by: benjym <3380296+benjym@users.noreply.github.com>
Co-authored-by: benjym <3380296+benjym@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 16, 2026

@copilot can you remove the fallback to a default s_i_hat everywhere and just make it fail without it? Also remove redundant legacy variable names, just one variable name per thing. No fallbacks or ba...

Done on c1aa426. Calibration now requires explicit fit.S_i_hat, removes the default/no-load-derived source-state fallback path, and rejects the legacy fit.initial_S_i_hat name with a direct error. The calibration tests/docs were updated to match, and the full suite is still green.

Copilot AI changed the title Implement fixed-source-state calibration and synthetic calibration coverage Implement strict fixed-source-state calibration and synthetic calibration coverage Mar 16, 2026
@benjym benjym marked this pull request as ready for review March 16, 2026 20:28
Copilot AI review requested due to automatic review settings March 16, 2026 20:28
@benjym benjym merged commit 7900f4e into main Mar 16, 2026
9 checks passed
@benjym benjym deleted the copilot/plan-stress-optic-coefficients branch March 16, 2026 20:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the calibration workflow to treat the incoming polarisation state (S_i_hat) as an explicit fixed input, fitting only per-wavelength stress-optic coefficients (C), and aligns tests/docs with that new contract.

Changes:

  • Require fit.S_i_hat in calibration configs; reject legacy fit.initial_S_i_hat.
  • Remove no-load-derived/default S_i_hat seeding and simplify the optimizer to fit C only.
  • Update synthetic calibration tests and user configuration documentation accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
photoelastimetry/calibrate.py Enforces explicit fixed fit.S_i_hat, removes S_i_hat fitting/initialisation, and fits C only.
tests/test_calibrate.py Updates synthetic fixtures and tests for fixed S_i_hat and new validation behavior.
docs/user/configuration.md Documents fit.S_i_hat as a required calibration input and clarifies fitting behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

)
fit_s_i_hat = np.asarray(fit_cfg["S_i_hat"], dtype=float)
if fit_s_i_hat.shape != (3,):
raise ValueError(f"fit.S_i_hat must have shape (3,), got {fit_s_i_hat.shape}.")
Comment on lines 370 to +387
fit_cfg = dict(config.get("fit", {}))
if "initial_S_i_hat" in fit_cfg:
raise ValueError("fit.initial_S_i_hat is no longer supported; use fit.S_i_hat.")
if "S_i_hat" not in fit_cfg:
raise ValueError(
"Calibration config must include fit.S_i_hat as a 3-component array " "[S1_hat, S2_hat, S3_hat]."
)
fit_s_i_hat = np.asarray(fit_cfg["S_i_hat"], dtype=float)
if fit_s_i_hat.shape != (3,):
raise ValueError(f"fit.S_i_hat must have shape (3,), got {fit_s_i_hat.shape}.")

fit_cfg.setdefault("max_points", 6000)
fit_cfg.setdefault("loss", "soft_l1")
fit_cfg.setdefault("f_scale", 0.05)
fit_cfg.setdefault("max_nfev", 300)
fit_cfg.setdefault("seed", 0)
fit_cfg.setdefault("s3_identifiability_threshold", 0.02)
fit_cfg.setdefault("prior_weight", 0.0)
fit_cfg["S_i_hat"] = fit_s_i_hat.tolist()
Comment on lines +873 to +878
def _resolve_fixed_s_i_hat(fit_config):
"""Resolve the fixed incoming Stokes state used during calibration."""
s_i_hat = np.asarray(fit_config["S_i_hat"], dtype=float)
if s_i_hat.shape != (3,):
raise ValueError(f"fit.S_i_hat must have shape (3,), got {s_i_hat.shape}.")
return _normalise_s_i_hat(s_i_hat)
- `fit.S_i_hat` as an explicit 3-component fixed source state `[S1_hat, S2_hat, S3_hat]`

`fit.S_i_hat` is treated as a fixed source state during calibration; the
optimiser only fits `C`. Calibration does not provide a default `S_i_hat`.
@codecov-commenter
Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

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.

4 participants