makewfs 1.0.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 (130) hide show
  1. makewfs-1.0.0/.gitignore +231 -0
  2. makewfs-1.0.0/AGENTS.md +223 -0
  3. makewfs-1.0.0/CHANGELOG.md +130 -0
  4. makewfs-1.0.0/CITATION.cff +12 -0
  5. makewfs-1.0.0/CONTRIBUTING.md +44 -0
  6. makewfs-1.0.0/LICENSE +21 -0
  7. makewfs-1.0.0/PKG-INFO +133 -0
  8. makewfs-1.0.0/README.md +84 -0
  9. makewfs-1.0.0/ROADMAP.md +994 -0
  10. makewfs-1.0.0/benchmarks/__init__.py +1 -0
  11. makewfs-1.0.0/benchmarks/check_regression.py +118 -0
  12. makewfs-1.0.0/benchmarks/configs/pyramid_40_float32.toml +37 -0
  13. makewfs-1.0.0/benchmarks/configs/pyramid_60_mod8_float32.toml +37 -0
  14. makewfs-1.0.0/benchmarks/configs/pyramid_80_mod32_float64.toml +37 -0
  15. makewfs-1.0.0/benchmarks/configs/shack_hartmann_20x20_float32.toml +37 -0
  16. makewfs-1.0.0/benchmarks/configs/shack_hartmann_60x60_float64.toml +37 -0
  17. makewfs-1.0.0/benchmarks/configs/shack_hartmann_broadband_lgs.toml +42 -0
  18. makewfs-1.0.0/benchmarks/device-results.json +357 -0
  19. makewfs-1.0.0/benchmarks/device-results.md +18 -0
  20. makewfs-1.0.0/benchmarks/profile_warm.py +58 -0
  21. makewfs-1.0.0/benchmarks/reference-results.json +205 -0
  22. makewfs-1.0.0/benchmarks/reference-table.md +18 -0
  23. makewfs-1.0.0/benchmarks/render_device_table.py +80 -0
  24. makewfs-1.0.0/benchmarks/render_table.py +96 -0
  25. makewfs-1.0.0/benchmarks/run.py +237 -0
  26. makewfs-1.0.0/docs/adr/0001-units-coordinates.md +12 -0
  27. makewfs-1.0.0/docs/adr/0002-flux-normalization.md +11 -0
  28. makewfs-1.0.0/docs/adr/0003-public-api.md +10 -0
  29. makewfs-1.0.0/docs/adr/0004-backend-boundary.md +21 -0
  30. makewfs-1.0.0/docs/adr/index.md +10 -0
  31. makewfs-1.0.0/docs/api.md +11 -0
  32. makewfs-1.0.0/docs/concepts.md +38 -0
  33. makewfs-1.0.0/docs/configuration.md +263 -0
  34. makewfs-1.0.0/docs/contributing.md +6 -0
  35. makewfs-1.0.0/docs/detectors.md +25 -0
  36. makewfs-1.0.0/docs/examples.md +39 -0
  37. makewfs-1.0.0/docs/gallery/makewfs-gallery.json +23 -0
  38. makewfs-1.0.0/docs/gallery/makewfs-gallery.svg +4071 -0
  39. makewfs-1.0.0/docs/gallery.md +18 -0
  40. makewfs-1.0.0/docs/guide-stars.md +62 -0
  41. makewfs-1.0.0/docs/index.md +29 -0
  42. makewfs-1.0.0/docs/interop.md +66 -0
  43. makewfs-1.0.0/docs/performance.md +133 -0
  44. makewfs-1.0.0/docs/pyramid.md +38 -0
  45. makewfs-1.0.0/docs/quickstart.md +50 -0
  46. makewfs-1.0.0/docs/release.md +28 -0
  47. makewfs-1.0.0/docs/shack-hartmann.md +52 -0
  48. makewfs-1.0.0/docs/stability.md +26 -0
  49. makewfs-1.0.0/docs/troubleshooting.md +26 -0
  50. makewfs-1.0.0/docs/units-and-coordinates.md +14 -0
  51. makewfs-1.0.0/docs/validation.md +81 -0
  52. makewfs-1.0.0/examples/README.md +49 -0
  53. makewfs-1.0.0/examples/closed_loop_injection.py +114 -0
  54. makewfs-1.0.0/examples/compare_sensors.py +58 -0
  55. makewfs-1.0.0/examples/configs/angular_kernel.txt +4 -0
  56. makewfs-1.0.0/examples/configs/lgs_thin_beacon.toml +42 -0
  57. makewfs-1.0.0/examples/configs/precision_throughput.toml +37 -0
  58. makewfs-1.0.0/examples/configs/pyramid_minimal.toml +43 -0
  59. makewfs-1.0.0/examples/configs/qe_curve.txt +4 -0
  60. makewfs-1.0.0/examples/configs/shack_hartmann_extended_source.toml +43 -0
  61. makewfs-1.0.0/examples/configs/shack_hartmann_minimal.toml +41 -0
  62. makewfs-1.0.0/examples/configs/shack_hartmann_spectral_qe.toml +40 -0
  63. makewfs-1.0.0/examples/detector_choices.py +84 -0
  64. makewfs-1.0.0/examples/gallery.py +160 -0
  65. makewfs-1.0.0/examples/keck_haka/README.md +361 -0
  66. makewfs-1.0.0/examples/keck_haka/analyze_lut.py +1018 -0
  67. makewfs-1.0.0/examples/keck_haka/benchmark.py +553 -0
  68. makewfs-1.0.0/examples/keck_haka/camera_modes.csv +21 -0
  69. makewfs-1.0.0/examples/keck_haka/camera_modes_empirical_floor_continuous.csv +202 -0
  70. makewfs-1.0.0/examples/keck_haka/compare_real.py +744 -0
  71. makewfs-1.0.0/examples/keck_haka/fit_secondary.py +213 -0
  72. makewfs-1.0.0/examples/keck_haka/haka_cpu_gpu_benchmark.json +2223 -0
  73. makewfs-1.0.0/examples/keck_haka/haka_lut_snr.json +7020 -0
  74. makewfs-1.0.0/examples/keck_haka/keck_haka.json +581 -0
  75. makewfs-1.0.0/examples/keck_haka/keck_haka.toml +65 -0
  76. makewfs-1.0.0/examples/keck_haka/mauna_kea_extinction.csv +19 -0
  77. makewfs-1.0.0/examples/keck_haka/ocam_20260720/extract_ocam_images.py +194 -0
  78. makewfs-1.0.0/examples/keck_haka/ocam_20260720/make_ocam_video.py +145 -0
  79. makewfs-1.0.0/examples/keck_haka/real_vs_simulation.json +596 -0
  80. makewfs-1.0.0/examples/keck_haka/secondary_fit.json +31 -0
  81. makewfs-1.0.0/examples/keck_haka/simulate.py +905 -0
  82. makewfs-1.0.0/examples/lgs_elongation.py +92 -0
  83. makewfs-1.0.0/examples/lgs_thin_beacon.py +96 -0
  84. makewfs-1.0.0/examples/magnitude_series.py +75 -0
  85. makewfs-1.0.0/examples/moving_atmosphere.py +100 -0
  86. makewfs-1.0.0/examples/precision_throughput.py +117 -0
  87. makewfs-1.0.0/examples/pyramid_modulation.py +61 -0
  88. makewfs-1.0.0/examples/quickstart.py +58 -0
  89. makewfs-1.0.0/examples/realistic_broadband.py +114 -0
  90. makewfs-1.0.0/examples/sh_design_trade.py +49 -0
  91. makewfs-1.0.0/examples/spectral_qe.py +179 -0
  92. makewfs-1.0.0/pyproject.toml +125 -0
  93. makewfs-1.0.0/src/makewfs/__about__.py +3 -0
  94. makewfs-1.0.0/src/makewfs/__init__.py +15 -0
  95. makewfs-1.0.0/src/makewfs/api.py +210 -0
  96. makewfs-1.0.0/src/makewfs/backend.py +396 -0
  97. makewfs-1.0.0/src/makewfs/cli.py +99 -0
  98. makewfs-1.0.0/src/makewfs/config.py +830 -0
  99. makewfs-1.0.0/src/makewfs/detector.py +116 -0
  100. makewfs-1.0.0/src/makewfs/provenance.py +109 -0
  101. makewfs-1.0.0/src/makewfs/pupil.py +112 -0
  102. makewfs-1.0.0/src/makewfs/py.typed +0 -0
  103. makewfs-1.0.0/src/makewfs/radiometry.py +34 -0
  104. makewfs-1.0.0/src/makewfs/sampling.py +164 -0
  105. makewfs-1.0.0/src/makewfs/sensors/__init__.py +7 -0
  106. makewfs-1.0.0/src/makewfs/sensors/base.py +46 -0
  107. makewfs-1.0.0/src/makewfs/sensors/pyramid.py +215 -0
  108. makewfs-1.0.0/src/makewfs/sensors/shack_hartmann.py +315 -0
  109. makewfs-1.0.0/src/makewfs/source.py +169 -0
  110. makewfs-1.0.0/src/makewfs/wavefront.py +204 -0
  111. makewfs-1.0.0/tests/test_backend_audit.py +98 -0
  112. makewfs-1.0.0/tests/test_benchmarks.py +60 -0
  113. makewfs-1.0.0/tests/test_cli.py +67 -0
  114. makewfs-1.0.0/tests/test_config.py +371 -0
  115. makewfs-1.0.0/tests/test_gpu_backend.py +110 -0
  116. makewfs-1.0.0/tests/test_hcipy_validation.py +248 -0
  117. makewfs-1.0.0/tests/test_interop.py +25 -0
  118. makewfs-1.0.0/tests/test_keck_haka_example.py +368 -0
  119. makewfs-1.0.0/tests/test_numerics.py +195 -0
  120. makewfs-1.0.0/tests/test_oopao_validation.py +193 -0
  121. makewfs-1.0.0/tests/test_optics_validation.py +381 -0
  122. makewfs-1.0.0/tests/test_provenance.py +50 -0
  123. makewfs-1.0.0/tests/test_public_api.py +18 -0
  124. makewfs-1.0.0/tests/test_pyramid.py +93 -0
  125. makewfs-1.0.0/tests/test_shack_hartmann.py +153 -0
  126. makewfs-1.0.0/tests/test_source.py +185 -0
  127. makewfs-1.0.0/tests/test_validation_report.py +28 -0
  128. makewfs-1.0.0/tests/test_wavefront.py +149 -0
  129. makewfs-1.0.0/validation/__init__.py +1 -0
  130. makewfs-1.0.0/validation/run.py +193 -0
