enchase 2.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 (43) hide show
  1. enchase-2.1.0/LICENSE +21 -0
  2. enchase-2.1.0/NOTICE +49 -0
  3. enchase-2.1.0/PKG-INFO +236 -0
  4. enchase-2.1.0/README.md +193 -0
  5. enchase-2.1.0/chase/__init__.py +119 -0
  6. enchase-2.1.0/chase/_compat.py +78 -0
  7. enchase-2.1.0/chase/analysis/__init__.py +35 -0
  8. enchase-2.1.0/chase/analysis/contrast.py +70 -0
  9. enchase-2.1.0/chase/analysis/temperature.py +246 -0
  10. enchase-2.1.0/chase/analysis/validation.py +285 -0
  11. enchase-2.1.0/chase/calib/__init__.py +58 -0
  12. enchase-2.1.0/chase/calib/align.py +127 -0
  13. enchase-2.1.0/chase/calib/intensity.py +321 -0
  14. enchase-2.1.0/chase/calib/spatial.py +295 -0
  15. enchase-2.1.0/chase/calib/wavelength.py +149 -0
  16. enchase-2.1.0/chase/cli.py +213 -0
  17. enchase-2.1.0/chase/config.py +139 -0
  18. enchase-2.1.0/chase/core.py +115 -0
  19. enchase-2.1.0/chase/io/__init__.py +26 -0
  20. enchase-2.1.0/chase/io/download.py +146 -0
  21. enchase-2.1.0/chase/io/fits_io.py +207 -0
  22. enchase-2.1.0/chase/io/loader.py +310 -0
  23. enchase-2.1.0/chase/pipeline.py +314 -0
  24. enchase-2.1.0/chase/portal.py +191 -0
  25. enchase-2.1.0/chase/shifts_cache.py +84 -0
  26. enchase-2.1.0/chase/tui.py +210 -0
  27. enchase-2.1.0/chase/viz/__init__.py +19 -0
  28. enchase-2.1.0/chase/viz/animate.py +232 -0
  29. enchase-2.1.0/chase/viz/diagnostics.py +160 -0
  30. enchase-2.1.0/enchase/__init__.py +31 -0
  31. enchase-2.1.0/enchase.egg-info/PKG-INFO +236 -0
  32. enchase-2.1.0/enchase.egg-info/SOURCES.txt +41 -0
  33. enchase-2.1.0/enchase.egg-info/dependency_links.txt +1 -0
  34. enchase-2.1.0/enchase.egg-info/entry_points.txt +4 -0
  35. enchase-2.1.0/enchase.egg-info/requires.txt +30 -0
  36. enchase-2.1.0/enchase.egg-info/top_level.txt +2 -0
  37. enchase-2.1.0/pyproject.toml +54 -0
  38. enchase-2.1.0/setup.cfg +4 -0
  39. enchase-2.1.0/tests/test_analysis.py +47 -0
  40. enchase-2.1.0/tests/test_calib.py +157 -0
  41. enchase-2.1.0/tests/test_io.py +130 -0
  42. enchase-2.1.0/tests/test_pipeline.py +153 -0
  43. enchase-2.1.0/tests/test_validation.py +110 -0
