seestar-photometry 0.2.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 (62) hide show
  1. seestar_photometry-0.2.0/.gitignore +38 -0
  2. seestar_photometry-0.2.0/.readthedocs.yaml +23 -0
  3. seestar_photometry-0.2.0/CHANGELOG.md +51 -0
  4. seestar_photometry-0.2.0/CLAUDE.md +82 -0
  5. seestar_photometry-0.2.0/LICENSE +674 -0
  6. seestar_photometry-0.2.0/PKG-INFO +161 -0
  7. seestar_photometry-0.2.0/README.md +110 -0
  8. seestar_photometry-0.2.0/docs/README.md +31 -0
  9. seestar_photometry-0.2.0/docs/api.md +84 -0
  10. seestar_photometry-0.2.0/docs/architecture.md +102 -0
  11. seestar_photometry-0.2.0/docs/astrometry-and-gaia.md +114 -0
  12. seestar_photometry-0.2.0/docs/conf.py +61 -0
  13. seestar_photometry-0.2.0/docs/data-format.md +118 -0
  14. seestar_photometry-0.2.0/docs/diagnostics.md +108 -0
  15. seestar_photometry-0.2.0/docs/example-data.md +86 -0
  16. seestar_photometry-0.2.0/docs/frame-table.md +143 -0
  17. seestar_photometry-0.2.0/docs/index.md +101 -0
  18. seestar_photometry-0.2.0/docs/install.md +108 -0
  19. seestar_photometry-0.2.0/docs/light-curves.md +149 -0
  20. seestar_photometry-0.2.0/docs/migration-from-mwcam.md +92 -0
  21. seestar_photometry-0.2.0/docs/photometry-design.md +127 -0
  22. seestar_photometry-0.2.0/docs/quickstart.md +146 -0
  23. seestar_photometry-0.2.0/docs/usecases/batch.md +140 -0
  24. seestar_photometry-0.2.0/docs/usecases/depth.md +122 -0
  25. seestar_photometry-0.2.0/docs/usecases/lightcurve.md +187 -0
  26. seestar_photometry-0.2.0/docs/usecases/saturation.md +99 -0
  27. seestar_photometry-0.2.0/docs/usecases/stacking.md +127 -0
  28. seestar_photometry-0.2.0/docs/usecases/zeropoint.md +106 -0
  29. seestar_photometry-0.2.0/example_data/SHA256 +1 -0
  30. seestar_photometry-0.2.0/examples/exoplanet_transit.py +116 -0
  31. seestar_photometry-0.2.0/examples/mwcam_lightcurve.py +106 -0
  32. seestar_photometry-0.2.0/examples/sn2026sqf_lightcurve.py +182 -0
  33. seestar_photometry-0.2.0/pyproject.toml +95 -0
  34. seestar_photometry-0.2.0/src/seestar_photometry/__init__.py +64 -0
  35. seestar_photometry-0.2.0/src/seestar_photometry/_style.py +218 -0
  36. seestar_photometry-0.2.0/src/seestar_photometry/astrometry.py +291 -0
  37. seestar_photometry-0.2.0/src/seestar_photometry/calibration.py +283 -0
  38. seestar_photometry-0.2.0/src/seestar_photometry/catalogs.py +182 -0
  39. seestar_photometry-0.2.0/src/seestar_photometry/contamination.py +116 -0
  40. seestar_photometry-0.2.0/src/seestar_photometry/debayer.py +123 -0
  41. seestar_photometry-0.2.0/src/seestar_photometry/depth.py +134 -0
  42. seestar_photometry-0.2.0/src/seestar_photometry/examples.py +329 -0
  43. seestar_photometry-0.2.0/src/seestar_photometry/frames.py +445 -0
  44. seestar_photometry-0.2.0/src/seestar_photometry/kron.py +90 -0
  45. seestar_photometry-0.2.0/src/seestar_photometry/lightcurves.py +386 -0
  46. seestar_photometry-0.2.0/src/seestar_photometry/photometry.py +483 -0
  47. seestar_photometry-0.2.0/src/seestar_photometry/pipeline.py +468 -0
  48. seestar_photometry-0.2.0/src/seestar_photometry/plots.py +985 -0
  49. seestar_photometry-0.2.0/src/seestar_photometry/project.py +180 -0
  50. seestar_photometry-0.2.0/src/seestar_photometry/quality.py +170 -0
  51. seestar_photometry-0.2.0/src/seestar_photometry/report.py +337 -0
  52. seestar_photometry-0.2.0/src/seestar_photometry/stacking.py +248 -0
  53. seestar_photometry-0.2.0/tests/conftest.py +357 -0
  54. seestar_photometry-0.2.0/tests/test_calibration.py +222 -0
  55. seestar_photometry-0.2.0/tests/test_frames.py +183 -0
  56. seestar_photometry-0.2.0/tests/test_imports.py +68 -0
  57. seestar_photometry-0.2.0/tests/test_lightcurves.py +319 -0
  58. seestar_photometry-0.2.0/tests/test_photometry.py +197 -0
  59. seestar_photometry-0.2.0/tests/test_pipeline.py +210 -0
  60. seestar_photometry-0.2.0/tests/test_real_data.py +464 -0
  61. seestar_photometry-0.2.0/tests/test_report.py +160 -0
  62. seestar_photometry-0.2.0/tools/build_example_data.py +304 -0
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .pytest_cache/
8
+ .coverage
9
+ htmlcov/
10
+
11
+ # uv
12
+ .venv/
13
+
14
+ # Editors
15
+ .idea/
16
+ .vscode/
17
+
18
+ # Secrets -- ASTROMETRY_KEY lives in the environment, never in the repo
19
+ .env
20
+
21
+ # Derived products. Everything the pipeline writes goes to a project work_dir, which
22
+ # should live outside the repo; these patterns are a backstop for a work_dir set to '.'
23
+ frames.ecsv
24
+ stars.ecsv
25
+ measurements.ecsv
26
+ catalogue_*.ecsv
27
+ diagnostics/
28
+ errors.log
29
+ *.wcs
30
+
31
+ # Build outputs
32
+ dist/
33
+ docs/_build/
34
+
35
+ # Example data: built by tools/build_example_data.py and published as a GitHub
36
+ # release asset, not committed. SHA256 is tracked so the checksum is reviewable.
37
+ example_data/*
38
+ !example_data/SHA256
@@ -0,0 +1,23 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.12"
7
+
8
+ sphinx:
9
+ configuration: docs/conf.py
10
+ fail_on_warning: false
11
+
12
+ python:
13
+ install:
14
+ # Install the package itself so autodoc imports the real modules and picks up the
15
+ # version. `sep` and the stacking extras are mocked in conf.py, so they are not needed
16
+ # to build -- but the docs extra brings sphinx, myst-parser and the theme.
17
+ - method: pip
18
+ path: .
19
+ extra_requirements:
20
+ - docs
21
+
22
+ formats:
23
+ - pdf
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-08-05
4
+
5
+ First release intended for publication.
6
+
7
+ ### Added
8
+
9
+ - **Raw sub-exposure support.** `load_frame` now recognises the single-plane Bayer layout
10
+ (`BAYERPAT`) and demosaics it on load, so a raw 20 s sub is interchangeable with an
11
+ on-board stack everywhere downstream. The native mosaic is kept on `frame.bayer`.
12
+ New `debayer` module.
13
+ - **Local stacking** (`stacking`, needs the `stack` extra): astroalign registration as a
14
+ similarity transform — rotation included, which alt-az frames require — plus a
15
+ footprint-weighted mean. `StackReport` records residual, field rotation, coverage and any
16
+ rejected sub. A local stack writes native-dialect exposure keywords, so `frame_metadata`
17
+ reads it exactly as it reads an on-board stack.
18
+ - **Bundled example data** (`examples`): 1000×1000 cutouts of real MW Cam observations —
19
+ three on-board stacks, a CrowdSky multi-extension frame, five raw subs, solved `.wcs`
20
+ sidecars and a trimmed Gaia DR3 table. Every documented example and every real-data test
21
+ runs offline.
22
+ - **Documentation** for Read the Docs: install, quickstart, six use-case pages driven by the
23
+ bundled data, an API reference, and the design-decision records.
24
+ - 40 real-data tests alongside the existing synthetic suite (162 total).
25
+
26
+ ### Fixed
27
+
28
+ - `wcs_cache_path` now strips a compression suffix, so `frame.fits.gz` caches its WCS to
29
+ `frame.wcs` rather than the unreachable `frame.fits.wcs`. Gzipped frames would otherwise
30
+ have been re-solved on every run.
31
+ - An empty light curve keeps its full column schema, so a downstream call reports "no epochs"
32
+ instead of raising a bare `KeyError: 'dmag'`.
33
+ - `pipeline` uses a thread pool when `workers == 1`; a single worker gained nothing from a
34
+ separate process and paid the full spawn cost. A dead process pool now raises a message
35
+ naming the usual Windows cause (a driver without an `if __name__ == "__main__"` guard)
36
+ instead of an opaque `BrokenProcessPool`.
37
+ - `forced_photometry` no longer emits a divide warning for a fully off-frame aperture; the
38
+ SNR is `nan`, which is the right answer.
39
+ - CrowdSky's `FWHM` header values are in **arcsec** while this package reports **pixels**.
40
+ `quality.onboard_quality` renames them `onboard_fwhm_*_arcsec` so the two are not compared
41
+ by accident — they looked 2.4× apart until the plate scale was applied, after which they
42
+ agree to 2%.
43
+
44
+ ## 0.1.0
45
+
46
+ Initial extraction of the photometry engine from the MW Cam analysis repo, where it had
47
+ existed as three diverging copies. See `docs/migration-from-mwcam.md`.
48
+
49
+ Verified against the frozen results it replaced: zero point bit-identical on 20 of 22 frames
50
+ (all 15 photometric-grade frames exactly 0.000 mmag apart), and forced photometry
51
+ bit-identical across a 52-frame CrowdSky set.
@@ -0,0 +1,82 @@
1
+ # seestar-photometry — conventions
2
+
3
+ Read this before changing code. The rationale for every numerical choice lives in
4
+ `docs/`; this file is the shape of the package.
5
+
6
+ ## Names
7
+
8
+ Distribution `seestar-photometry`, import package `seestar_photometry`. Python
9
+ import names can't contain hyphens.
10
+
11
+ ## Hard conventions
12
+
13
+ - **`BANDS = ("R", "G", "B")` is the canonical axis-0 order.** Every frame is a
14
+ `(3, ny, nx)` float32 array in that order. Every per-band array (`Extraction.rms`,
15
+ `.aperture`, `.fwhm`) is shape `(3,)` in that order. **Green is index 1 and is the
16
+ science band** (≈ Johnson V through the Seestar's IRCUT filter).
17
+
18
+ - **Image shape comes from the data, never the header.** `frame.g.shape` gives
19
+ `(ny, nx)`. The CrowdSky multi-extension layout has an *empty* primary HDU with no
20
+ `NAXIS1`/`NAXIS2`, so any code reading the shape from a header is broken on half
21
+ the datasets.
22
+
23
+ - **`SeestarFrame` and `Project` are thin data containers, not god objects.** All
24
+ analysis is module-level *functions* taking the object as their first argument
25
+ (`photometry.extract_sources(frame, ...)`). Methods that briefly lived on the
26
+ frame class were moved out on purpose.
27
+
28
+ - **Optional dependencies are imported lazily**, inside the function that needs
29
+ them — never at module top. `matplotlib` in particular: `import
30
+ seestar_photometry` must work in a core install. `tests/test_imports.py` asserts
31
+ `matplotlib` is absent from `sys.modules` after import, so this can't rot.
32
+
33
+ - **The standard photometry path and the cross-checks live in separate modules.**
34
+ `photometry` is what the science uses; `kron` (Kron/AUTO) is kept only for
35
+ star/galaxy separation and total-flux cross-checks. Do not promote it.
36
+
37
+ - **Derived products never go into the data tree or the FITS headers.** Tables and
38
+ figures go to `Project.work_dir`. The one exception is the per-frame `.wcs`
39
+ sidecar, which lives next to its FITS file because it is expensive to recompute
40
+ and shared between projects.
41
+
42
+ - **No defensive error handling in the measurement layer.** If a frame is broken,
43
+ let it raise; the batch runner in `pipeline` catches per-frame exceptions, records
44
+ a status, and keeps going. Don't scatter `try/except` through the science code.
45
+
46
+ ## Module layers
47
+
48
+ Each module imports only from layers above it.
49
+
50
+ | Layer | Modules |
51
+ |---|---|
52
+ | data | `frames` |
53
+ | measure | `photometry`, `astrometry`, `catalogs` |
54
+ | calibrate | `calibration`, `quality` |
55
+ | science | `lightcurves`, `contamination`, `depth` |
56
+ | orchestrate | `project`, `pipeline` |
57
+ | inspect | `plots`, `report` |
58
+ | cross-check | `kron` |
59
+
60
+ ## Two FITS layouts, two header dialects
61
+
62
+ Both normalise to the same in-memory frame. See `docs/data-format.md`.
63
+
64
+ | | `"cube"` — native Seestar | `"mef"` — CrowdSky |
65
+ |---|---|---|
66
+ | structure | 3-D primary HDU, FITS axes `(nx, ny, 3)` → numpy `(3, ny, nx)` | empty primary + `RED`/`GREEN`/`BLUE` ImageHDUs (+ `FOOTPRINT`, `STAR-TAB`) |
67
+ | sub count | `STACKCNT` | `NIMAGES` |
68
+ | `EXPTIME` | **per sub-exposure** (10 s) | **total on-sky** (e.g. 410 s) |
69
+ | total on-sky | `TOTALEXP` | `EXPTIME` |
70
+ | exposure span | `DATE-OBS` only | `OB-START` / `OB-END` (use these) |
71
+
72
+ That `EXPTIME` collision is the trap: reading it naively gives a per-sub time of
73
+ 410 s and a mid-exposure timestamp ~5 minutes wrong. `frames.frame_metadata`
74
+ resolves the dialect; never read these keywords directly.
75
+
76
+ ## Environment
77
+
78
+ `uv` only. `uv sync --extra dev`, then `uv run pytest` / `uv run python ...`.
79
+ Never `uv run --active`. Point PyCharm at `.venv`; don't let it make its own.
80
+
81
+ `ASTROMETRY_KEY` (astrometry.net) is read from the environment and never stored in
82
+ the repo — only needed for `solver="nova"`; the default ASTAP solver is offline.