@@ -0,0 +1,231 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+
220
+ # makewfs generated artifacts
221
+ *.coverage
222
+ .coverage
223
+ *.npy
224
+ *.npz
225
+ *.fits
226
+ *.fit
227
+ *.png
228
+ validation-metrics.json
229
+ benchmark-results.json
230
+ *.gif
231
+ .vscode
@@ -0,0 +1,223 @@
1
+ # AGENTS.md
2
+
3
+ This file is the operating guide for AI agents working in `makewfs`. It applies
4
+ to the entire repository.
5
+
6
+ ## Start here
7
+
8
+ Before changing anything:
9
+
10
+ 1. Read `README.md` and the complete `ROADMAP.md`.
11
+ 2. Read `pyproject.toml` when present, the relevant source/tests/docs, and any
12
+ linked ADR once those files exist.
13
+ 3. Run `git status --short --branch`. Preserve all user changes and unrelated
14
+ work; never reset or overwrite them.
15
+ 4. Identify the smallest unchecked roadmap item that contains the requested work
16
+ and state its acceptance criteria.
17
+ 5. Inspect the public sibling API before proposing cross-repository work:
18
+ `/home/donkeykong/pyturb` for atmosphere and
19
+ `/home/donkeykong/getframes` for detector/radiometry.
20
+
21
+ The repository is currently in the Shack-Hartmann and fixed-mask four-face
22
+ pyramid stage, with deterministic source spectral/angular quadrature, measured
23
+ source curves and user-supplied angular kernels, physical SH sampling controls,
24
+ analytic segmented/rotated pupils, and a documented SH sodium-range geometry
25
+ model. It also includes a versioned labelled documentation gallery, benchmark
26
+ reference snapshot, non-editable-wheel clean-room smoke evidence, released
27
+ wavelength-resolved detector QE through `getframes>=2.1.1`, and public
28
+ end-to-end CuPy execution with CPU parity tests. The GPU path uses
29
+ `numerics.device = "gpu"` and the sibling `getframes` CuPy detector. Do not
30
+ present range-resolved turbulent LGS OPD or broad independent-reference parity
31
+ as implemented until their gates pass.
32
+
33
+ ## Product boundary
34
+
35
+ `makewfs` owns wavefront-sensor image formation:
36
+
37
+ ```text
38
+ pupil OPD/phase + static config
39
+ |
40
+ v
41
+ makewfs optics
42
+ |
43
+ v
44
+ photon rate [photons/s/native detector pixel]
45
+ |
46
+ v
47
+ getframes.Camera.expose[_spectral]
48
+ |
49
+ v
50
+ Frame data [ADU]
51
+ ```
52
+
53
+ - Atmosphere, frozen flow, Cn2 profiles, off-axis footprints, and LGS cone-effect
54
+ phase belong to `pyturb`.
55
+ - QE, shot/read/dark noise, gain, detector artifacts, digitization, calibration,
56
+ and detector presets belong to `getframes`.
57
+ - Reconstruction, centroid/slopes, DMs, and controllers belong to downstream AO
58
+ software.
59
+ - `makewfs` may model finite guide-source and sodium-layer image morphology
60
+ because it is part of WFS image formation, but it never predicts LGS return
61
+ flux or evolves the sodium layer.
62
+
63
+ Do not copy sibling physics for convenience. If their public API is insufficient,
64
+ write a failing integration test/design note, use the conditional gates in
65
+ `ROADMAP.md`, and make the smallest change in the owning repository.
66
+
67
+ ## Stable contracts to protect
68
+
69
+ - The per-frame runtime inputs are the wavefront and an optional noise seed. All
70
+ instrument/source/detector choices live in versioned config.
71
+ - OPD metres are the canonical internal wavefront quantity. Phase-radian input
72
+ must declare its reference wavelength; units are never inferred.
73
+ - Arrays use `(y, x)` order and documented centered pixel coordinates. Never fix
74
+ a sign or transpose mismatch by visual trial and error—add an analytic ramp test.
75
+ - Ideal output is a non-negative photon-rate map in photons/s/native detector
76
+ pixel. Only `getframes` turns it into electrons or ADU.
77
+ - Intensities, not fields, are summed over incoherent wavelengths, modulation
78
+ points, finite-source samples, and sodium slices.
79
+ - Cropping reports lost flux; it does not renormalize it away.
80
+ - The intended top-level API is `load_config`, `WavefrontSensor`, and `simulate`.
81
+ Keep other implementation objects out of `makewfs.__init__` unless an API review
82
+ explicitly accepts them.
83
+ - The optical core must not import `pyturb`. Only the detector adapter imports
84
+ `getframes.Camera`; radiometry may import documented `getframes` radiometry APIs.
85
+
86
+ ## Numerical and physics standards
87
+
88
+ - Start from a derivation, primary paper, or maintained independent reference.
89
+ Cite it in the module and user guide.
90
+ - Every physics feature needs a quantitative assertion against theory or an
91
+ independent calculation. “The plot looks right” is not validation.
92
+ - Required invariants include piston invariance, non-negative intensity, explicit
93
+ flux accounting, photon-rate linearity, stable axis/sign conventions, and
94
+ convergence with numerical sampling.
95
+ - Use explicit centered FFT helpers and normalization. Do not scatter `fftshift`
96
+ conventions through sensor implementations.
97
+ - Use flux-conserving pixel-area integration/rebinning; interpolation is not a
98
+ substitute for integrating detector pixels.
99
+ - Preserve `float32/complex64` and `float64/complex128` pairs. Test both; do not
100
+ allow silent promotion in a hot path.
101
+ - Randomness uses passed `numpy.random.Generator` instances or reproducibly
102
+ derived named seeds. Never use global `np.random` state.
103
+ - Static grids, masks, ramps, normalization constants, and detector construction
104
+ are cached on the persistent sensor. Benchmark construction separately from
105
+ warm per-frame operation.
106
+ - Write array operations behind `ArrayBackend`. Sensor engines must not call
107
+ NumPy allocation, FFT, or reduction functions directly. File readers and
108
+ source/config parsing are explicit host operations; the detector boundary
109
+ preserves the selected backend. Use `ArrayBackend.scalar` or `to_host` only at
110
+ named metadata/file crossings.
111
+ The backend-audit AST test and injected-CPU parity tests must remain green.
112
+ - CPU correctness comes first. The private `_backend=cupy_backend()` hook remains
113
+ an implementation/testing escape hatch. The supported GPU contract is the
114
+ serializable `numerics.device = "gpu"` field and requires device-resident
115
+ `getframes`; never reimplement detector behavior in this repository.
116
+
117
+ ## Configuration rules
118
+
119
+ - TOML is canonical. All physical keys include units in their names.
120
+ - Config models are immutable and contain only serializable intent, never runtime
121
+ arrays, FFT plans, RNG state, or camera state.
122
+ - Reject unknown keys, incompatible alternatives, non-finite values, invalid
123
+ ranges, mismatched detector geometry, and unsupported schema versions with
124
+ path-specific actionable messages.
125
+ - Resolve file references relative to the config file. Hash referenced masks,
126
+ curves, and static OPD for provenance.
127
+ - A new user-facing field requires validation tests, serialization/digest tests,
128
+ config-reference documentation, and at least one example where appropriate.
129
+ - Backward-incompatible schema changes require a schema-version change and a
130
+ migration/stability note.
131
+
132
+ ## Code organization
133
+
134
+ Follow the target layout in `ROADMAP.md`:
135
+
136
+ - `config.py` parses and validates; it does not propagate optics.
137
+ - `wavefront.py`, `pupil.py`, and `sampling.py` hold shared numerical rules.
138
+ - `sensors/` contains deterministic ideal optical engines and no camera noise.
139
+ - `radiometry.py` produces source photon budgets using public `getframes` tools.
140
+ - `detector.py` is a narrow adapter to `getframes.Camera.expose` and the
141
+ optional public `expose_spectral` cube API.
142
+ - `api.py` owns the user facade and caching lifecycle.
143
+ - `validation/` produces theory/reference evidence; `benchmarks/` measures speed;
144
+ neither is imported by the runtime package.
145
+
146
+ Keep functions small enough that their units and normalization can be tested in
147
+ isolation. Prefer immutable dataclasses and pure numerical kernels. Public APIs
148
+ have type hints and NumPy-style docstrings. Internal names start with `_` unless
149
+ another module has a deliberate need for them.
150
+
151
+ ## Tests and quality gate
152
+
153
+ Once Phase 0 creates the tooling, the normal pre-handoff gate is:
154
+
155
+ ```bash
156
+ ruff check .
157
+ ruff format --check .
158
+ python -m mypy
159
+ pytest --cov=makewfs --cov-branch --cov-report=term-missing
160
+ mkdocs build --strict
161
+ python -m build
162
+ ```
163
+
164
+ Run `python -m mypy` from a clean Python 3.10 environment, as the CI lint job
165
+ does. The configured mypy target is the minimum supported Python version, so the
166
+ type-check environment must also resolve the Python 3.10 dependency markers
167
+ (including `tomli`) and a NumPy release whose stubs support Python 3.10. Running
168
+ the Python 3.10-targeted check from a newer environment can instead install
169
+ newer-only NumPy stubs and omit the conditional `tomli` dependency.
170
+
171
+ Also run the narrowest relevant tests while iterating. Mark slow statistical,
172
+ validation, GPU, and example tests explicitly; ordinary tests must stay quick.
173
+ When sibling packages are installed, run `python -m pytest -m interop` as a
174
+ separate compatibility check.
175
+
176
+ Test public behavior, units, signs, shapes, failure modes, precision, and seeded
177
+ reproducibility. Statistical tests use ensemble uncertainty and non-flaky
178
+ tolerances. Independent reference packages such as HCIPy are optional validation
179
+ dependencies, never core dependencies.
180
+
181
+ When CUDA 12 CuPy and a device are available, also run `python -m pytest -q -m
182
+ gpu`. GPU tests are optional and must verify CPU optical parity, device-resident
183
+ detector/truth arrays, direct `pyturb` interoperability, and seeded detector
184
+ behavior; they must not make ordinary CI depend on CUDA.
185
+
186
+ For performance changes, run the affected warm and cold benchmarks and report the
187
+ hardware/dependency context. Do not claim a speedup from one timing sample or
188
+ weaken physics accuracy to win a benchmark without an explicit documented mode.
189
+ The representative CI guard is reproducible locally with:
190
+
191
+ ```bash
192
+ python benchmarks/run.py --representative --frames 1 --output /tmp/makewfs-benchmark.json
193
+ python benchmarks/check_regression.py /tmp/makewfs-benchmark.json
194
+ MPLBACKEND=Agg python examples/gallery.py
195
+ ```
196
+
197
+ ## Documentation and examples
198
+
199
+ - Every public feature lands with its API docstring and the relevant user guide.
200
+ - Every configuration field appears in the configuration reference with units,
201
+ default, allowed range, interactions, and an example.
202
+ - Examples are scripts plus TOML, deterministic by default, headless, and able to
203
+ save plots. Give them a reduced CI mode; never rely on an interactive notebook
204
+ as the only executable form.
205
+ - Plot labels include units and state whether an image is ideal photon rate,
206
+ expected electrons, or noisy ADU.
207
+ - Be explicit about approximations, especially partial sampling, chromatic
208
+ pyramid behavior, LGS mean-altitude OPD, and detector cropping.
209
+ - Update `CHANGELOG.md` under Unreleased for user-visible changes.
210
+
211
+ ## Roadmap and handoff discipline
212
+
213
+ - Work in dependency order. Shared contracts/ADRs precede parallel sensor work.
214
+ - Take bounded vertical slices; avoid sweeping “implement a whole phase” changes.
215
+ - Check a roadmap box only when implementation, tests, docs, and required
216
+ validation/benchmark are all complete and passing.
217
+ - Do not mark conditional sibling work as required until its gate is demonstrated.
218
+ - At handoff, summarize files changed, assumptions, physics/reference basis,
219
+ commands run and results, benchmark impact, and remaining roadmap items.
220
+ - Maintain this file: update it when the repository layout, quality-gate commands,
221
+ stable contracts, or current implementation stage changes.
222
+ - If blocked, document the exact failing contract and evidence. Do not work around
223
+ it by absorbing atmosphere or detector behavior into this repository.
@@ -0,0 +1,130 @@
1
+ # Changelog
2
+
3
+ All notable changes to `makewfs` are documented here.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.0.0] - 2026-07-26
8
+
9
+ - Prepared the first stable public release with versioned package metadata,
10
+ PyPI/CI badges, citation and release documentation, and a trusted-publishing
11
+ GitHub Actions workflow using the `pypi` environment.
12
+ - Fixed the benchmark runner on Python 3.10 by using the portable
13
+ `datetime.timezone.utc` API.
14
+ - Raised the detector dependency to released `getframes>=2.1.1`, made
15
+ wavelength-resolved detector QE and full spectral truth part of the supported
16
+ contract, and removed the pre-release integrated-signal compatibility path.
17
+ - Added an R-band HAKA camera-LUT analysis using representative A0 V through M3
18
+ V continua and generated open-loop Maunakea states. It reports mean active
19
+ 4x4-lenslet intensity SNR with OCAM2K photon, EM-excess, dark, CIC, read, and
20
+ quantization noise, fits a smooth ceiling-aware broken-power-law cadence floor
21
+ only to the R>=10 fine-adjustment tail, asymptotes to the true 2067 Hz OCAM2K
22
+ limit, and emits a smooth saturation-constrained policy that never slows below
23
+ that empirical model merely to recover per-frame SNR.
24
+ - Generalized the HAKA broadband photon-budget helper from fixed Johnson V
25
+ normalization to an explicit Johnson normalization band, retaining V as the
26
+ showcase and eng519 default.
27
+ - Fixed temporal integration to average and forward wavelength-resolved photon
28
+ cubes to the detector, preserving configured spectral QE instead of silently
29
+ falling back to scalar QE.
30
+ - Fixed even-sized Shack-Hartmann focal-plane registration: zero slope now lies
31
+ at the intersection of the central four detector pixels, using half-integer
32
+ Fourier samples rather than an asymmetric integer-grid crop.
33
+ - Added a Keck II HAKA open-loop worked example with a generated 36-segment
34
+ Keck pupil including a live-data-fitted circle-plus-hexagon secondary shadow
35
+ and six 26 mm support arms, exact
36
+ 57x57-by-4x4 (228x228) Shack-Hartmann/OCAM2K geometry,
37
+ magnitude-dependent EM gain and frame rate, temporally integrated `pyturb`
38
+ Maunakea OPD, exposure-matched master-dark subtraction, GIF/MP4 output, and a
39
+ reproducibility manifest with per-frame photon/electron/count flux auditing.
40
+ The supplied real eng519 V=10.16 RTC cube constrains the roughly 54-lenslet pupil
41
+ diameter, compact quadcell sampling, and the eight-output 4x2 OCAM geometry,
42
+ outside-pupil dark/bias levels, and relative conversion gains. With no matched
43
+ dark cube, the RTC comparison subtracts a per-output/repeated-4x4 template and
44
+ per-frame output drift inferred outside the pupil, then reports real and
45
+ simulated lenslet signal/morphology without global rescaling. The
46
+ eng519 comparison now simulates V=10.16 at 750 fps, retains every tenth
47
+ generated phase-screen exposure like the telemetry, and writes a side-by-side
48
+ GIF. The magnitude showcase advances frozen flow by a visible minimum cadence
49
+ at every magnitude without changing the physical detector exposure. HAKA NGS
50
+ photon formation now integrates a V-normalized 6600 K spectrum over the full
51
+ 400--950 nm band, applies measured Mauna Kea extinction at the observed
52
+ airmass, applies 0.88 reflectivity to the aluminum primary, secondary, and
53
+ tertiary, uses the sampled clear-pupil collecting area, and passes the
54
+ resolved spectral cube through OCAM2K's wavelength-dependent QE. The reference
55
+ renders now use the Keck-characterized approximately 28 output e-/ADU OCAM2K
56
+ conversion. Team-confirmed independent bench measurements now establish the
57
+ downstream HAKA throughput as 28.7%; it is applied as physical radiometry after
58
+ the telescope mirrors rather than inferred or fitted by the RTC comparison.
59
+ The regenerated eng519 comparison has a real/simulation signal ratio of
60
+ 1.00029.
61
+ - Added a reproducible warm HAKA CPU/GPU benchmark. It times non-periodic Mauna
62
+ Kea atmosphere evolution, the full eight-wavelength 57x57 Shack--Hartmann
63
+ propagation, and noisy OCAM2K exposure while excluding static setup and
64
+ synchronizing CUDA batches. Its local GIF shows CPU/GPU detector streams over
65
+ equal wall-clock playback with measured FPS, real-time factor, frame counter,
66
+ and atmosphere time overlays.
67
+ - Removed generated PNG/GIF artifacts from version control and ignore them
68
+ globally; example scripts continue to create them locally on demand.
69
+ - Added public end-to-end GPU execution through `numerics.device = "gpu"`.
70
+ CuPy OPD, SH/PWFS optics, wavelength-resolved photon maps, the `getframes`
71
+ detector chain, truth, and ADU remain device-resident. The runtime reports an
72
+ actionable error when the installed `getframes` lacks its GPU camera contract.
73
+ - Added direct `pyturb` GPU OPD → Shack–Hartmann → GPU ADU integration coverage,
74
+ updated SH/PWFS CUDA parity tests, synchronized GPU benchmark mode, and measured
75
+ detector-only timing.
76
+ - Added a paired CPU/GPU bulk-throughput artifact and rendered comparison for
77
+ representative SH, broadband LGS, and modulated pyramid workflows, with
78
+ README and performance-guide results plus exact reproduction commands.
79
+ - Optimized persistent SH/PWFS execution by caching source/range geometry,
80
+ modulation phasors, resampling grids, flux normalization, and monochromatic
81
+ spectral views; using native orthonormal FFT scaling and an intensity-only SH
82
+ transform; removing redundant validations/resampling; and batching GPU
83
+ metadata scalar transfers. On the RTX 5090 reference matrix this improves CPU
84
+ throughput by 1.19x–1.73x and GPU throughput by 1.42x–2.58x over the initial
85
+ end-to-end implementation while retaining the physics/parity gates.
86
+
87
+ - Expanded optical verification with a direct-DFT pyramid reference,
88
+ multi-amplitude HCIPy SH response curves, HCIPy low-order pyramid response
89
+ maps, supplementary local OOPAO comparisons, and quantitative SH/pyramid
90
+ metrics in the deterministic validation report.
91
+ - Fixed the pyramid propagation grid to honor `numerics.fft_oversampling`, so
92
+ the diffraction halo no longer wraps onto the pupil rims; cropped flux is
93
+ reported as captured rate and independent HCIPy parity improved.
94
+ - Reconfigured the shipped example TOMLs to be representative demonstrations:
95
+ pyramid pupils are now separated (`pupil_separation_pixels` larger than
96
+ `pixels_across_pupil`) and source photon rates correspond to a bright guide
97
+ star so detector frames show spots above read noise.
98
+ - Added deterministic broadband/finite-source quadrature, measured SED and
99
+ transmission curves, physical SH sampling, field stops, optical blur,
100
+ detector margins, and sodium-range SH elongation examples.
101
+ - Added strict configuration-reference documentation for every v1 table and
102
+ key, plus validation and benchmark smoke reports in CI.
103
+ - Added headless worked-example CI smoke tests, deterministic plotting backend
104
+ selection, and a 90% enforced branch-coverage gate.
105
+ - Added configuration-relative three-column angular source kernels for measured
106
+ or resolved guide-star morphologies, with normalized state provenance.
107
+ - Added rotated analytic segment-gap pupils and a cached physical-coordinate
108
+ lenslet-grid rotation/offset path with aligned-grid parity tests.
109
+ - Added an optional HCIPy ideal-pyramid cross-check and a dedicated validation
110
+ CI job; HCIPy remains outside runtime dependencies.
111
+ - Added configuration-relative measured SH optical blur kernels with unit-sum
112
+ validation, cached convolution, and provenance hashes.
113
+ - Added a public API/configuration stability audit and same-run benchmark
114
+ regression envelopes for representative CPU kernels.
115
+ - Added versioned benchmark snapshots and isolated non-editable-wheel
116
+ interoperability verification for `pyturb` 1.0 and `getframes`.
117
+ - Added a versioned labelled SVG capability gallery with units, color bars,
118
+ seeds, configuration digests, and modeling notes.
119
+ - Added wavelength-resolved detector QE through the public `getframes` spectral
120
+ cube contract, with truth preservation and a shipped comparison example.
121
+ - Formalized the private optical `ArrayBackend` boundary and added static
122
+ leakage/parity checks so a future device backend does not require sensor
123
+ mathematics to be rewritten.
124
+ - Added the original private CUDA 12 CuPy optical path with SH/pyramid parity
125
+ tests; it is retained as a compatibility hook underneath the public
126
+ configuration-driven GPU path.
127
+ - Added the monochromatic CPU four-face pyramid engine, modulation support, a
128
+ complete pyramid example configuration, and symmetry/flux/detector tests.
129
+ - Added the implementation roadmap and agent guide.
130
+ - Added the initial configuration and numerical implementation foundation.
@@ -0,0 +1,12 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use makewfs in your research, please cite this software."
3
+ title: "makewfs: configuration-driven adaptive-optics wavefront-sensor image simulation"
4
+ type: software
5
+ authors:
6
+ - family-names: Taylor
7
+ given-names: Jacob
8
+ version: 1.0.0
9
+ date-released: 2026-07-26
10
+ license: MIT
11
+ repository-code: "https://github.com/jacotay7/makewfs"
12
+ url: "https://jacotay7.github.io/makewfs/"
@@ -0,0 +1,44 @@
1
+ # Contributing to makewfs
2
+
3
+ `makewfs` is a numerical optics package. A contribution is complete only when
4
+ its behavior, units, tests, documentation, and performance implications are
5
+ clear.
6
+
7
+ Read [AGENTS.md](AGENTS.md) and the relevant section of [ROADMAP.md](ROADMAP.md)
8
+ before starting. Keep atmosphere physics in `pyturb` and detector physics in
9
+ `getframes`.
10
+
11
+ ## Development setup
12
+
13
+ ```bash
14
+ python -m venv .venv
15
+ source .venv/bin/activate
16
+ python -m pip install -e ".[dev,examples]"
17
+ ```
18
+
19
+ For local sibling development, install the checked-out packages separately:
20
+
21
+ ```bash
22
+ python -m pip install -e ../getframes
23
+ python -m pip install -e ../pyturb
24
+ ```
25
+
26
+ ## Quality gate
27
+
28
+ ```bash
29
+ ruff check .
30
+ ruff format --check .
31
+ mypy
32
+ python -m pytest -q --cov=makewfs --cov-branch --cov-report=term-missing
33
+ mkdocs build --strict
34
+ python -m build
35
+ ```
36
+
37
+ Physics changes require an analytic or independent-reference assertion. Keep
38
+ randomness on explicit seeded generators, state all array units, and update the
39
+ configuration reference and changelog for public changes.
40
+
41
+ ## Pull requests
42
+
43
+ Keep changes focused. Describe the numerical model, source/reference used for
44
+ validation, tests run, benchmark impact, and any conditional upstream work.
makewfs-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jacob Taylor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.