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.
- enchase-2.1.0/LICENSE +21 -0
- enchase-2.1.0/NOTICE +49 -0
- enchase-2.1.0/PKG-INFO +236 -0
- enchase-2.1.0/README.md +193 -0
- enchase-2.1.0/chase/__init__.py +119 -0
- enchase-2.1.0/chase/_compat.py +78 -0
- enchase-2.1.0/chase/analysis/__init__.py +35 -0
- enchase-2.1.0/chase/analysis/contrast.py +70 -0
- enchase-2.1.0/chase/analysis/temperature.py +246 -0
- enchase-2.1.0/chase/analysis/validation.py +285 -0
- enchase-2.1.0/chase/calib/__init__.py +58 -0
- enchase-2.1.0/chase/calib/align.py +127 -0
- enchase-2.1.0/chase/calib/intensity.py +321 -0
- enchase-2.1.0/chase/calib/spatial.py +295 -0
- enchase-2.1.0/chase/calib/wavelength.py +149 -0
- enchase-2.1.0/chase/cli.py +213 -0
- enchase-2.1.0/chase/config.py +139 -0
- enchase-2.1.0/chase/core.py +115 -0
- enchase-2.1.0/chase/io/__init__.py +26 -0
- enchase-2.1.0/chase/io/download.py +146 -0
- enchase-2.1.0/chase/io/fits_io.py +207 -0
- enchase-2.1.0/chase/io/loader.py +310 -0
- enchase-2.1.0/chase/pipeline.py +314 -0
- enchase-2.1.0/chase/portal.py +191 -0
- enchase-2.1.0/chase/shifts_cache.py +84 -0
- enchase-2.1.0/chase/tui.py +210 -0
- enchase-2.1.0/chase/viz/__init__.py +19 -0
- enchase-2.1.0/chase/viz/animate.py +232 -0
- enchase-2.1.0/chase/viz/diagnostics.py +160 -0
- enchase-2.1.0/enchase/__init__.py +31 -0
- enchase-2.1.0/enchase.egg-info/PKG-INFO +236 -0
- enchase-2.1.0/enchase.egg-info/SOURCES.txt +41 -0
- enchase-2.1.0/enchase.egg-info/dependency_links.txt +1 -0
- enchase-2.1.0/enchase.egg-info/entry_points.txt +4 -0
- enchase-2.1.0/enchase.egg-info/requires.txt +30 -0
- enchase-2.1.0/enchase.egg-info/top_level.txt +2 -0
- enchase-2.1.0/pyproject.toml +54 -0
- enchase-2.1.0/setup.cfg +4 -0
- enchase-2.1.0/tests/test_analysis.py +47 -0
- enchase-2.1.0/tests/test_calib.py +157 -0
- enchase-2.1.0/tests/test_io.py +130 -0
- enchase-2.1.0/tests/test_pipeline.py +153 -0
- 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
|
+
[](https://github.com/Majboor/enchase/actions/workflows/test.yml)
|
|
49
|
+
[](https://pypi.org/project/enchase/)
|
|
50
|
+

|
|
51
|
+

|
|
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).
|
enchase-2.1.0/README.md
ADDED
|
@@ -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
|
+
[](https://github.com/Majboor/enchase/actions/workflows/test.yml)
|
|
6
|
+
[](https://pypi.org/project/enchase/)
|
|
7
|
+

|
|
8
|
+

|
|
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
|
+
]
|