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.
Files changed (224) hide show
  1. croak-0.1.0/.github/workflows/ci.yml +61 -0
  2. croak-0.1.0/.github/workflows/release.yml +47 -0
  3. croak-0.1.0/.gitignore +36 -0
  4. croak-0.1.0/.python-version +1 -0
  5. croak-0.1.0/.readthedocs.yaml +20 -0
  6. croak-0.1.0/.zenodo.json +29 -0
  7. croak-0.1.0/AGENTS.md +98 -0
  8. croak-0.1.0/CHANGELOG.md +347 -0
  9. croak-0.1.0/CITATION.cff +45 -0
  10. croak-0.1.0/LICENSE +21 -0
  11. croak-0.1.0/PKG-INFO +321 -0
  12. croak-0.1.0/README.md +283 -0
  13. croak-0.1.0/croak/__init__.py +255 -0
  14. croak-0.1.0/croak/_jax_pulse.py +705 -0
  15. croak-0.1.0/croak/_optimistix.py +142 -0
  16. croak-0.1.0/croak/cli.py +207 -0
  17. croak-0.1.0/croak/cmaes.py +483 -0
  18. croak-0.1.0/croak/collection.py +646 -0
  19. croak-0.1.0/croak/constants.py +11 -0
  20. croak-0.1.0/croak/copra.py +434 -0
  21. croak-0.1.0/croak/copra_jax.py +482 -0
  22. croak-0.1.0/croak/covariance.py +637 -0
  23. croak-0.1.0/croak/data/materials/FrantaSiO2.csv +7411 -0
  24. croak-0.1.0/croak/data/mirrors/HD120.csv +2401 -0
  25. croak-0.1.0/croak/data/mirrors/HD59_GDD.dat +2478 -0
  26. croak-0.1.0/croak/data/mirrors/MgF2Al_00deg.csv +10008 -0
  27. croak-0.1.0/croak/data/mirrors/MgF2Al_45deg.csv +10008 -0
  28. croak-0.1.0/croak/data/mirrors/PC147.txt +2402 -0
  29. croak-0.1.0/croak/data/mirrors/PC1611.txt +2402 -0
  30. croak-0.1.0/croak/data/mirrors/PC1821.txt +2401 -0
  31. croak-0.1.0/croak/data/mirrors/PC70_GDD.csv +2401 -0
  32. croak-0.1.0/croak/data/mirrors/PC70_R.csv +2788 -0
  33. croak-0.1.0/croak/data/mirrors/Si_00deg.csv +10009 -0
  34. croak-0.1.0/croak/data/mirrors/Si_45deg.csv +10009 -0
  35. croak-0.1.0/croak/data/mirrors/Si_74deg.csv +10009 -0
  36. croak-0.1.0/croak/data/mirrors/UCxx-15FS_GD.csv +226 -0
  37. croak-0.1.0/croak/data/mirrors/UCxx-15FS_R.csv +1373 -0
  38. croak-0.1.0/croak/dispersion.py +351 -0
  39. croak-0.1.0/croak/focal.py +430 -0
  40. croak-0.1.0/croak/forward.py +385 -0
  41. croak-0.1.0/croak/forward_jax.py +862 -0
  42. croak-0.1.0/croak/gases.py +169 -0
  43. croak-0.1.0/croak/grid.py +190 -0
  44. croak-0.1.0/croak/gui/__init__.py +73 -0
  45. croak-0.1.0/croak/gui/__main__.py +10 -0
  46. croak-0.1.0/croak/gui/base.py +108 -0
  47. croak-0.1.0/croak/gui/branding.py +96 -0
  48. croak-0.1.0/croak/gui/canvas.py +154 -0
  49. croak-0.1.0/croak/gui/help.py +148 -0
  50. croak-0.1.0/croak/gui/mirror_stack.py +387 -0
  51. croak-0.1.0/croak/gui/readout.py +241 -0
  52. croak-0.1.0/croak/gui/resources/croak.svg +37 -0
  53. croak-0.1.0/croak/gui/stage_dispersion.py +881 -0
  54. croak-0.1.0/croak/gui/stage_load.py +786 -0
  55. croak-0.1.0/croak/gui/stage_marginal_check.py +690 -0
  56. croak-0.1.0/croak/gui/stage_preprocess.py +676 -0
  57. croak-0.1.0/croak/gui/stage_retrieve.py +1755 -0
  58. croak-0.1.0/croak/gui/stage_simulated.py +661 -0
  59. croak-0.1.0/croak/gui/stage_synthetic.py +591 -0
  60. croak-0.1.0/croak/gui/stage_uncertainty.py +365 -0
  61. croak-0.1.0/croak/gui/stage_welcome.py +73 -0
  62. croak-0.1.0/croak/gui/state.py +245 -0
  63. croak-0.1.0/croak/gui/widgets.py +270 -0
  64. croak-0.1.0/croak/gui/wizard.py +607 -0
  65. croak-0.1.0/croak/gui/worker.py +247 -0
  66. croak-0.1.0/croak/interactions.py +212 -0
  67. croak-0.1.0/croak/io.py +1151 -0
  68. croak-0.1.0/croak/lbfgs.py +268 -0
  69. croak-0.1.0/croak/lbfgs_ad.py +483 -0
  70. croak-0.1.0/croak/lbfgs_hand.py +240 -0
  71. croak-0.1.0/croak/lm.py +447 -0
  72. croak-0.1.0/croak/marginal_checks.py +522 -0
  73. croak-0.1.0/croak/materials.py +356 -0
  74. croak-0.1.0/croak/maths.py +300 -0
  75. croak-0.1.0/croak/metrics.py +148 -0
  76. croak-0.1.0/croak/metrics_jax.py +161 -0
  77. croak-0.1.0/croak/mirrors.py +592 -0
  78. croak-0.1.0/croak/optimistix_lbfgs.py +229 -0
  79. croak-0.1.0/croak/optimistix_lm.py +436 -0
  80. croak-0.1.0/croak/pipeline.py +390 -0
  81. croak-0.1.0/croak/plotting.py +1428 -0
  82. croak-0.1.0/croak/preprocess.py +1772 -0
  83. croak-0.1.0/croak/processing.py +1005 -0
  84. croak-0.1.0/croak/progress.py +278 -0
  85. croak-0.1.0/croak/pulses.py +443 -0
  86. croak-0.1.0/croak/refractive_db.py +161 -0
  87. croak-0.1.0/croak/result.py +137 -0
  88. croak-0.1.0/croak/retrieve.py +189 -0
  89. croak-0.1.0/croak/save.py +429 -0
  90. croak-0.1.0/croak/scripting.py +236 -0
  91. croak-0.1.0/croak/session/__init__.py +61 -0
  92. croak-0.1.0/croak/session/dispersion.py +217 -0
  93. croak-0.1.0/croak/session/engine.py +196 -0
  94. croak-0.1.0/croak/session/options.py +152 -0
  95. croak-0.1.0/croak/session/params.py +609 -0
  96. croak-0.1.0/croak/session/pipeline.py +1242 -0
  97. croak-0.1.0/croak/session/retarget.py +69 -0
  98. croak-0.1.0/croak/smearing.py +726 -0
  99. croak-0.1.0/croak/solver.py +414 -0
  100. croak-0.1.0/croak/truth_metrics.py +348 -0
  101. croak-0.1.0/croak/uncertainty.py +1380 -0
  102. croak-0.1.0/croak/warm_lbfgs.py +159 -0
  103. croak-0.1.0/docs/Makefile +14 -0
  104. croak-0.1.0/docs/_static/custom.css +18 -0
  105. croak-0.1.0/docs/_static/gui_retrieve.png +0 -0
  106. croak-0.1.0/docs/_static/hero.png +0 -0
  107. croak-0.1.0/docs/_static/paper/figure_aperture.png +0 -0
  108. croak-0.1.0/docs/_static/paper/figure_boxcars3d.png +0 -0
  109. croak-0.1.0/docs/_static/paper/figure_dispersion_failure.png +0 -0
  110. croak-0.1.0/docs/_static/paper/figure_loop.png +0 -0
  111. croak-0.1.0/docs/_static/paper/figure_rdw.png +0 -0
  112. croak-0.1.0/docs/_static/paper/figure_smearing_dd.png +0 -0
  113. croak-0.1.0/docs/_static/paper/figure_thickness1fs.png +0 -0
  114. croak-0.1.0/docs/_templates/.gitkeep +0 -0
  115. croak-0.1.0/docs/conf.py +103 -0
  116. croak-0.1.0/docs/explanation/algorithms.md +403 -0
  117. croak-0.1.0/docs/explanation/forward_model.md +321 -0
  118. croak-0.1.0/docs/explanation/gradients.md +153 -0
  119. croak-0.1.0/docs/explanation/interactions.md +118 -0
  120. croak-0.1.0/docs/explanation/marginals.md +226 -0
  121. croak-0.1.0/docs/explanation/pnps_framework.md +114 -0
  122. croak-0.1.0/docs/explanation/retrieval_theory.md +140 -0
  123. croak-0.1.0/docs/explanation/uncertainty_estimation.md +536 -0
  124. croak-0.1.0/docs/explanation/validation.md +269 -0
  125. croak-0.1.0/docs/getting_started/gui.md +89 -0
  126. croak-0.1.0/docs/getting_started/installation.md +147 -0
  127. croak-0.1.0/docs/getting_started/quickstart.md +108 -0
  128. croak-0.1.0/docs/howto/collection_aperture.md +217 -0
  129. croak-0.1.0/docs/howto/fitting_thickness_tau0.md +160 -0
  130. croak-0.1.0/docs/howto/geometric_smearing.md +262 -0
  131. croak-0.1.0/docs/howto/grids_and_filtering.md +168 -0
  132. croak-0.1.0/docs/howto/gui.md +263 -0
  133. croak-0.1.0/docs/howto/loading_simulated.md +492 -0
  134. croak-0.1.0/docs/howto/marginal_checks.md +108 -0
  135. croak-0.1.0/docs/howto/materials_and_mirrors.md +224 -0
  136. croak-0.1.0/docs/howto/postprocessing.md +250 -0
  137. croak-0.1.0/docs/howto/preprocessing.md +324 -0
  138. croak-0.1.0/docs/howto/regularisation.md +199 -0
  139. croak-0.1.0/docs/howto/saving_and_loading.md +146 -0
  140. croak-0.1.0/docs/howto/solver_selection.md +146 -0
  141. croak-0.1.0/docs/howto/uncertainty.md +290 -0
  142. croak-0.1.0/docs/index.md +197 -0
  143. croak-0.1.0/docs/make.bat +25 -0
  144. croak-0.1.0/docs/reference/api/core.md +91 -0
  145. croak-0.1.0/docs/reference/api/index.md +33 -0
  146. croak-0.1.0/docs/reference/api/jax.md +40 -0
  147. croak-0.1.0/docs/reference/api/plotting.md +10 -0
  148. croak-0.1.0/docs/reference/api/retrieval.md +138 -0
  149. croak-0.1.0/docs/reference/api/workflow.md +161 -0
  150. croak-0.1.0/docs/reference/bibliography.md +59 -0
  151. croak-0.1.0/docs/reference/conventions.md +90 -0
  152. croak-0.1.0/docs/reference/features.md +222 -0
  153. croak-0.1.0/docs/reference/fft_convention.md +65 -0
  154. croak-0.1.0/docs/reference/glossary.md +105 -0
  155. croak-0.1.0/docs/reference/naming.md +47 -0
  156. croak-0.1.0/docs/requirements.txt +9 -0
  157. croak-0.1.0/docs/tutorials/01_first_retrieval.md +194 -0
  158. croak-0.1.0/docs/tutorials/02_choosing_an_algorithm.md +301 -0
  159. croak-0.1.0/docs/tutorials/03_dispersive_retrieval.md +183 -0
  160. croak-0.1.0/docs/tutorials/04_experimental_workflow.md +419 -0
  161. croak-0.1.0/docs/tutorials/05_dispersion_tuning.md +131 -0
  162. croak-0.1.0/examples/data/README.md +48 -0
  163. croak-0.1.0/examples/data/tgfrog_sim_1fs_uvfs.h5 +0 -0
  164. croak-0.1.0/examples/data/tgfrog_sim_rdw_duv.h5 +0 -0
  165. croak-0.1.0/examples/example_arpls_baseline.py +132 -0
  166. croak-0.1.0/examples/example_defringe.py +142 -0
  167. croak-0.1.0/examples/example_geometric_smearing.py +252 -0
  168. croak-0.1.0/examples/example_paper_rdw.py +141 -0
  169. croak-0.1.0/examples/example_paper_thickness.py +160 -0
  170. croak-0.1.0/examples/example_retrieval.py +125 -0
  171. croak-0.1.0/examples/example_thickness_uncertainty.py +177 -0
  172. croak-0.1.0/examples/example_workflow.py +123 -0
  173. croak-0.1.0/examples/synthetic_duv_trace.py +195 -0
  174. croak-0.1.0/pyproject.toml +157 -0
  175. croak-0.1.0/tests/conftest.py +347 -0
  176. croak-0.1.0/tests/test_ad_vs_analytic.py +320 -0
  177. croak-0.1.0/tests/test_basis_global.py +356 -0
  178. croak-0.1.0/tests/test_cli.py +103 -0
  179. croak-0.1.0/tests/test_collection.py +574 -0
  180. croak-0.1.0/tests/test_copra.py +156 -0
  181. croak-0.1.0/tests/test_copra_jax.py +208 -0
  182. croak-0.1.0/tests/test_covariance.py +319 -0
  183. croak-0.1.0/tests/test_depth_weight.py +108 -0
  184. croak-0.1.0/tests/test_dispersion.py +145 -0
  185. croak-0.1.0/tests/test_focal.py +505 -0
  186. croak-0.1.0/tests/test_focal_params.py +115 -0
  187. croak-0.1.0/tests/test_forward.py +141 -0
  188. croak-0.1.0/tests/test_forward_jax.py +184 -0
  189. croak-0.1.0/tests/test_gases.py +123 -0
  190. croak-0.1.0/tests/test_grid.py +72 -0
  191. croak-0.1.0/tests/test_gui.py +2339 -0
  192. croak-0.1.0/tests/test_interactions.py +74 -0
  193. croak-0.1.0/tests/test_io.py +81 -0
  194. croak-0.1.0/tests/test_io_simulated.py +445 -0
  195. croak-0.1.0/tests/test_lbfgs.py +113 -0
  196. croak-0.1.0/tests/test_lbfgs_ad.py +348 -0
  197. croak-0.1.0/tests/test_live_preview.py +249 -0
  198. croak-0.1.0/tests/test_lm.py +187 -0
  199. croak-0.1.0/tests/test_marginal_checks.py +198 -0
  200. croak-0.1.0/tests/test_materials.py +179 -0
  201. croak-0.1.0/tests/test_maths.py +52 -0
  202. croak-0.1.0/tests/test_metrics.py +51 -0
  203. croak-0.1.0/tests/test_mirrors.py +194 -0
  204. croak-0.1.0/tests/test_noise.py +93 -0
  205. croak-0.1.0/tests/test_nonfinite_guard.py +64 -0
  206. croak-0.1.0/tests/test_optimistix.py +460 -0
  207. croak-0.1.0/tests/test_pipeline.py +235 -0
  208. croak-0.1.0/tests/test_pipeline_simulated.py +555 -0
  209. croak-0.1.0/tests/test_plotting.py +494 -0
  210. croak-0.1.0/tests/test_preprocess.py +909 -0
  211. croak-0.1.0/tests/test_processing.py +349 -0
  212. croak-0.1.0/tests/test_progress.py +135 -0
  213. croak-0.1.0/tests/test_pulses.py +177 -0
  214. croak-0.1.0/tests/test_retrieve.py +149 -0
  215. croak-0.1.0/tests/test_save.py +250 -0
  216. croak-0.1.0/tests/test_scripting.py +134 -0
  217. croak-0.1.0/tests/test_session.py +670 -0
  218. croak-0.1.0/tests/test_smearing.py +700 -0
  219. croak-0.1.0/tests/test_truth_metrics.py +202 -0
  220. croak-0.1.0/tests/test_uncertainty.py +644 -0
  221. croak-0.1.0/tests/test_warm_lbfgs.py +92 -0
  222. croak-0.1.0/tools/make_docs_images.py +255 -0
  223. croak-0.1.0/tools/reduce_scansave.py +128 -0
  224. 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
@@ -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
@@ -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