croak 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- croak-0.1.0/.github/workflows/ci.yml +61 -0
- croak-0.1.0/.github/workflows/release.yml +47 -0
- croak-0.1.0/.gitignore +36 -0
- croak-0.1.0/.python-version +1 -0
- croak-0.1.0/.readthedocs.yaml +20 -0
- croak-0.1.0/.zenodo.json +29 -0
- croak-0.1.0/AGENTS.md +98 -0
- croak-0.1.0/CHANGELOG.md +347 -0
- croak-0.1.0/CITATION.cff +45 -0
- croak-0.1.0/LICENSE +21 -0
- croak-0.1.0/PKG-INFO +321 -0
- croak-0.1.0/README.md +283 -0
- croak-0.1.0/croak/__init__.py +255 -0
- croak-0.1.0/croak/_jax_pulse.py +705 -0
- croak-0.1.0/croak/_optimistix.py +142 -0
- croak-0.1.0/croak/cli.py +207 -0
- croak-0.1.0/croak/cmaes.py +483 -0
- croak-0.1.0/croak/collection.py +646 -0
- croak-0.1.0/croak/constants.py +11 -0
- croak-0.1.0/croak/copra.py +434 -0
- croak-0.1.0/croak/copra_jax.py +482 -0
- croak-0.1.0/croak/covariance.py +637 -0
- croak-0.1.0/croak/data/materials/FrantaSiO2.csv +7411 -0
- croak-0.1.0/croak/data/mirrors/HD120.csv +2401 -0
- croak-0.1.0/croak/data/mirrors/HD59_GDD.dat +2478 -0
- croak-0.1.0/croak/data/mirrors/MgF2Al_00deg.csv +10008 -0
- croak-0.1.0/croak/data/mirrors/MgF2Al_45deg.csv +10008 -0
- croak-0.1.0/croak/data/mirrors/PC147.txt +2402 -0
- croak-0.1.0/croak/data/mirrors/PC1611.txt +2402 -0
- croak-0.1.0/croak/data/mirrors/PC1821.txt +2401 -0
- croak-0.1.0/croak/data/mirrors/PC70_GDD.csv +2401 -0
- croak-0.1.0/croak/data/mirrors/PC70_R.csv +2788 -0
- croak-0.1.0/croak/data/mirrors/Si_00deg.csv +10009 -0
- croak-0.1.0/croak/data/mirrors/Si_45deg.csv +10009 -0
- croak-0.1.0/croak/data/mirrors/Si_74deg.csv +10009 -0
- croak-0.1.0/croak/data/mirrors/UCxx-15FS_GD.csv +226 -0
- croak-0.1.0/croak/data/mirrors/UCxx-15FS_R.csv +1373 -0
- croak-0.1.0/croak/dispersion.py +351 -0
- croak-0.1.0/croak/focal.py +430 -0
- croak-0.1.0/croak/forward.py +385 -0
- croak-0.1.0/croak/forward_jax.py +862 -0
- croak-0.1.0/croak/gases.py +169 -0
- croak-0.1.0/croak/grid.py +190 -0
- croak-0.1.0/croak/gui/__init__.py +73 -0
- croak-0.1.0/croak/gui/__main__.py +10 -0
- croak-0.1.0/croak/gui/base.py +108 -0
- croak-0.1.0/croak/gui/branding.py +96 -0
- croak-0.1.0/croak/gui/canvas.py +154 -0
- croak-0.1.0/croak/gui/help.py +148 -0
- croak-0.1.0/croak/gui/mirror_stack.py +387 -0
- croak-0.1.0/croak/gui/readout.py +241 -0
- croak-0.1.0/croak/gui/resources/croak.svg +37 -0
- croak-0.1.0/croak/gui/stage_dispersion.py +881 -0
- croak-0.1.0/croak/gui/stage_load.py +786 -0
- croak-0.1.0/croak/gui/stage_marginal_check.py +690 -0
- croak-0.1.0/croak/gui/stage_preprocess.py +676 -0
- croak-0.1.0/croak/gui/stage_retrieve.py +1755 -0
- croak-0.1.0/croak/gui/stage_simulated.py +661 -0
- croak-0.1.0/croak/gui/stage_synthetic.py +591 -0
- croak-0.1.0/croak/gui/stage_uncertainty.py +365 -0
- croak-0.1.0/croak/gui/stage_welcome.py +73 -0
- croak-0.1.0/croak/gui/state.py +245 -0
- croak-0.1.0/croak/gui/widgets.py +270 -0
- croak-0.1.0/croak/gui/wizard.py +607 -0
- croak-0.1.0/croak/gui/worker.py +247 -0
- croak-0.1.0/croak/interactions.py +212 -0
- croak-0.1.0/croak/io.py +1151 -0
- croak-0.1.0/croak/lbfgs.py +268 -0
- croak-0.1.0/croak/lbfgs_ad.py +483 -0
- croak-0.1.0/croak/lbfgs_hand.py +240 -0
- croak-0.1.0/croak/lm.py +447 -0
- croak-0.1.0/croak/marginal_checks.py +522 -0
- croak-0.1.0/croak/materials.py +356 -0
- croak-0.1.0/croak/maths.py +300 -0
- croak-0.1.0/croak/metrics.py +148 -0
- croak-0.1.0/croak/metrics_jax.py +161 -0
- croak-0.1.0/croak/mirrors.py +592 -0
- croak-0.1.0/croak/optimistix_lbfgs.py +229 -0
- croak-0.1.0/croak/optimistix_lm.py +436 -0
- croak-0.1.0/croak/pipeline.py +390 -0
- croak-0.1.0/croak/plotting.py +1428 -0
- croak-0.1.0/croak/preprocess.py +1772 -0
- croak-0.1.0/croak/processing.py +1005 -0
- croak-0.1.0/croak/progress.py +278 -0
- croak-0.1.0/croak/pulses.py +443 -0
- croak-0.1.0/croak/refractive_db.py +161 -0
- croak-0.1.0/croak/result.py +137 -0
- croak-0.1.0/croak/retrieve.py +189 -0
- croak-0.1.0/croak/save.py +429 -0
- croak-0.1.0/croak/scripting.py +236 -0
- croak-0.1.0/croak/session/__init__.py +61 -0
- croak-0.1.0/croak/session/dispersion.py +217 -0
- croak-0.1.0/croak/session/engine.py +196 -0
- croak-0.1.0/croak/session/options.py +152 -0
- croak-0.1.0/croak/session/params.py +609 -0
- croak-0.1.0/croak/session/pipeline.py +1242 -0
- croak-0.1.0/croak/session/retarget.py +69 -0
- croak-0.1.0/croak/smearing.py +726 -0
- croak-0.1.0/croak/solver.py +414 -0
- croak-0.1.0/croak/truth_metrics.py +348 -0
- croak-0.1.0/croak/uncertainty.py +1380 -0
- croak-0.1.0/croak/warm_lbfgs.py +159 -0
- croak-0.1.0/docs/Makefile +14 -0
- croak-0.1.0/docs/_static/custom.css +18 -0
- croak-0.1.0/docs/_static/gui_retrieve.png +0 -0
- croak-0.1.0/docs/_static/hero.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_aperture.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_boxcars3d.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_dispersion_failure.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_loop.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_rdw.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_smearing_dd.png +0 -0
- croak-0.1.0/docs/_static/paper/figure_thickness1fs.png +0 -0
- croak-0.1.0/docs/_templates/.gitkeep +0 -0
- croak-0.1.0/docs/conf.py +103 -0
- croak-0.1.0/docs/explanation/algorithms.md +403 -0
- croak-0.1.0/docs/explanation/forward_model.md +321 -0
- croak-0.1.0/docs/explanation/gradients.md +153 -0
- croak-0.1.0/docs/explanation/interactions.md +118 -0
- croak-0.1.0/docs/explanation/marginals.md +226 -0
- croak-0.1.0/docs/explanation/pnps_framework.md +114 -0
- croak-0.1.0/docs/explanation/retrieval_theory.md +140 -0
- croak-0.1.0/docs/explanation/uncertainty_estimation.md +536 -0
- croak-0.1.0/docs/explanation/validation.md +269 -0
- croak-0.1.0/docs/getting_started/gui.md +89 -0
- croak-0.1.0/docs/getting_started/installation.md +147 -0
- croak-0.1.0/docs/getting_started/quickstart.md +108 -0
- croak-0.1.0/docs/howto/collection_aperture.md +217 -0
- croak-0.1.0/docs/howto/fitting_thickness_tau0.md +160 -0
- croak-0.1.0/docs/howto/geometric_smearing.md +262 -0
- croak-0.1.0/docs/howto/grids_and_filtering.md +168 -0
- croak-0.1.0/docs/howto/gui.md +263 -0
- croak-0.1.0/docs/howto/loading_simulated.md +492 -0
- croak-0.1.0/docs/howto/marginal_checks.md +108 -0
- croak-0.1.0/docs/howto/materials_and_mirrors.md +224 -0
- croak-0.1.0/docs/howto/postprocessing.md +250 -0
- croak-0.1.0/docs/howto/preprocessing.md +324 -0
- croak-0.1.0/docs/howto/regularisation.md +199 -0
- croak-0.1.0/docs/howto/saving_and_loading.md +146 -0
- croak-0.1.0/docs/howto/solver_selection.md +146 -0
- croak-0.1.0/docs/howto/uncertainty.md +290 -0
- croak-0.1.0/docs/index.md +197 -0
- croak-0.1.0/docs/make.bat +25 -0
- croak-0.1.0/docs/reference/api/core.md +91 -0
- croak-0.1.0/docs/reference/api/index.md +33 -0
- croak-0.1.0/docs/reference/api/jax.md +40 -0
- croak-0.1.0/docs/reference/api/plotting.md +10 -0
- croak-0.1.0/docs/reference/api/retrieval.md +138 -0
- croak-0.1.0/docs/reference/api/workflow.md +161 -0
- croak-0.1.0/docs/reference/bibliography.md +59 -0
- croak-0.1.0/docs/reference/conventions.md +90 -0
- croak-0.1.0/docs/reference/features.md +222 -0
- croak-0.1.0/docs/reference/fft_convention.md +65 -0
- croak-0.1.0/docs/reference/glossary.md +105 -0
- croak-0.1.0/docs/reference/naming.md +47 -0
- croak-0.1.0/docs/requirements.txt +9 -0
- croak-0.1.0/docs/tutorials/01_first_retrieval.md +194 -0
- croak-0.1.0/docs/tutorials/02_choosing_an_algorithm.md +301 -0
- croak-0.1.0/docs/tutorials/03_dispersive_retrieval.md +183 -0
- croak-0.1.0/docs/tutorials/04_experimental_workflow.md +419 -0
- croak-0.1.0/docs/tutorials/05_dispersion_tuning.md +131 -0
- croak-0.1.0/examples/data/README.md +48 -0
- croak-0.1.0/examples/data/tgfrog_sim_1fs_uvfs.h5 +0 -0
- croak-0.1.0/examples/data/tgfrog_sim_rdw_duv.h5 +0 -0
- croak-0.1.0/examples/example_arpls_baseline.py +132 -0
- croak-0.1.0/examples/example_defringe.py +142 -0
- croak-0.1.0/examples/example_geometric_smearing.py +252 -0
- croak-0.1.0/examples/example_paper_rdw.py +141 -0
- croak-0.1.0/examples/example_paper_thickness.py +160 -0
- croak-0.1.0/examples/example_retrieval.py +125 -0
- croak-0.1.0/examples/example_thickness_uncertainty.py +177 -0
- croak-0.1.0/examples/example_workflow.py +123 -0
- croak-0.1.0/examples/synthetic_duv_trace.py +195 -0
- croak-0.1.0/pyproject.toml +157 -0
- croak-0.1.0/tests/conftest.py +347 -0
- croak-0.1.0/tests/test_ad_vs_analytic.py +320 -0
- croak-0.1.0/tests/test_basis_global.py +356 -0
- croak-0.1.0/tests/test_cli.py +103 -0
- croak-0.1.0/tests/test_collection.py +574 -0
- croak-0.1.0/tests/test_copra.py +156 -0
- croak-0.1.0/tests/test_copra_jax.py +208 -0
- croak-0.1.0/tests/test_covariance.py +319 -0
- croak-0.1.0/tests/test_depth_weight.py +108 -0
- croak-0.1.0/tests/test_dispersion.py +145 -0
- croak-0.1.0/tests/test_focal.py +505 -0
- croak-0.1.0/tests/test_focal_params.py +115 -0
- croak-0.1.0/tests/test_forward.py +141 -0
- croak-0.1.0/tests/test_forward_jax.py +184 -0
- croak-0.1.0/tests/test_gases.py +123 -0
- croak-0.1.0/tests/test_grid.py +72 -0
- croak-0.1.0/tests/test_gui.py +2339 -0
- croak-0.1.0/tests/test_interactions.py +74 -0
- croak-0.1.0/tests/test_io.py +81 -0
- croak-0.1.0/tests/test_io_simulated.py +445 -0
- croak-0.1.0/tests/test_lbfgs.py +113 -0
- croak-0.1.0/tests/test_lbfgs_ad.py +348 -0
- croak-0.1.0/tests/test_live_preview.py +249 -0
- croak-0.1.0/tests/test_lm.py +187 -0
- croak-0.1.0/tests/test_marginal_checks.py +198 -0
- croak-0.1.0/tests/test_materials.py +179 -0
- croak-0.1.0/tests/test_maths.py +52 -0
- croak-0.1.0/tests/test_metrics.py +51 -0
- croak-0.1.0/tests/test_mirrors.py +194 -0
- croak-0.1.0/tests/test_noise.py +93 -0
- croak-0.1.0/tests/test_nonfinite_guard.py +64 -0
- croak-0.1.0/tests/test_optimistix.py +460 -0
- croak-0.1.0/tests/test_pipeline.py +235 -0
- croak-0.1.0/tests/test_pipeline_simulated.py +555 -0
- croak-0.1.0/tests/test_plotting.py +494 -0
- croak-0.1.0/tests/test_preprocess.py +909 -0
- croak-0.1.0/tests/test_processing.py +349 -0
- croak-0.1.0/tests/test_progress.py +135 -0
- croak-0.1.0/tests/test_pulses.py +177 -0
- croak-0.1.0/tests/test_retrieve.py +149 -0
- croak-0.1.0/tests/test_save.py +250 -0
- croak-0.1.0/tests/test_scripting.py +134 -0
- croak-0.1.0/tests/test_session.py +670 -0
- croak-0.1.0/tests/test_smearing.py +700 -0
- croak-0.1.0/tests/test_truth_metrics.py +202 -0
- croak-0.1.0/tests/test_uncertainty.py +644 -0
- croak-0.1.0/tests/test_warm_lbfgs.py +92 -0
- croak-0.1.0/tools/make_docs_images.py +255 -0
- croak-0.1.0/tools/reduce_scansave.py +128 -0
- croak-0.1.0/uv.lock +2252 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# The workflow name is the label on the README's badge.
|
|
2
|
+
name: Tests
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main, master]
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint-type-test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
run: uv python install
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --group dev
|
|
29
|
+
|
|
30
|
+
- name: Ruff lint
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Ruff format check
|
|
34
|
+
run: uv run ruff format --check .
|
|
35
|
+
|
|
36
|
+
# The optional extras ('ridb', 'evo') are deliberately not installed: the
|
|
37
|
+
# refractiveindex.info database is a hundreds-of-megabyte download on first
|
|
38
|
+
# use. Their import sites are guarded, so this only skips their tests.
|
|
39
|
+
- name: Type check
|
|
40
|
+
run: uv run pyright
|
|
41
|
+
|
|
42
|
+
# PyQt6 needs system libraries and a virtual display.
|
|
43
|
+
- name: Install Qt system libraries
|
|
44
|
+
run: |
|
|
45
|
+
sudo apt-get update
|
|
46
|
+
sudo apt-get install -y \
|
|
47
|
+
libegl1 libgl1 libxkbcommon-x11-0 libdbus-1-3 \
|
|
48
|
+
libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
|
|
49
|
+
libxcb-randr0 libxcb-render-util0 libxcb-shape0 \
|
|
50
|
+
libxcb-xinerama0 libxcb-cursor0 xvfb
|
|
51
|
+
|
|
52
|
+
- name: Run tests with coverage
|
|
53
|
+
run: xvfb-run -a uv run pytest --cov-report=xml
|
|
54
|
+
|
|
55
|
+
# Tokenless upload works for public repos; set CODECOV_TOKEN in the repo
|
|
56
|
+
# secrets to avoid rate limits (see the release checklist).
|
|
57
|
+
- name: Upload coverage to Codecov
|
|
58
|
+
uses: codecov/codecov-action@v5
|
|
59
|
+
with:
|
|
60
|
+
files: coverage.xml
|
|
61
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Publishes to PyPI via trusted publishing (OIDC) — no token stored anywhere.
|
|
2
|
+
# Triggered by publishing a GitHub Release whose tag is v<version> (e.g. v0.1.0).
|
|
3
|
+
# One-time setup: a "pending publisher" on PyPI pointing at this repo/workflow/
|
|
4
|
+
# environment, and a GitHub environment named "pypi" (see the release checklist).
|
|
5
|
+
name: Release
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # the OIDC token PyPI trusted publishing exchanges
|
|
17
|
+
contents: read
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
run: uv python install
|
|
26
|
+
|
|
27
|
+
# A tag that disagrees with pyproject.toml would publish a version under
|
|
28
|
+
# the wrong name; fail fast instead.
|
|
29
|
+
- name: Check the tag matches the package version
|
|
30
|
+
run: |
|
|
31
|
+
version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
32
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
33
|
+
echo "tag $GITHUB_REF_NAME does not match pyproject version $version" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
- name: Build sdist and wheel
|
|
38
|
+
run: uv build
|
|
39
|
+
|
|
40
|
+
# Smoke-test the built wheel in a clean environment before publishing.
|
|
41
|
+
- name: Check the wheel imports
|
|
42
|
+
run: |
|
|
43
|
+
uv run --isolated --no-project --with dist/*.whl \
|
|
44
|
+
python -c "import croak; print(croak.__version__)"
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
run: uv publish
|
croak-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv*/
|
|
11
|
+
|
|
12
|
+
# example outputs (regenerate by running the scripts in examples/)
|
|
13
|
+
retrieval.png
|
|
14
|
+
retrieval.h5
|
|
15
|
+
defringe_check.png
|
|
16
|
+
baseline_check.png
|
|
17
|
+
geometric_smearing.png
|
|
18
|
+
thickness_uncertainty.png
|
|
19
|
+
paper_thickness.png
|
|
20
|
+
paper_rdw.png
|
|
21
|
+
|
|
22
|
+
# local agent state
|
|
23
|
+
.claude/
|
|
24
|
+
|
|
25
|
+
# documentation build artifacts
|
|
26
|
+
docs/_build/
|
|
27
|
+
.jupyter_cache/
|
|
28
|
+
*.ipynb_checkpoints/
|
|
29
|
+
|
|
30
|
+
# tool caches
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
|
|
34
|
+
.coverage
|
|
35
|
+
.DS_Store
|
|
36
|
+
qfile_*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Read the Docs configuration for the croak documentation.
|
|
2
|
+
# https://docs.readthedocs.io/en/stable/config-file/v2.html
|
|
3
|
+
version: 2
|
|
4
|
+
|
|
5
|
+
build:
|
|
6
|
+
os: ubuntu-24.04
|
|
7
|
+
tools:
|
|
8
|
+
python: "3.14"
|
|
9
|
+
|
|
10
|
+
sphinx:
|
|
11
|
+
configuration: docs/conf.py
|
|
12
|
+
# The build is warning-free, so treat warnings as errors. This is the only
|
|
13
|
+
# gate on documentation drift: CI does not build the docs, and a broken
|
|
14
|
+
# autodoc/cross-reference degrades to a warning rather than a failure, so
|
|
15
|
+
# without this a silently-broken API reference would publish.
|
|
16
|
+
fail_on_warning: true
|
|
17
|
+
|
|
18
|
+
python:
|
|
19
|
+
install:
|
|
20
|
+
- requirements: docs/requirements.txt
|
croak-0.1.0/.zenodo.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "croak: complete retrieval of ultrashort laser pulses from FROG traces",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"upload_type": "software",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "croak reconstructs the full complex electric field of an ultrashort laser pulse — spectral amplitude and phase — from a delay-scanned nonlinear-process spectrum. It is a general retrieval package for the PNPS class of measurements, currently focused on FROG (SHG, SD and PG/transient-grating), with a differentiable forward model that includes dispersive propagation in the nonlinear medium, geometric delay smearing of the crossed-beam geometry, and the chromatic collection aperture — the physics needed for quantitative retrieval of few-femtosecond deep-ultraviolet pulses. Companion code to: J. C. Travers and C. Brahms, \"Extreme ultrashort pulse retrieval with differentiable physical forward models\" (to be published).",
|
|
7
|
+
"creators": [
|
|
8
|
+
{
|
|
9
|
+
"name": "Travers, John C.",
|
|
10
|
+
"affiliation": "Heriot-Watt University",
|
|
11
|
+
"orcid": "0000-0003-0350-9104"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Brahms, Christian",
|
|
15
|
+
"affiliation": "Heriot-Watt University",
|
|
16
|
+
"orcid": "0000-0002-8009-3547"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ultrafast",
|
|
21
|
+
"FROG",
|
|
22
|
+
"pulse retrieval",
|
|
23
|
+
"frequency-resolved optical gating",
|
|
24
|
+
"transient grating",
|
|
25
|
+
"PNPS",
|
|
26
|
+
"ultrashort pulses",
|
|
27
|
+
"nonlinear optics"
|
|
28
|
+
]
|
|
29
|
+
}
|
croak-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Guiding principles
|
|
4
|
+
|
|
5
|
+
- There should be one obvious way to do each thing. Prefer the existing pattern over inventing a new one; if you find two ways to do the same thing, consolidate them.
|
|
6
|
+
- Optimise for the reader. Clear beats clever — code is read far more often than it is written.
|
|
7
|
+
- Small, single-purpose functions with explicit inputs and outputs. Prefer pure functions; push side effects (I/O, global state, mutation) to the edges.
|
|
8
|
+
- Make the implicit explicit: explicit arguments over hidden state, explicit types on interfaces, explicit errors over silent fallbacks.
|
|
9
|
+
- Fail loudly and early with a clear exception. Never swallow errors or return a sentinel where raising is correct.
|
|
10
|
+
|
|
11
|
+
## Environment and tooling
|
|
12
|
+
|
|
13
|
+
- Python is managed with **uv**. Dependencies are declared in `pyproject.toml`; the resolved lockfile is `uv.lock`. Never `pip install` into the environment by hand.
|
|
14
|
+
- Add dependencies with `uv add <pkg>` (and `uv add --dev <pkg>` for dev/test tools). Sync the environment with `uv sync`.
|
|
15
|
+
- Run everything through the project environment by prefixing commands with `uv run`.
|
|
16
|
+
- Target Python [3.14]; keep it consistent with `requires-python` in `pyproject.toml`.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
- Sync environment: `uv sync`
|
|
21
|
+
- Format: `uv run ruff format .`
|
|
22
|
+
- Lint (with autofix): `uv run ruff check --fix .`
|
|
23
|
+
- Type-check: `uv run pyright`
|
|
24
|
+
- Test: `uv run pytest`
|
|
25
|
+
- Build docs: `uv run sphinx-build -b html docs docs/_build/html`
|
|
26
|
+
|
|
27
|
+
## Definition of done
|
|
28
|
+
|
|
29
|
+
Before treating any change as complete, run these in order and ensure each passes with no new warnings:
|
|
30
|
+
|
|
31
|
+
1. `uv run ruff format .`
|
|
32
|
+
2. `uv run ruff check --fix .`
|
|
33
|
+
3. `uv run pyright`
|
|
34
|
+
4. `uv run pytest`
|
|
35
|
+
|
|
36
|
+
Do not declare work finished while any step fails. If a check genuinely cannot pass for a justified reason, say so explicitly rather than suppressing it.
|
|
37
|
+
|
|
38
|
+
Furthermore, ensure all added functionality is documented both in docstrings *and* in the manual under docs.
|
|
39
|
+
|
|
40
|
+
## Code style
|
|
41
|
+
|
|
42
|
+
- Formatting is owned by `ruff format`; do not hand-format or fight the formatter. Line length is the ruff default (88) unless `pyproject.toml` overrides it.
|
|
43
|
+
- Linting is owned by `ruff check`. Fix the cause rather than silencing the warning. A suppression must be rule-specific with a reason (`# noqa: E501 — URL must stay on one line`), never a blanket `# noqa`.
|
|
44
|
+
- Imports are absolute and sorted by ruff. No wildcard (`from x import *`) imports.
|
|
45
|
+
- Naming: `snake_case` for functions and variables, `PascalCase` for classes, `UPPER_SNAKE` for constants. Names state what a thing is or does; avoid abbreviations beyond well-established domain ones.
|
|
46
|
+
- Use f-strings for interpolation and `pathlib.Path` for filesystem paths. Reach for the standard-library idiom before adding a dependency.
|
|
47
|
+
|
|
48
|
+
## Types
|
|
49
|
+
|
|
50
|
+
- Annotate every public function signature and class attribute. Add types where they clarify intent or catch mistakes; do not annotate obvious locals just to fill space.
|
|
51
|
+
- Code must pass `pyright` in the project's configured mode with no new errors.
|
|
52
|
+
- Prefer precise types: `Sequence`/`Mapping` over `list`/`dict` for read-only parameters, `X | None` over `Optional[X]`, and built-in generics (`list[int]`). Avoid `Any`; if it is unavoidable, confine it to one place and comment why.
|
|
53
|
+
- Use a `@dataclass` (frozen where it can be) for structured data instead of passing loose tuples or dicts around.
|
|
54
|
+
|
|
55
|
+
## Docstrings
|
|
56
|
+
|
|
57
|
+
- Every public module, class, and function has a **NumPy-style** docstring (rendered by Sphinx via the napoleon extension). Private helpers get a one-line docstring when the name is not fully self-explanatory.
|
|
58
|
+
- The first line is an imperative one-sentence summary. Then include, as applicable: `Parameters`, `Returns`, `Raises`, and `Examples`.
|
|
59
|
+
- For numerical quantities, state units and valid ranges in the parameter descriptions.
|
|
60
|
+
- Make `Examples` runnable (doctest style) where practical. Describe behaviour and contracts, not implementation details that will drift.
|
|
61
|
+
|
|
62
|
+
## Comments
|
|
63
|
+
|
|
64
|
+
- Code comments should be comprehensive. They should explain both the maths and physics as well as coding details.
|
|
65
|
+
- Comments cover: rationale, assumptions, references (a paper, an issue link), and non-obvious trade-offs.
|
|
66
|
+
- Keep each comment next to what it describes and update it when the code changes. Delete commented-out code — version history is the archive.
|
|
67
|
+
- Mark deliberate follow-ups as `# TODO(context): ...` so they are greppable.
|
|
68
|
+
|
|
69
|
+
## Functions and modules
|
|
70
|
+
|
|
71
|
+
- One function, one responsibility. If a function needs a paragraph to explain, or has many nested branches, split it.
|
|
72
|
+
- Keep parameter lists short. Group related parameters into a small dataclass or config object rather than passing many positional arguments.
|
|
73
|
+
- Prefer returning new values over mutating arguments in place. Keep the core logic pure and testable; isolate I/O at the boundaries.
|
|
74
|
+
- Organise modules by domain concept, not by a catch-all `utils`. Each module should have a clear, nameable purpose. Declare the public surface explicitly with `__all__` where it helps.
|
|
75
|
+
|
|
76
|
+
## Tests
|
|
77
|
+
|
|
78
|
+
- Tests use **pytest** and live in `tests/`, mirroring the package layout. Name them `test_<unit>_<behaviour>`.
|
|
79
|
+
- Every public function has tests for the normal case, the edge cases, and the error paths. Add a regression test with every bug fix.
|
|
80
|
+
- Tests are deterministic, isolated, and fast: use fixtures for setup, `pytest.mark.parametrize` instead of copy-pasted cases, and no network access or hidden global state.
|
|
81
|
+
- For numerical code, assert with explicit tolerances (`numpy.testing.assert_allclose`, `pytest.approx`) and test invariants and conservation laws, not only point values.
|
|
82
|
+
- Write the test alongside the code; a feature is not done until it is tested.
|
|
83
|
+
|
|
84
|
+
## Documentation
|
|
85
|
+
|
|
86
|
+
- Docs are built with **Sphinx**, and the API reference is generated from docstrings — so the docstring is the source of truth. Keep the `README.md` and any usage guide current with behaviour changes.
|
|
87
|
+
- Record notable changes in `CHANGELOG.md` (Keep a Changelog style).
|
|
88
|
+
- All functionality of this package must be described in the user manual under the docs/ folder. This should include background explanations and context, how the functionality works, and examples
|
|
89
|
+
- When adding functionality, always add documentation.
|
|
90
|
+
- When working on something that does not appear to be documented, check this and add appropriate documentation.
|
|
91
|
+
|
|
92
|
+
## Git
|
|
93
|
+
|
|
94
|
+
- Make clean, logical git commits with descriptive but not overly verbose commit messages.
|
|
95
|
+
- Prefer more frequenct clean commits over large big ones.
|
|
96
|
+
- *Never* push.
|
|
97
|
+
- You can fetch, pull, branch when instructed to do so. If you want to do this, ask.
|
|
98
|
+
- Do not make releases or change release versions
|
croak-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to follow
|
|
5
|
+
semantic versioning.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-08-31
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **`warm-lbfgs` is now the default solver everywhere.** `croak.retrieve`
|
|
14
|
+
(previously `"copra"`), `retrieve_from_tracedata` (previously `"lbfgs"`) and
|
|
15
|
+
the coverage-calibration harness (previously `"copra"`) now default to
|
|
16
|
+
`"warm-lbfgs"`, matching the session pipeline, the GUI and the companion
|
|
17
|
+
paper's production protocol. Pass `algorithm=` explicitly to keep the old
|
|
18
|
+
behaviour.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Companion-paper validation data and examples.** Two reduced datasets from
|
|
23
|
+
the paper's first-principles 3D instrument simulations ship in
|
|
24
|
+
`examples/data/` (the 1 fs fused-silica thickness series and the single-cycle
|
|
25
|
+
RDW pulse), with worked retrievals in `examples/example_paper_thickness.py`
|
|
26
|
+
and `examples/example_paper_rdw.py`, and `tools/reduce_scansave.py` to cut
|
|
27
|
+
such datasets from full `scansave` scans. The documentation's
|
|
28
|
+
[Validation](docs/explanation/validation.md) page now summarises the paper's
|
|
29
|
+
study — model hierarchy, thickness series, geometry and collection results,
|
|
30
|
+
solver protocol and metrics guidance — with its figures.
|
|
31
|
+
|
|
32
|
+
- **GUI-only install guide.** `docs/getting_started/gui.md` walks a non-Python
|
|
33
|
+
user from nothing to a running wizard, linked from the top of the README.
|
|
34
|
+
|
|
35
|
+
- **Release metadata.** README badges (tests, coverage, docs, ruff, pyright,
|
|
36
|
+
Python, licence), a `CITATION.cff`, and Codecov upload in CI.
|
|
37
|
+
|
|
38
|
+
- **Known-truth pulse errors.** New `croak.truth_metrics` scores a retrieval
|
|
39
|
+
against a *known* pulse rather than against the measured trace: `eps_It`
|
|
40
|
+
(temporal intensity), `eps_Iw` (spectral intensity) and `eps_Ew` (Geib's
|
|
41
|
+
complex-field epsilon). Each removes exactly the gauge freedoms a PNPS
|
|
42
|
+
measurement cannot determine — energy scale, absolute phase, delay — and no
|
|
43
|
+
others. The Retrieve stage's numeric read-out shows all three beside the FROG
|
|
44
|
+
error whenever a known pulse is loaded. Read together they localise a failure a
|
|
45
|
+
trace error hides: `eps_Iw` small with `eps_Ew` large is a correct amplitude
|
|
46
|
+
with a wrong phase.
|
|
47
|
+
|
|
48
|
+
- **Live preview during COPRA.** `copra` and `copra-jax` now hand the GUI an
|
|
49
|
+
in-progress result each iteration, so the full 12-panel preview redraws during
|
|
50
|
+
their sweeps instead of only showing a convergence curve. This matters most
|
|
51
|
+
for `warm-lbfgs`, whose 300-iteration COPRA warm-up previously left the
|
|
52
|
+
preview blank for most of the run. The snapshot reuses the iterate the sweep
|
|
53
|
+
already computed, so it costs no extra forward pass.
|
|
54
|
+
|
|
55
|
+
- **Complex ModelPNPS truth as a retrieval seed.** Complex source/beamlet fields
|
|
56
|
+
are retained on `TruthPulse`; `initial_guess(..., mode="truth")`,
|
|
57
|
+
`retrieve_from_tracedata(..., guess="truth", truth=...)`, and
|
|
58
|
+
`RetrieveParams(truth_init=True)` route the known solution into the solver for
|
|
59
|
+
direct forward-model residual checks. The Retrieve GUI now has one mutually
|
|
60
|
+
exclusive initial-guess selector (including **Truth** and **Reuse previous**),
|
|
61
|
+
replacing the conflicting independent checkboxes. Known temporal and spectral
|
|
62
|
+
phases are dotted, named in both legends, and saved with the complex truth.
|
|
63
|
+
|
|
64
|
+
- **Headless install: the GUI is now an extra.** `pyqt6`/`superqt` moved from
|
|
65
|
+
the core dependencies to `croak[gui]`; everything except the wizard —
|
|
66
|
+
retrieval, preprocessing, processing, uncertainty, plotting, the `croak`
|
|
67
|
+
CLI — runs without Qt, so compute nodes install without it (`uv sync
|
|
68
|
+
--no-dev`, or plain `pip install croak`). Launching `croak-gui` without the
|
|
69
|
+
extra raises a message saying how to get it. The dev group still carries Qt
|
|
70
|
+
(the suite includes the GUI tests), so `uv sync` in a checkout is unchanged.
|
|
71
|
+
|
|
72
|
+
- **Multi-aperture scans: every stored window is selectable.**
|
|
73
|
+
`io.read_simulated_window_keys` now discovers all trace windows in the
|
|
74
|
+
file — the canonical trio first, then the numbered windows of an
|
|
75
|
+
aperture-series scan (`Iω_win_2` … with their `_reimaged` partners, one
|
|
76
|
+
propagation reduced through several collection holes) — so the GUI's
|
|
77
|
+
trace-window selector offers them all.
|
|
78
|
+
|
|
79
|
+
- **Aperture selection for multi-window scans** (`window=` on
|
|
80
|
+
`aperture_from_scan` / `read_simulated_mask_window`, plus
|
|
81
|
+
`io.window_def_prefix`). An aperture-series scan records one
|
|
82
|
+
`window_def_N_*` record per collection hole; the selector (the trace-window
|
|
83
|
+
dataset name, `_reimaged` accepted, or the bare index) rebuilds that hole's
|
|
84
|
+
aperture, so the focal+collection model can retrieve any window of the
|
|
85
|
+
series against its own recorded geometry.
|
|
86
|
+
|
|
87
|
+
- **Uniform-delay-core selection at load** (`SimulatedLoadParams.
|
|
88
|
+
uniform_delay_core`, a checkbox on the simulated-load stage, and
|
|
89
|
+
`preprocess.uniform_core_indices`). Campaign scans often extend a uniform
|
|
90
|
+
delay core with coarser wing points (e.g. 1 fs-step wings to ±40 fs for the
|
|
91
|
+
Raman wake); the smearing kernel's delay-axis convolution needs a uniform
|
|
92
|
+
grid, so such scans could not previously be retrieved with the kernel
|
|
93
|
+
modelled. The option keeps the longest uniformly spaced contiguous stretch
|
|
94
|
+
of the axis (off by default — the wings are real data for every
|
|
95
|
+
kernel-free model).
|
|
96
|
+
|
|
97
|
+
- **`focal=` now reaches the high-level pipeline.** `retrieve_from_tracedata`
|
|
98
|
+
(and therefore `croak.session.pipeline.run_retrieval`, via a new keyword)
|
|
99
|
+
accepts a `FocalMixture`, forwarding it to the solvers that can model it and
|
|
100
|
+
raising for those that cannot — closing the gap where the solver
|
|
101
|
+
constructors accepted a mixture but every session-level entry point silently
|
|
102
|
+
could not pass one.
|
|
103
|
+
|
|
104
|
+
- **The focal mixture in `RetrieveParams` and the GUI.** New scalar fields
|
|
105
|
+
(`focal`, `focal_n_radial`/`focal_n_azimuth`/`focal_rmax_units`,
|
|
106
|
+
`focal_f_mm`, `collection`, `collection_mode`, `collection_diam_mm`,
|
|
107
|
+
`spectrum_frame_p`) plus `session.pipeline.focal_mixture_from_params` — the
|
|
108
|
+
focal counterpart of `smearing_kernel` — which `run_retrieval` invokes
|
|
109
|
+
automatically when `focal` is set (pass `scan_path` for
|
|
110
|
+
`collection="file"`, which rebuilds the aperture from the scan's own
|
|
111
|
+
`window_def_*` record; `"manual"` builds a hard-edged pinhole at the
|
|
112
|
+
phase-matched corner). `apply_spectrum_frame` reweights the measured
|
|
113
|
+
spectrum (initial guess and `reg_spectrum` target together) by
|
|
114
|
+
`(ω/ω₀)^{2p}` into the mixture's on-axis frame. The GUI's retrieve stage
|
|
115
|
+
gains a *Chromatic focal mixture (advanced)* group, mutually exclusive with
|
|
116
|
+
the reduced kernel, with the focusing focal length adopted from simulated
|
|
117
|
+
scans' geometry records (`io.SimulatedScan` now reads `f_foc`).
|
|
118
|
+
|
|
119
|
+
- **Finite collection aperture for the focal mixture** (new `croak.collection`,
|
|
120
|
+
reached through `focal_mixture(..., collection=...)`). `croak.focal` sums
|
|
121
|
+
*incoherently* over focal position, which by Parseval is exact only if the
|
|
122
|
+
detector collects the whole signal beam. Most non-collinear instruments pick the
|
|
123
|
+
signal out of the BOXCARS pattern with a hole — a filter in transverse
|
|
124
|
+
wavevector — and a typical DUV instrument's hole is **six times narrower
|
|
125
|
+
than the signal's own k content**, so it sits far closer to the coherent limit
|
|
126
|
+
than to the incoherent one. The new path transforms the focal field to
|
|
127
|
+
transverse k, applies the aperture there and integrates, in either the
|
|
128
|
+
`"integrated"` (spectrometer behind the hole) or `"reimaged"` (on-axis re-imaged
|
|
129
|
+
pixel, the fully coherent limit) model.
|
|
130
|
+
|
|
131
|
+
The `(p, theta)` reduction survives intact: croak's per-node build and the exact
|
|
132
|
+
focal-plane signal differ by a rigid time shift, so the incoherent sum discards
|
|
133
|
+
exactly one phase, and after the signal's phase-matched carrier is removed that
|
|
134
|
+
phase is `exp(+i w p)` — *w times one of the two reduced smearing parameters*
|
|
135
|
+
(`-w theta` for SD, which is not yet implemented). Validated against a dense 2-D
|
|
136
|
+
FFT of the focal field built directly from the arm tilts: 3.5e-7 of peak with no
|
|
137
|
+
fitted scale, plus an exact Parseval test on a Cartesian grid. Costs +5 % of
|
|
138
|
+
runtime at production shape, stays differentiable, and reaches every solver that
|
|
139
|
+
already accepted `focal=`. `collection=None` (the default) leaves the existing
|
|
140
|
+
incoherent sum untouched.
|
|
141
|
+
|
|
142
|
+
- **`aperture_from_scan` / `croak.io.MaskWindowSpec`.** Simulated scans record the
|
|
143
|
+
collection window they were taken through as flattened `/grid/window_def_*`
|
|
144
|
+
scalars; these read it back and rebuild the aperture, resolving the simulator's
|
|
145
|
+
`"default"` apodisation width from the file's own transverse k-grid. That matters:
|
|
146
|
+
for the reference instrument it is a 96.9 µm `tanh` edge on a 500 µm hole — 19 %
|
|
147
|
+
of the diameter — which a top hat does not approximate.
|
|
148
|
+
|
|
149
|
+
- **`croak.focal.mixture_from_nodes`** builds a mixture on arbitrary focal-plane
|
|
150
|
+
nodes, and `FocalMixture` now records the mask hole positions it was built from
|
|
151
|
+
(`arms`, plus a `signal_position` property). The incoherent sum needed only the
|
|
152
|
+
arm *differences* that make `(p, theta)`; the aperture needs to know where the
|
|
153
|
+
signal actually goes.
|
|
154
|
+
|
|
155
|
+
- **Faster `lm-optx` linear solve** (`linear_solver` on
|
|
156
|
+
`croak.optimistix_lm.OptxLM`). Each Levenberg–Marquardt iteration solves
|
|
157
|
+
`[J; sqrt(lambda) I] d = [r; 0]`; the new default `"normal"` forms
|
|
158
|
+
`J^T J + lambda I` and takes its Cholesky factor instead of running
|
|
159
|
+
Optimistix's QR on the stacked operator (still available as `"qr"`). The Gram
|
|
160
|
+
matrix is one BLAS `gemm` at near-peak throughput while a tall-skinny
|
|
161
|
+
Householder QR is panel-bound, so a 512-point PG retrieval drops from 2.9 s to
|
|
162
|
+
1.2 s per iteration at an identical final error. Both routes rely on
|
|
163
|
+
`lambda > 0` — the FROG Jacobian is strongly rank-deficient either way — but
|
|
164
|
+
the normal equations square the condition number, so `"qr"` is kept as an
|
|
165
|
+
escape hatch.
|
|
166
|
+
|
|
167
|
+
- **Complex simulated-scan truth spectra.** `read_simulated_scan` now loads
|
|
168
|
+
the complex vignetted-beamlet spectrum (`Eω_beamlet_re`/`_im`, written by
|
|
169
|
+
newer ModelPNPS runs) and the complex source spectrum onto
|
|
170
|
+
`SimulatedScan.Eomega_beamlet` / `.Eomega` (both `None` for
|
|
171
|
+
intensity-only legacy files). The beamlet phase carries any input chirp
|
|
172
|
+
exactly, enabling complex-field retrieval-error metrics and direct
|
|
173
|
+
truth-GDD measurement instead of intensity-only comparisons.
|
|
174
|
+
|
|
175
|
+
- **Generation-envelope weights** (`depth_weight` on
|
|
176
|
+
`croak.forward_jax.make_param_trace_fn`, the `lbfgs-ad` solver and
|
|
177
|
+
`retrieve_from_tracedata`, which raises rather than silently dropping it on
|
|
178
|
+
unsupported solvers): optional complex per-node weights multiplying the
|
|
179
|
+
depth quadrature, modelling the transverse geometry a 1D depth integral
|
|
180
|
+
cannot see (focal-spot evolution across the slab, beamlet walk-through, Gouy
|
|
181
|
+
phase). The session layer exposes the analytic Gaussian-crossing form via
|
|
182
|
+
`RetrieveParams.envelope*` and `croak.session.pipeline.generation_envelope`.
|
|
183
|
+
Uniform weights are a verified no-op; the envelope matters only when the
|
|
184
|
+
slab is not much thinner than the beams' effective depth of focus
|
|
185
|
+
(`docs/explanation/forward_model.md`) — note that for aperture-masked
|
|
186
|
+
beamlets that scale is set by diffraction (`~lambda (f/D)^2`), not by the
|
|
187
|
+
imaged-source Rayleigh range.
|
|
188
|
+
|
|
189
|
+
- **Split-channel smearing fit** (`fit_smearing_split`, AD solvers): fit the
|
|
190
|
+
gate-shape (`p`) and delay (`delta`) widths of the geometric-smearing kernel
|
|
191
|
+
as two independent multipliers instead of the single joint one — the
|
|
192
|
+
diagnostic for *which* channel deviates from the geometric prediction. The
|
|
193
|
+
split is exact (the bivariate kernel scales channel-wise at fixed `rho`);
|
|
194
|
+
the fitted values land in `RetrievalResult.smear_scale` (p) and the new
|
|
195
|
+
`smear_scale_delta` (delta), are saved/reloaded by `croak.save`, and
|
|
196
|
+
`parameter_covariance` reports `sigma_smear_delta` alongside `sigma_smear`.
|
|
197
|
+
Note the `p` multiplier is only identifiable for a chirped gate (a
|
|
198
|
+
transform-limited gate's `p` response is absorbed by the intensity scale).
|
|
199
|
+
|
|
200
|
+
- **`croak.save.save_result(..., group=…)`** writes the unchanged flat result
|
|
201
|
+
schema into a named HDF5 group instead of the file root, appending rather than
|
|
202
|
+
truncating, so a parameter study (a thickness series, a solver comparison) can
|
|
203
|
+
keep every run in one file — one group each, nesting with `"full/z00"`-style
|
|
204
|
+
names. `force=True` then replaces only that group. `load_result` already
|
|
205
|
+
recurses into subgroups, so such a file reads back as a nested dict.
|
|
206
|
+
- **Retrieval-quality diagnostics for spectral energy at the band edges.** The
|
|
207
|
+
outermost frequency bins of the retrieval grid carry no measurement (`regrid`
|
|
208
|
+
tapers them to zero), so the field there is nearly unconstrained and — since
|
|
209
|
+
intensity is bounded below by zero — can only drift upward. Two new measures
|
|
210
|
+
catch it:
|
|
211
|
+
- {func}`croak.processing.edge_energy_fraction`, also carried on
|
|
212
|
+
`ProcessedResult.edge_energy` and printed in the `plot_retrieval` spectrum
|
|
213
|
+
panel (red above `croak.plotting.EDGE_ENERGY_WARN`, 1 %).
|
|
214
|
+
- `TraceData.taper_loss`, the fraction of the measured trace the edge taper
|
|
215
|
+
removed. `load_and_clean`/`regrid` now warn above 1 % (`taper_warn_level`),
|
|
216
|
+
which is the load-time signature of a `lam_min`/`lam_max` band that clips.
|
|
217
|
+
- `croak.preprocess.TAPER_COLLAR_BINS` names the taper width, previously an
|
|
218
|
+
unexplained literal `10` in two places.
|
|
219
|
+
|
|
220
|
+
### Changed
|
|
221
|
+
|
|
222
|
+
- **Simulated-scan delay convention is now auto-detected.**
|
|
223
|
+
`SimulatedLoadParams.reverse_trace` defaults to `None` (auto): scansave
|
|
224
|
+
files that carry the new `/grid/delay_convention = "gate"` marker (written
|
|
225
|
+
by ModelPNPS runs that store the trace directly in the gate-delay/paper
|
|
226
|
+
frame) load without delay-axis reversal, while legacy marker-less files are
|
|
227
|
+
negated exactly as before. An explicit `True`/`False` still overrides.
|
|
228
|
+
`SimulatedScan` gained the `delay_convention` attribute.
|
|
229
|
+
|
|
230
|
+
- `regrid` returns a seventh element, the taper loss.
|
|
231
|
+
- **Renamed `ProcessedResult.Iw_photon` to `ProcessedResult.Ilam`.** The
|
|
232
|
+
quantity is the spectral intensity as a *wavelength density* (`|Ẽ(ω)|²ω²`,
|
|
233
|
+
the λ→ω Jacobian applied), not a photon-flux weighting; the new name pairs it
|
|
234
|
+
with `ProcessedResult.wavelength` and matches the saved `Ilam_retr` dataset.
|
|
235
|
+
- Documented that `R_omega=True` must not be used without `reg_spectrum` or
|
|
236
|
+
`phase_only`: per-frequency scaling discards the frequency marginal, which is
|
|
237
|
+
what pins the retrieved spectral amplitude, and it *lowers* the reported trace
|
|
238
|
+
error while doing so. On a perfect synthetic PG trace it raises the edge energy
|
|
239
|
+
from 0.02 % to 5.4 %.
|
|
240
|
+
|
|
241
|
+
### Fixed
|
|
242
|
+
|
|
243
|
+
- **Three-argument progress callbacks work with every solver.** The documented
|
|
244
|
+
signature is `callback(iteration, R, best_R, snapshot=None)`, but the shorter
|
|
245
|
+
form worked with `copra`/`copra-jax` (which never passed a snapshot) and failed
|
|
246
|
+
with `lbfgs-ad` — so which forms were legal depended on the solver, and giving
|
|
247
|
+
COPRA a snapshot to offer would have broken existing code. `Retriever.run` now
|
|
248
|
+
adapts a callback that cannot receive a snapshot instead of raising.
|
|
249
|
+
|
|
250
|
+
- **`warm-lbfgs` reported only half of its own convergence.** The COPRA warm-up's
|
|
251
|
+
error log was discarded, so the curve began partway down with no account of the
|
|
252
|
+
work that got it there, and progress restarted from iteration 1 at the
|
|
253
|
+
handover. Both stages are now spliced into one history, with the join recorded
|
|
254
|
+
in the new `RetrievalResult.stage_boundaries` and drawn as a dashed rule —
|
|
255
|
+
necessary because the halves do not count the same unit of work (a COPRA
|
|
256
|
+
iteration is a full local sweep; an L-BFGS step is one function evaluation).
|
|
257
|
+
Its results also identify themselves as `warm-lbfgs` instead of inheriting
|
|
258
|
+
`lbfgs-ad`, which had sent an uncertainty bootstrap off to re-run replicates
|
|
259
|
+
with the cold solver.
|
|
260
|
+
|
|
261
|
+
- **Native-grid simulated loads quantised time zero to a delay bin.**
|
|
262
|
+
`assemble_simulated_tracedata` located the delay marginal's peak by taking its
|
|
263
|
+
largest *sample*, so a scan whose true zero delay falls between two bins — an
|
|
264
|
+
even, symmetric delay axis does exactly that — was modelled up to half a delay
|
|
265
|
+
step away from where it was measured. It now uses
|
|
266
|
+
`preprocess.marginal_peak_delay`, the sub-sample helper `regrid` already used
|
|
267
|
+
and which its own docstring describes this defect in. A retrieval absorbed the
|
|
268
|
+
shift into a linear spectral phase, so retrieved pulses were unaffected beyond
|
|
269
|
+
a time translation, but any comparison against an un-translated known field
|
|
270
|
+
paid for it in full: the truth-seeded forward-model check floored at `R ~ 5e-3`
|
|
271
|
+
and now reaches `~1e-16`.
|
|
272
|
+
|
|
273
|
+
- **`make_trace_fn(focal=...)` was silently ignored** when no `smearing` kernel was
|
|
274
|
+
also passed: the single-delay entry point fell through to the unsmeared model and
|
|
275
|
+
said nothing. Retrievals were unaffected (they go through
|
|
276
|
+
`make_param_trace_fn`), but a forward-model call was.
|
|
277
|
+
|
|
278
|
+
- **NLopt failures no longer abort retrievals.** The `lbfgs` and `lbfgs-ad`
|
|
279
|
+
solvers previously died with a bare `nlopt.runtime_error` when NLopt's
|
|
280
|
+
internal line search failed (observed for roughly one in six random starts
|
|
281
|
+
on large dispersive grids). They now return the best iterate found, with a
|
|
282
|
+
one-time warning; a non-finite objective or gradient is additionally trapped
|
|
283
|
+
and replaced by a large finite penalty (`croak.solver.guard_nonfinite`) so
|
|
284
|
+
the line search backtracks.
|
|
285
|
+
|
|
286
|
+
- Two docstring examples that did not match their own output
|
|
287
|
+
(`peak_wavelength`, `marginal_peak_delay`).
|
|
288
|
+
- A batch of docstring/docs statements that had drifted from the code:
|
|
289
|
+
`croak.processing.edge_energy_fraction` cited a function that does not exist
|
|
290
|
+
(`taper_collar_signal`; the real load-time guard is `regrid`'s
|
|
291
|
+
`taper_warn_level` warning plus `TraceData.taper_loss`); the `croak.lm` module
|
|
292
|
+
docstring opened by claiming the MINPACK `method="lm"` default that the rest
|
|
293
|
+
of the docstring and the code contradict (the default is `"trf"` with the JAX
|
|
294
|
+
Jacobian); `retrieve_from_tracedata` omitted `"copra-jax"` and `"cma-es"`
|
|
295
|
+
from its documented algorithm set and described `R_omega` as L-BFGS-only (all
|
|
296
|
+
solvers support it, as `docs/explanation/retrieval_theory.md` now also
|
|
297
|
+
says); `compute_mu_per_freq`/`mu_per_freq` now document that the weights
|
|
298
|
+
cancel identically (the per-frequency factors are weight-independent; a
|
|
299
|
+
zero-weight row falls back to `1.0`); `croak/__init__.py` and the README
|
|
300
|
+
claimed all three geometries propagate dispersively (dispersive SHG raises —
|
|
301
|
+
PG/SD only); `docs/explanation/forward_model.md` listed only the Sellmeier
|
|
302
|
+
materials (missing `SiO2-Franta`); and `croak.solver`'s docstring described
|
|
303
|
+
`Retriever` as the base of two solvers rather than of all of them.
|
|
304
|
+
|
|
305
|
+
## [0.1.0] — unreleased
|
|
306
|
+
|
|
307
|
+
First public release.
|
|
308
|
+
|
|
309
|
+
croak was developed privately before being open-sourced, and that history was
|
|
310
|
+
squashed for release, so this changelog starts here. The design rationale worth
|
|
311
|
+
keeping lives in the documentation: see [Explanation](docs/explanation/) for
|
|
312
|
+
the physics and algorithms, and the [how-to guides](docs/howto/) for the
|
|
313
|
+
practical trade-offs.
|
|
314
|
+
|
|
315
|
+
### Added
|
|
316
|
+
|
|
317
|
+
Everything. The initial release provides:
|
|
318
|
+
|
|
319
|
+
- **Retrieval.** Nine solvers over one forward model, selected by an `algorithm=`
|
|
320
|
+
string: `copra`, `copra-jax`, `lbfgs`, `lbfgs-hand`, `lbfgs-ad`, `lbfgs-optx`,
|
|
321
|
+
`lm`, `lm-optx` and `cma-es`. Four algorithm families — COPRA, L-BFGS,
|
|
322
|
+
Levenberg–Marquardt and a derivative-free CMA-ES global search — with the rest
|
|
323
|
+
on-device JAX reimplementations.
|
|
324
|
+
- **Forward model.** SHG, SD and PG (transient-grating) interactions, thin or
|
|
325
|
+
propagated through a dispersive medium by Gauss–Legendre depth quadrature.
|
|
326
|
+
- **Gradients both ways.** A JAX model differentiable end to end, plus a
|
|
327
|
+
hand-derived Wirtinger adjoint for the core model, cross-checked against each
|
|
328
|
+
other to ~1e-6.
|
|
329
|
+
- **Geometric time smearing.** The pulse-front-tilt instrument response of a
|
|
330
|
+
non-collinear BOXCARS geometry, as a two-parameter kernel that can be computed
|
|
331
|
+
from the mask or fitted from the data.
|
|
332
|
+
- **Materials and dispersion.** Built-in Sellmeier materials, tabulated measured
|
|
333
|
+
index, pressure-scaled gases, seven chirped-mirror designs and five beam-path
|
|
334
|
+
coatings, plus an optional bridge to refractiveindex.info.
|
|
335
|
+
- **Workflow.** Loading (HDF5/NPZ/CSV, unit and axis detection, simulated scans),
|
|
336
|
+
preprocessing (fringe and DC filtering, Takeda de-fringing, arPLS baseline
|
|
337
|
+
removal, regridding), marginal consistency checks, post-processing, dispersion
|
|
338
|
+
tuning, plotting and saving.
|
|
339
|
+
- **Uncertainty.** Parametric and resampling bootstraps, a substrate-thickness
|
|
340
|
+
systematic, fast Laplace covariance, coverage calibration, and propagation of
|
|
341
|
+
an interval to another point in the beamline.
|
|
342
|
+
- **Interfaces.** A library-first API, a PyQt6 wizard GUI, a headless session
|
|
343
|
+
replay engine, and a `croak` CLI that replays a saved session or generates a
|
|
344
|
+
standalone script from it.
|
|
345
|
+
|
|
346
|
+
[Unreleased]: https://github.com/LupoLab/croak/compare/v0.1.0...HEAD
|
|
347
|
+
[0.1.0]: https://github.com/LupoLab/croak/releases/tag/v0.1.0
|