enchase-2.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Waleed Ajmal
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.
enchase-2.1.0/NOTICE ADDED
@@ -0,0 +1,49 @@
1
+ chase-pipeline
2
+ Copyright (c) 2025-2026 Waleed Ajmal
3
+ Licensed under the MIT License (see LICENSE).
4
+
5
+ ================================================================================
6
+ Third-party components
7
+ ================================================================================
8
+
9
+ This product includes software adapted from `satprocess`
10
+ (https://github.com/FinlayDavis/satprocess), used under the BSD 3-Clause License.
11
+
12
+ BSD 3-Clause License
13
+
14
+ Copyright (c) 2025, Finlay Davis
15
+ All rights reserved.
16
+
17
+ Redistribution and use in source and binary forms, with or without
18
+ modification, are permitted provided that the following conditions are met:
19
+
20
+ 1. Redistributions of source code must retain the above copyright notice,
21
+ this list of conditions and the following disclaimer.
22
+ 2. Redistributions in binary form must reproduce the above copyright notice,
23
+ this list of conditions and the following disclaimer in the documentation
24
+ and/or other materials provided with the distribution.
25
+ 3. Neither the name of the copyright holder nor the names of its
26
+ contributors may be used to endorse or promote products derived from this
27
+ software without specific prior written permission.
28
+
29
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
33
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39
+ POSSIBILITY OF SUCH DAMAGE.
40
+
41
+ Specifically, the following functions were ported/adapted from satprocess and
42
+ carry the BSD-3 notice in their source:
43
+ - chase.calib.spatial.hough_disk_center (preprocess_image + detect_circles)
44
+ - chase.calib.wavelength.cross_correlation_shift, align_spectrum (calculate_shift)
45
+ - chase.calib.intensity.integral_scaling (calculate_scaling)
46
+ - chase.shifts_cache.* (CSV shift-cache)
47
+
48
+ The absolute intensity calibration uses ISPy (https://github.com/ISP-SST/ISPy)
49
+ when the optional `atlas` extra is installed.
enchase-2.1.0/PKG-INFO ADDED
@@ -0,0 +1,236 @@
1
+ Metadata-Version: 2.4
2
+ Name: enchase
3
+ Version: 2.1.0
4
+ Summary: enCHASE: calibration and analysis pipeline for CHASE/HIS solar spectroscopy (TUI + SDK + CLI)
5
+ Author: Waleed Ajmal
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Majboor/enchase
8
+ Project-URL: Repository, https://github.com/Majboor/enchase
9
+ Project-URL: Changelog, https://github.com/Majboor/enchase/blob/main/CHANGELOG.md
10
+ Keywords: solar,CHASE,HIS,spectroscopy,flare,calibration,astronomy
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
14
+ Classifier: Intended Audience :: Science/Research
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ License-File: NOTICE
19
+ Requires-Dist: numpy>=1.21
20
+ Requires-Dist: scipy>=1.7
21
+ Requires-Dist: astropy>=5.0
22
+ Requires-Dist: scikit-image>=0.19
23
+ Requires-Dist: opencv-python>=4.5
24
+ Requires-Dist: matplotlib>=3.4
25
+ Requires-Dist: Pillow>=9.0
26
+ Requires-Dist: tqdm>=4.60
27
+ Requires-Dist: requests>=2.25
28
+ Requires-Dist: tomli>=1.2; python_version < "3.11"
29
+ Provides-Extra: tui
30
+ Requires-Dist: textual>=0.40; extra == "tui"
31
+ Provides-Extra: portal
32
+ Requires-Dist: playwright>=1.40; extra == "portal"
33
+ Provides-Extra: atlas
34
+ Requires-Dist: ISPy; extra == "atlas"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=7.0; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Provides-Extra: all
39
+ Requires-Dist: textual>=0.40; extra == "all"
40
+ Requires-Dist: ISPy; extra == "all"
41
+ Requires-Dist: pytest>=7.0; extra == "all"
42
+ Dynamic: license-file
43
+
44
+ # enchase
45
+
46
+ Calibration and flare analysis for CHASE/HIS solar spectroscopy. Install one package, point it at data, get aligned cubes, animations, contrast profiles, and temperature maps.
47
+
48
+ [![tests](https://github.com/Majboor/enchase/actions/workflows/test.yml/badge.svg)](https://github.com/Majboor/enchase/actions/workflows/test.yml)
49
+ [![PyPI](https://img.shields.io/pypi/v/enchase)](https://pypi.org/project/enchase/)
50
+ ![python](https://img.shields.io/badge/python-3.9%2B-blue)
51
+ ![license](https://img.shields.io/badge/license-MIT-green)
52
+
53
+ <p align="center">
54
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/flare.gif" width="760" alt="Stabilised 2-panel animation of the 2023-03-29 X2.1 flare"><br>
55
+ <em>The 2023-03-29 X2.1 flare, stabilised by this pipeline. Photosphere left, chromosphere right.</em>
56
+ </p>
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ pip install enchase
62
+ ```
63
+
64
+ That gives you the `chase` module, the `chase` command, and the `chase-tui` interactive app. Extras: `pip install 'enchase[tui]'` (TUI), `'enchase[atlas]'` (absolute Fe I calibration), `'enchase[all]'` (everything).
65
+
66
+ ## Get data
67
+
68
+ Two ways.
69
+
70
+ **Option 1: our hosted dataset.** We host ready-to-use CHASE observations on an S3 server, including the 2026-07-04 M3.2 flare (48 files, 8.65 GiB). No portal account, no expiring links:
71
+
72
+ ```python
73
+ import boto3
74
+ s3 = boto3.client("s3", endpoint_url="https://s3.preservemy.world",
75
+ aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY,
76
+ region_name="garage")
77
+ s3.download_file("chase-data",
78
+ "events/2026/07/04/M3.2/raw/HA/RSM20260704T132940_0000_HA.fits",
79
+ "data/RSM20260704T132940_0000_HA.fits")
80
+ ```
81
+
82
+ Read credentials are free for research use: see [docs/S3_DATA.md](docs/S3_DATA.md) for the full guide (bucket layout, Colab setup, s3fs) and how to get keys.
83
+
84
+ **Option 2: the CHASE portal.** The portal ([ssdc.nju.edu.cn](https://ssdc.nju.edu.cn)) has a captcha, so you log in once in a real browser; after that everything is command line:
85
+
86
+ ```bash
87
+ pip install enchase[portal] && playwright install chromium
88
+
89
+ chase-portal login # opens the portal once; log in, close the window
90
+ chase-portal search --start "2023-03-29 02:00" --end "2023-03-29 03:00"
91
+ chase-portal urls --start "2023-03-29 02:00" --end "2023-03-29 03:00" -o links.txt
92
+ chase links.txt # downloads the files and runs the pipeline
93
+ ```
94
+
95
+ `urls` writes one signed link per line (they expire in ~24h, so mint right before downloading). `chase` also accepts a single URL or a folder of already-downloaded `*.fits`. Walkthrough: [docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md).
96
+
97
+ ## Run it
98
+
99
+ **Run this:**
100
+
101
+ ```bash
102
+ chase ./data --patch 940 1080 1870 2040 --contrast --temperature halpha
103
+ ```
104
+
105
+ **What happens:** loads every scan, tracks the patch across frames, resamples wavelengths onto one grid, stabilises with optical flow, then writes results to `./chase_out/`:
106
+
107
+ | You get | What it is |
108
+ |---|---|
109
+ | `quicklook.gif` | photosphere / chromosphere animation |
110
+ | `contrast_profile.gif` + `.npy` | wavelength-vs-time flare contrast |
111
+ | `ha_temp_maps.npy` + `temperature.gif` | chromospheric temperature maps |
112
+ | `aligned_data.npz` | the aligned cubes (also the resume checkpoint) |
113
+ | `diag_*.png` | patch overlay, quiet-Sun spectrum |
114
+
115
+ No patch coordinates yet? Run with `--full-fov --no-optical-flow --no-track` first, look at `diag_qs_spectrum.png` and the GIF, then pick a `[y0 y1 x0 x1]` box.
116
+
117
+ ## Rerun just one thing
118
+
119
+ The first run saved a checkpoint, so reruns skip the slow loading and alignment:
120
+
121
+ ```bash
122
+ chase ./data --out ./chase_out --only gif # re-render the animation. Takes seconds
123
+ chase ./data --out ./chase_out --only contrast # recompute contrast only
124
+ chase ./data --out ./chase_out --only temperature --temperature fe_voigt
125
+ ```
126
+
127
+ ## Turn things off
128
+
129
+ Every stage has a switch. Mix freely:
130
+
131
+ | Flag | What it disables |
132
+ |---|---|
133
+ | `--no-track` | cross-correlation patch tracking |
134
+ | `--no-resample` | per-frame wavelength resampling |
135
+ | `--no-drift` | spectral drift correction in the contrast stage |
136
+ | `--no-optical-flow` | optical-flow stabilisation |
137
+ | `--no-gif`, `--no-npz`, `--no-diagnostics` | those outputs |
138
+
139
+ `--no-resample --no-drift` together = completely untouched wavelength axis.
140
+
141
+ ## The same thing in Python
142
+
143
+ ```python
144
+ from chase import Config, run_pipeline
145
+
146
+ results = run_pipeline(Config(fits_dir="./data", patch=[940, 1080, 1870, 2040],
147
+ contrast=True, temperature="halpha"))
148
+
149
+ results["aligned"]["ha"].shape # (nframes, 118, H, W) stabilised float32
150
+ results["contrast"].shape # (nframes, 118)
151
+ results["gif"] # './chase_out/quicklook.gif'
152
+ ```
153
+
154
+ Rerun one step from the checkpoint: add `resume=True` and only the toggles you want.
155
+
156
+ Or skip the pipeline and call single functions:
157
+
158
+ ```python
159
+ import chase
160
+
161
+ cube, hdr, wav = chase.load_cube("RSM..._HA.fits") # one cube: (118, H, W) + wavelengths
162
+ seq = chase.load_flare_sequence("./data", patch=[...]) # tracked + resampled sequence
163
+ ha_al = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
164
+ contrast, _ = chase.contrast_profile(ha_al, background=seq["ha_bg"])
165
+ T, width = chase.halpha_width_temperature(ha_al[12], seq["wavelength_ha"])
166
+ ```
167
+
168
+ Each line works on its own. Full list with snippets: [docs/SDK.md](docs/SDK.md).
169
+
170
+ ## Prefer clicking to typing?
171
+
172
+ ```bash
173
+ chase-tui
174
+ ```
175
+
176
+ <p align="center">
177
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/tui_e2e_walkthrough.gif" width="800" alt="End-to-end chase-tui walkthrough">
178
+ </p>
179
+
180
+ Every checkbox is one of the switches above. There is also a one-file config workflow: `chase --config my.toml`, keys mirror the flags ([examples/config.toml](examples/config.toml)).
181
+
182
+ ## Why this exists (statement of need)
183
+
184
+ CHASE (the Chinese Hα Solar Explorer) scans the full solar disk in Hα (118 wavelengths, 6559.4 to 6565.1 Å) and Fe I (46 wavelengths near 6569 Å). The portal ships full-disk cubes with no co-alignment and a wavelength zero-point that drifts by ~0.8 channels per frame. Getting a science-ready region out of that takes real calibration work, which is why the data is under-used. Earlier scripts (the original `chase-pipeline`, Finlay Davis's `satprocess`) each solved part of the problem. This package merges them into one installable tool that preserves the physics: shift-then-crop tracking, per-frame wavelength resampling, alignment on a flare-free channel, absolute atlas calibration, and the correct temperature inversion per line. The reasoning behind each rule is in [docs/CALIBRATION.md](docs/CALIBRATION.md).
185
+
186
+ ## What lives where
187
+
188
+ | Subpackage | Does | |
189
+ |---|---|---|
190
+ | `chase.io` | resumable download, FITS discovery, cube + sequence loading | <img src="assets/diag_patch.png" width="220"> |
191
+ | `chase.calib.spatial` / `.align` | shift-then-crop tracking, optical-flow stabilisation | <img src="assets/diag_optical_flow.png" width="220"> |
192
+ | `chase.calib.wavelength` | per-frame resampling onto a common grid, spectral-drift xcorr | <img src="assets/diag_qs_spectrum.png" width="220"> |
193
+ | `chase.calib.intensity` | quiet-Sun norm, integral scaling, FTS-atlas absolute calibration | <img src="assets/atlas_calibration.png" width="220"> |
194
+ | `chase.analysis.contrast` | wavelength-vs-time flare contrast | <img src="assets/contrast_profile.png" width="220"> |
195
+ | `chase.analysis.temperature` | Hα width to T (Molnar), Fe I to T (Planck / EB / Voigt) | <img src="assets/double_temp_map_peak.png" width="220"> |
196
+
197
+ <p align="center">
198
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/temperature.gif" width="720" alt="Double temperature map animation"><br>
199
+ <em>Double temperature map: chromosphere from Hα width (~10⁴ K), photosphere from Fe I Planck inversion (~5100 K).</em>
200
+ </p>
201
+
202
+ ## Documentation
203
+
204
+ - [docs/S3_DATA.md](docs/S3_DATA.md): use our hosted CHASE datasets from anywhere (incl. Google Colab)
205
+ - [docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md): CHASE portal to processed cube, end to end
206
+ - [docs/SDK.md](docs/SDK.md): every function, with snippets
207
+ - [docs/TUI.md](docs/TUI.md): TUI walkthrough
208
+ - [docs/CALIBRATION.md](docs/CALIBRATION.md): the science behind each calibration step
209
+ - [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md): dev setup, tests, style
210
+
211
+ ## Development
212
+
213
+ ```bash
214
+ git clone https://github.com/Majboor/enchase.git
215
+ cd enchase
216
+ pip install -e '.[all]'
217
+ pytest -q # 27 tests, synthetic FITS fixtures, no big data needed
218
+ ```
219
+
220
+ ## Acknowledgements
221
+
222
+ Built with the guidance and corrections of **Dr. Alexander Pietrow** (AIP) and **Dr. Malcolm Druett**. The Hough-circle limb centring, spectral cross-correlation, integral intensity scaling, and CSV shift-cache are adapted from **Finlay Davis**'s [`satprocess`](https://github.com/FinlayDavis/satprocess) (BSD-3-Clause; see [`NOTICE`](NOTICE)). Affiliation support from **Prof. Dr. Sayed Amer Mahmood** (University of the Punjab). Data courtesy of the **CHASE/HIS** mission and the [Solar Science Data Center, Nanjing University](https://ssdc.nju.edu.cn/NdchaseSatellite).
223
+
224
+ ## Citing
225
+
226
+ Author: Waleed Ajmal, ORCID [0009-0006-8222-1983](https://orcid.org/0009-0006-8222-1983). Software citation metadata is in [`CITATION.cff`](CITATION.cff).
227
+
228
+ If you use this pipeline, cite the methods it builds on:
229
+
230
+ - Molnar et al. 2019, *ApJ* **881**, 99. Hα width to temperature ([10.3847/1538-4357/ab2ba3](https://doi.org/10.3847/1538-4357/ab2ba3))
231
+ - The Eddington-Barbier photospheric inversion (A&A 2013, aa21259-13)
232
+ - The **ISPy** FTS solar-atlas calibration ([ISP-SST/ISPy](https://github.com/ISP-SST/ISPy))
233
+
234
+ ## License
235
+
236
+ MIT. Ported `satprocess` code stays BSD-3-Clause, listed in [`NOTICE`](NOTICE).
@@ -0,0 +1,193 @@
1
+ # enchase
2
+
3
+ Calibration and flare analysis for CHASE/HIS solar spectroscopy. Install one package, point it at data, get aligned cubes, animations, contrast profiles, and temperature maps.
4
+
5
+ [![tests](https://github.com/Majboor/enchase/actions/workflows/test.yml/badge.svg)](https://github.com/Majboor/enchase/actions/workflows/test.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/enchase)](https://pypi.org/project/enchase/)
7
+ ![python](https://img.shields.io/badge/python-3.9%2B-blue)
8
+ ![license](https://img.shields.io/badge/license-MIT-green)
9
+
10
+ <p align="center">
11
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/flare.gif" width="760" alt="Stabilised 2-panel animation of the 2023-03-29 X2.1 flare"><br>
12
+ <em>The 2023-03-29 X2.1 flare, stabilised by this pipeline. Photosphere left, chromosphere right.</em>
13
+ </p>
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install enchase
19
+ ```
20
+
21
+ That gives you the `chase` module, the `chase` command, and the `chase-tui` interactive app. Extras: `pip install 'enchase[tui]'` (TUI), `'enchase[atlas]'` (absolute Fe I calibration), `'enchase[all]'` (everything).
22
+
23
+ ## Get data
24
+
25
+ Two ways.
26
+
27
+ **Option 1: our hosted dataset.** We host ready-to-use CHASE observations on an S3 server, including the 2026-07-04 M3.2 flare (48 files, 8.65 GiB). No portal account, no expiring links:
28
+
29
+ ```python
30
+ import boto3
31
+ s3 = boto3.client("s3", endpoint_url="https://s3.preservemy.world",
32
+ aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY,
33
+ region_name="garage")
34
+ s3.download_file("chase-data",
35
+ "events/2026/07/04/M3.2/raw/HA/RSM20260704T132940_0000_HA.fits",
36
+ "data/RSM20260704T132940_0000_HA.fits")
37
+ ```
38
+
39
+ Read credentials are free for research use: see [docs/S3_DATA.md](docs/S3_DATA.md) for the full guide (bucket layout, Colab setup, s3fs) and how to get keys.
40
+
41
+ **Option 2: the CHASE portal.** The portal ([ssdc.nju.edu.cn](https://ssdc.nju.edu.cn)) has a captcha, so you log in once in a real browser; after that everything is command line:
42
+
43
+ ```bash
44
+ pip install enchase[portal] && playwright install chromium
45
+
46
+ chase-portal login # opens the portal once; log in, close the window
47
+ chase-portal search --start "2023-03-29 02:00" --end "2023-03-29 03:00"
48
+ chase-portal urls --start "2023-03-29 02:00" --end "2023-03-29 03:00" -o links.txt
49
+ chase links.txt # downloads the files and runs the pipeline
50
+ ```
51
+
52
+ `urls` writes one signed link per line (they expire in ~24h, so mint right before downloading). `chase` also accepts a single URL or a folder of already-downloaded `*.fits`. Walkthrough: [docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md).
53
+
54
+ ## Run it
55
+
56
+ **Run this:**
57
+
58
+ ```bash
59
+ chase ./data --patch 940 1080 1870 2040 --contrast --temperature halpha
60
+ ```
61
+
62
+ **What happens:** loads every scan, tracks the patch across frames, resamples wavelengths onto one grid, stabilises with optical flow, then writes results to `./chase_out/`:
63
+
64
+ | You get | What it is |
65
+ |---|---|
66
+ | `quicklook.gif` | photosphere / chromosphere animation |
67
+ | `contrast_profile.gif` + `.npy` | wavelength-vs-time flare contrast |
68
+ | `ha_temp_maps.npy` + `temperature.gif` | chromospheric temperature maps |
69
+ | `aligned_data.npz` | the aligned cubes (also the resume checkpoint) |
70
+ | `diag_*.png` | patch overlay, quiet-Sun spectrum |
71
+
72
+ No patch coordinates yet? Run with `--full-fov --no-optical-flow --no-track` first, look at `diag_qs_spectrum.png` and the GIF, then pick a `[y0 y1 x0 x1]` box.
73
+
74
+ ## Rerun just one thing
75
+
76
+ The first run saved a checkpoint, so reruns skip the slow loading and alignment:
77
+
78
+ ```bash
79
+ chase ./data --out ./chase_out --only gif # re-render the animation. Takes seconds
80
+ chase ./data --out ./chase_out --only contrast # recompute contrast only
81
+ chase ./data --out ./chase_out --only temperature --temperature fe_voigt
82
+ ```
83
+
84
+ ## Turn things off
85
+
86
+ Every stage has a switch. Mix freely:
87
+
88
+ | Flag | What it disables |
89
+ |---|---|
90
+ | `--no-track` | cross-correlation patch tracking |
91
+ | `--no-resample` | per-frame wavelength resampling |
92
+ | `--no-drift` | spectral drift correction in the contrast stage |
93
+ | `--no-optical-flow` | optical-flow stabilisation |
94
+ | `--no-gif`, `--no-npz`, `--no-diagnostics` | those outputs |
95
+
96
+ `--no-resample --no-drift` together = completely untouched wavelength axis.
97
+
98
+ ## The same thing in Python
99
+
100
+ ```python
101
+ from chase import Config, run_pipeline
102
+
103
+ results = run_pipeline(Config(fits_dir="./data", patch=[940, 1080, 1870, 2040],
104
+ contrast=True, temperature="halpha"))
105
+
106
+ results["aligned"]["ha"].shape # (nframes, 118, H, W) stabilised float32
107
+ results["contrast"].shape # (nframes, 118)
108
+ results["gif"] # './chase_out/quicklook.gif'
109
+ ```
110
+
111
+ Rerun one step from the checkpoint: add `resume=True` and only the toggles you want.
112
+
113
+ Or skip the pipeline and call single functions:
114
+
115
+ ```python
116
+ import chase
117
+
118
+ cube, hdr, wav = chase.load_cube("RSM..._HA.fits") # one cube: (118, H, W) + wavelengths
119
+ seq = chase.load_flare_sequence("./data", patch=[...]) # tracked + resampled sequence
120
+ ha_al = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
121
+ contrast, _ = chase.contrast_profile(ha_al, background=seq["ha_bg"])
122
+ T, width = chase.halpha_width_temperature(ha_al[12], seq["wavelength_ha"])
123
+ ```
124
+
125
+ Each line works on its own. Full list with snippets: [docs/SDK.md](docs/SDK.md).
126
+
127
+ ## Prefer clicking to typing?
128
+
129
+ ```bash
130
+ chase-tui
131
+ ```
132
+
133
+ <p align="center">
134
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/tui_e2e_walkthrough.gif" width="800" alt="End-to-end chase-tui walkthrough">
135
+ </p>
136
+
137
+ Every checkbox is one of the switches above. There is also a one-file config workflow: `chase --config my.toml`, keys mirror the flags ([examples/config.toml](examples/config.toml)).
138
+
139
+ ## Why this exists (statement of need)
140
+
141
+ CHASE (the Chinese Hα Solar Explorer) scans the full solar disk in Hα (118 wavelengths, 6559.4 to 6565.1 Å) and Fe I (46 wavelengths near 6569 Å). The portal ships full-disk cubes with no co-alignment and a wavelength zero-point that drifts by ~0.8 channels per frame. Getting a science-ready region out of that takes real calibration work, which is why the data is under-used. Earlier scripts (the original `chase-pipeline`, Finlay Davis's `satprocess`) each solved part of the problem. This package merges them into one installable tool that preserves the physics: shift-then-crop tracking, per-frame wavelength resampling, alignment on a flare-free channel, absolute atlas calibration, and the correct temperature inversion per line. The reasoning behind each rule is in [docs/CALIBRATION.md](docs/CALIBRATION.md).
142
+
143
+ ## What lives where
144
+
145
+ | Subpackage | Does | |
146
+ |---|---|---|
147
+ | `chase.io` | resumable download, FITS discovery, cube + sequence loading | <img src="assets/diag_patch.png" width="220"> |
148
+ | `chase.calib.spatial` / `.align` | shift-then-crop tracking, optical-flow stabilisation | <img src="assets/diag_optical_flow.png" width="220"> |
149
+ | `chase.calib.wavelength` | per-frame resampling onto a common grid, spectral-drift xcorr | <img src="assets/diag_qs_spectrum.png" width="220"> |
150
+ | `chase.calib.intensity` | quiet-Sun norm, integral scaling, FTS-atlas absolute calibration | <img src="assets/atlas_calibration.png" width="220"> |
151
+ | `chase.analysis.contrast` | wavelength-vs-time flare contrast | <img src="assets/contrast_profile.png" width="220"> |
152
+ | `chase.analysis.temperature` | Hα width to T (Molnar), Fe I to T (Planck / EB / Voigt) | <img src="assets/double_temp_map_peak.png" width="220"> |
153
+
154
+ <p align="center">
155
+ <img src="https://raw.githubusercontent.com/Majboor/enchase/main/assets/temperature.gif" width="720" alt="Double temperature map animation"><br>
156
+ <em>Double temperature map: chromosphere from Hα width (~10⁴ K), photosphere from Fe I Planck inversion (~5100 K).</em>
157
+ </p>
158
+
159
+ ## Documentation
160
+
161
+ - [docs/S3_DATA.md](docs/S3_DATA.md): use our hosted CHASE datasets from anywhere (incl. Google Colab)
162
+ - [docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md): CHASE portal to processed cube, end to end
163
+ - [docs/SDK.md](docs/SDK.md): every function, with snippets
164
+ - [docs/TUI.md](docs/TUI.md): TUI walkthrough
165
+ - [docs/CALIBRATION.md](docs/CALIBRATION.md): the science behind each calibration step
166
+ - [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md): dev setup, tests, style
167
+
168
+ ## Development
169
+
170
+ ```bash
171
+ git clone https://github.com/Majboor/enchase.git
172
+ cd enchase
173
+ pip install -e '.[all]'
174
+ pytest -q # 27 tests, synthetic FITS fixtures, no big data needed
175
+ ```
176
+
177
+ ## Acknowledgements
178
+
179
+ Built with the guidance and corrections of **Dr. Alexander Pietrow** (AIP) and **Dr. Malcolm Druett**. The Hough-circle limb centring, spectral cross-correlation, integral intensity scaling, and CSV shift-cache are adapted from **Finlay Davis**'s [`satprocess`](https://github.com/FinlayDavis/satprocess) (BSD-3-Clause; see [`NOTICE`](NOTICE)). Affiliation support from **Prof. Dr. Sayed Amer Mahmood** (University of the Punjab). Data courtesy of the **CHASE/HIS** mission and the [Solar Science Data Center, Nanjing University](https://ssdc.nju.edu.cn/NdchaseSatellite).
180
+
181
+ ## Citing
182
+
183
+ Author: Waleed Ajmal, ORCID [0009-0006-8222-1983](https://orcid.org/0009-0006-8222-1983). Software citation metadata is in [`CITATION.cff`](CITATION.cff).
184
+
185
+ If you use this pipeline, cite the methods it builds on:
186
+
187
+ - Molnar et al. 2019, *ApJ* **881**, 99. Hα width to temperature ([10.3847/1538-4357/ab2ba3](https://doi.org/10.3847/1538-4357/ab2ba3))
188
+ - The Eddington-Barbier photospheric inversion (A&A 2013, aa21259-13)
189
+ - The **ISPy** FTS solar-atlas calibration ([ISP-SST/ISPy](https://github.com/ISP-SST/ISPy))
190
+
191
+ ## License
192
+
193
+ MIT. Ported `satprocess` code stays BSD-3-Clause, listed in [`NOTICE`](NOTICE).
@@ -0,0 +1,119 @@
1
+ """chase — a modular CHASE/HIS calibration + flare-analysis pipeline.
2
+
3
+ Nothing here is a forced pipeline: every stage is an importable function you can
4
+ call on its own, *and* there is a one-call orchestrator (:func:`run_pipeline`), a
5
+ config-file workflow (:class:`Config`), a CLI (``chase``), and a TUI
6
+ (``chase-tui``) layered on top of the exact same functions.
7
+
8
+ Quick start (SDK)
9
+ -----------------
10
+ >>> import chase
11
+ >>> cube, hdr, wav = chase.load_cube("RSM..._HA.fits")
12
+ >>> seq = chase.load_flare_sequence("/path/to/fits", patch=[940, 1080, 1870, 2040])
13
+ >>> aligned = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
14
+ >>> contrast, _ = chase.contrast_profile(aligned, background=seq["ha_bg"])
15
+
16
+ Quick start (lazy)
17
+ ------------------
18
+ >>> from chase import Config, run_pipeline
19
+ >>> run_pipeline(Config(fits_dir="/path/to/fits", patch=[940, 1080, 1870, 2040],
20
+ ... contrast=True, temperature="double"))
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from . import _compat # noqa: F401 (lzma stub + headless matplotlib)
26
+
27
+ __version__ = "2.1.0"
28
+
29
+ # --- I/O -------------------------------------------------------------------
30
+ from .io import (
31
+ discover_fits,
32
+ download_file,
33
+ extract_disk_center,
34
+ load_cube,
35
+ load_fits_data,
36
+ load_flare_sequence,
37
+ resolve_inputs,
38
+ save_cube_fits,
39
+ save_npz,
40
+ )
41
+ download = download_file # SDK alias
42
+
43
+ # --- Calibration -----------------------------------------------------------
44
+ from .calib import (
45
+ align_spectrum,
46
+ derotate,
47
+ derotate_crop,
48
+ atlas_calibrate,
49
+ atlas_fit_quality,
50
+ correct_spectral_drift,
51
+ find_quiet_patch,
52
+ quiet_sun_mask,
53
+ create_optical_flow_solver,
54
+ find_disk_center,
55
+ hough_disk_center,
56
+ integral_scaling,
57
+ normalize_intensity,
58
+ optical_flow_align,
59
+ phase_shift,
60
+ planck_inversion,
61
+ recenter,
62
+ resample_wavelength,
63
+ track_and_crop,
64
+ warp_cube,
65
+ )
66
+
67
+ # --- Analysis --------------------------------------------------------------
68
+ from .analysis import (
69
+ contrast_profile,
70
+ fe_eb_temperature,
71
+ fe_planck_temperature,
72
+ fe_voigt_temperature,
73
+ frame_rms,
74
+ halpha_width_map,
75
+ line_centroid,
76
+ residual_pointing,
77
+ residual_wavelength,
78
+ stray_light_estimate,
79
+ halpha_width_temperature,
80
+ width_to_temperature,
81
+ )
82
+
83
+ # --- Visualisation ---------------------------------------------------------
84
+ from .viz import (
85
+ make_animation,
86
+ make_contrast_animation,
87
+ make_temperature_animation,
88
+ )
89
+
90
+ # --- Orchestration ---------------------------------------------------------
91
+ from .config import Config, load_config
92
+ from .pipeline import run_pipeline
93
+
94
+ __all__ = [
95
+ "__version__",
96
+ # io
97
+ "download", "download_file", "resolve_inputs", "discover_fits",
98
+ "load_cube", "load_fits_data", "load_flare_sequence", "extract_disk_center",
99
+ "save_cube_fits", "save_npz",
100
+ # calib
101
+ "derotate", "derotate_crop",
102
+ "find_disk_center", "hough_disk_center", "recenter", "phase_shift",
103
+ "track_and_crop", "optical_flow_align", "warp_cube",
104
+ "create_optical_flow_solver", "resample_wavelength", "correct_spectral_drift",
105
+ "align_spectrum", "normalize_intensity", "integral_scaling",
106
+ "atlas_calibrate", "atlas_fit_quality", "quiet_sun_mask", "find_quiet_patch",
107
+ "planck_inversion",
108
+ # analysis
109
+ "contrast_profile", "halpha_width_map", "halpha_width_temperature",
110
+ "width_to_temperature", "fe_planck_temperature", "fe_eb_temperature",
111
+ "fe_voigt_temperature",
112
+ # validation
113
+ "residual_pointing", "line_centroid", "residual_wavelength", "frame_rms",
114
+ "stray_light_estimate",
115
+ # viz
116
+ "make_animation", "make_contrast_animation", "make_temperature_animation",
117
+ # orchestration
118
+ "Config", "load_config", "run_pipeline",
119
+ ]