chasepy 2.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.
- chasepy-2.0.0/LICENSE +21 -0
- chasepy-2.0.0/NOTICE +49 -0
- chasepy-2.0.0/PKG-INFO +261 -0
- chasepy-2.0.0/README.md +221 -0
- chasepy-2.0.0/chase/__init__.py +104 -0
- chasepy-2.0.0/chase/_compat.py +78 -0
- chasepy-2.0.0/chase/analysis/__init__.py +23 -0
- chasepy-2.0.0/chase/analysis/contrast.py +70 -0
- chasepy-2.0.0/chase/analysis/temperature.py +246 -0
- chasepy-2.0.0/chase/calib/__init__.py +48 -0
- chasepy-2.0.0/chase/calib/align.py +127 -0
- chasepy-2.0.0/chase/calib/intensity.py +142 -0
- chasepy-2.0.0/chase/calib/spatial.py +225 -0
- chasepy-2.0.0/chase/calib/wavelength.py +149 -0
- chasepy-2.0.0/chase/cli.py +205 -0
- chasepy-2.0.0/chase/config.py +130 -0
- chasepy-2.0.0/chase/core.py +115 -0
- chasepy-2.0.0/chase/io/__init__.py +25 -0
- chasepy-2.0.0/chase/io/download.py +146 -0
- chasepy-2.0.0/chase/io/fits_io.py +147 -0
- chasepy-2.0.0/chase/io/loader.py +195 -0
- chasepy-2.0.0/chase/pipeline.py +303 -0
- chasepy-2.0.0/chase/shifts_cache.py +84 -0
- chasepy-2.0.0/chase/tui.py +206 -0
- chasepy-2.0.0/chase/viz/__init__.py +19 -0
- chasepy-2.0.0/chase/viz/animate.py +224 -0
- chasepy-2.0.0/chase/viz/diagnostics.py +110 -0
- chasepy-2.0.0/chasepy.egg-info/PKG-INFO +261 -0
- chasepy-2.0.0/chasepy.egg-info/SOURCES.txt +37 -0
- chasepy-2.0.0/chasepy.egg-info/dependency_links.txt +1 -0
- chasepy-2.0.0/chasepy.egg-info/entry_points.txt +3 -0
- chasepy-2.0.0/chasepy.egg-info/requires.txt +27 -0
- chasepy-2.0.0/chasepy.egg-info/top_level.txt +1 -0
- chasepy-2.0.0/pyproject.toml +51 -0
- chasepy-2.0.0/setup.cfg +4 -0
- chasepy-2.0.0/tests/test_analysis.py +47 -0
- chasepy-2.0.0/tests/test_calib.py +111 -0
- chasepy-2.0.0/tests/test_io.py +59 -0
- chasepy-2.0.0/tests/test_pipeline.py +131 -0
chasepy-2.0.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.
|
chasepy-2.0.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.
|
chasepy-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chasepy
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Modular CHASE/HIS solar calibration and flare-analysis pipeline (TUI + SDK + CLI)
|
|
5
|
+
Author: Waleed Ajmal
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Majboor/chase-pipeline
|
|
8
|
+
Project-URL: Repository, https://github.com/Majboor/chase-pipeline
|
|
9
|
+
Keywords: solar,CHASE,HIS,spectroscopy,flare,calibration,astronomy
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
License-File: NOTICE
|
|
18
|
+
Requires-Dist: numpy>=1.21
|
|
19
|
+
Requires-Dist: scipy>=1.7
|
|
20
|
+
Requires-Dist: astropy>=5.0
|
|
21
|
+
Requires-Dist: scikit-image>=0.19
|
|
22
|
+
Requires-Dist: opencv-python>=4.5
|
|
23
|
+
Requires-Dist: matplotlib>=3.4
|
|
24
|
+
Requires-Dist: Pillow>=9.0
|
|
25
|
+
Requires-Dist: tqdm>=4.60
|
|
26
|
+
Requires-Dist: requests>=2.25
|
|
27
|
+
Requires-Dist: tomli>=1.2; python_version < "3.11"
|
|
28
|
+
Provides-Extra: tui
|
|
29
|
+
Requires-Dist: textual>=0.40; extra == "tui"
|
|
30
|
+
Provides-Extra: atlas
|
|
31
|
+
Requires-Dist: ISPy; extra == "atlas"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
35
|
+
Provides-Extra: all
|
|
36
|
+
Requires-Dist: textual>=0.40; extra == "all"
|
|
37
|
+
Requires-Dist: ISPy; extra == "all"
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == "all"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# chase-pipeline
|
|
42
|
+
|
|
43
|
+
**A modular calibration and flare-analysis pipeline for CHASE / HIS solar spectroscopy — with a TUI, a Python SDK, and a one-file config workflow.**
|
|
44
|
+
|
|
45
|
+
[](https://github.com/Majboor/chase-pipeline/actions/workflows/test.yml)
|
|
46
|
+

|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
CHASE (the **Chinese Hα Solar Explorer**) scans the full solar disk in Hα (118
|
|
50
|
+
wavelengths, 6559.4–6565.1 Å) and Fe I (46 wavelengths, ~6569 Å). The raw data is
|
|
51
|
+
scientifically rich but awkward to use — every frame drifts spatially *and*
|
|
52
|
+
spectrally, and turning a stack of `RSM…_HA.fits` cubes into a clean, aligned,
|
|
53
|
+
calibrated data product takes a lot of careful work. `chase-pipeline` does that
|
|
54
|
+
work for you, and lets you do as much or as little of it as you want.
|
|
55
|
+
|
|
56
|
+
<p align="center">
|
|
57
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/flare.gif" width="760" alt="Stabilised 2-panel animation of the 2023-03-29 X2.1 flare"><br>
|
|
58
|
+
<em>The 2023-03-29 <strong>X2.1</strong> flare, stabilised by this pipeline:
|
|
59
|
+
photosphere (Hα continuum) | chromosphere (Hα core). 25 frames, per-frame colour scaling.<br>
|
|
60
|
+
Rendered with <code>--flow-method farneback</code>: TV-L1 optical flow blocks at the flare peak
|
|
61
|
+
(its total-variation regularizer turns the brightening into a rigid motion block); Farneback stays
|
|
62
|
+
clean. See <a href="docs/FLARE_STABILIZATION.md">docs/FLARE_STABILIZATION.md</a>.</em>
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Why this exists (statement of need)
|
|
68
|
+
|
|
69
|
+
CHASE data is under-used because it is hard to use: the portal ships full-disk
|
|
70
|
+
cubes with no co-alignment, a per-frame wavelength zero-point that drifts by ~0.8
|
|
71
|
+
channels, and no turnkey way to extract a science-ready region. Existing scripts
|
|
72
|
+
(including the original `chase-pipeline` and Finlay Davis's `satprocess`) each
|
|
73
|
+
solved part of the problem. **This package unifies them into one installable
|
|
74
|
+
tool** that:
|
|
75
|
+
|
|
76
|
+
- **just works for the lazy user** — point at a folder, set a field of view and a
|
|
77
|
+
few true/false flags in a text file (or use the TUI), run once, get an aligned
|
|
78
|
+
**FITS/npz cube + images/GIF** out;
|
|
79
|
+
- **stays hackable for the power user** — every stage is an importable function
|
|
80
|
+
(`import chase`), nothing is a forced monolithic pipeline;
|
|
81
|
+
- **preserves the physics** — shift-then-crop tracking, per-frame wavelength
|
|
82
|
+
resampling, flare-free alignment, absolute atlas calibration, and the correct
|
|
83
|
+
temperature inversions per line (it does not "simplify away" the hard-won fixes).
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
From PyPI (the package is named `chasepy`; it installs the `chase` module and
|
|
88
|
+
the `chase` / `chase-tui` commands):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install chasepy # core
|
|
92
|
+
pip install 'chasepy[tui]' # + Textual TUI (chase-tui)
|
|
93
|
+
pip install 'chasepy[atlas]' # + ISPy for absolute Fe I temperature calibration
|
|
94
|
+
pip install 'chasepy[all]' # everything, incl. test deps
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or for development, clone and install editable:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/Majboor/chase-pipeline.git
|
|
101
|
+
cd chase-pipeline
|
|
102
|
+
pip install -e '.[all]'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Three ways to use it
|
|
106
|
+
|
|
107
|
+
### 1. TUI — interactive, no flags to memorise
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
chase-tui
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**End-to-end walkthrough** — the real TUI configured on the 2023-03-29 X2.1
|
|
114
|
+
flare (data source → field of view → calibration → temperature → a before/after
|
|
115
|
+
of the stabilisation → the actual `flare.gif` / `temperature.gif` /
|
|
116
|
+
`contrast_profile.gif` output):
|
|
117
|
+
|
|
118
|
+
<p align="center">
|
|
119
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/tui_e2e_walkthrough.gif" width="800" alt="End-to-end chase-tui walkthrough">
|
|
120
|
+
</p>
|
|
121
|
+
|
|
122
|
+
> Prefer the full-resolution, scrubbable version?
|
|
123
|
+
> [assets/tui_e2e_walkthrough.mp4](https://github.com/Majboor/chase-pipeline/raw/feat/unified-pipeline-tui-sdk/assets/tui_e2e_walkthrough.mp4).
|
|
124
|
+
|
|
125
|
+
Pick a data source, enter a full FOV or an arbitrary patch (and a separate, smaller
|
|
126
|
+
*alignment* patch if you want), tick the calibrations you want, and hit **Run** —
|
|
127
|
+
live progress streams into the log pane. The TUI is a thin front-end over the SDK;
|
|
128
|
+
it duplicates no logic.
|
|
129
|
+
|
|
130
|
+
**Before / after** — what the spatial calibration buys you. Left: a raw fixed
|
|
131
|
+
crop of the portal data, drifting frame to frame. Right: the same patch after
|
|
132
|
+
shift-then-crop tracking + optical-flow stabilisation (Hα core):
|
|
133
|
+
|
|
134
|
+
<p align="center">
|
|
135
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/before_after.gif" width="760" alt="Raw fixed crop vs tracked + optical-flow stabilised, Hα core">
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
### 2. Config file — the "lazy" one-shot workflow
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
chase --config examples/config.toml
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```toml
|
|
145
|
+
# examples/config.toml
|
|
146
|
+
fits_dir = "/data/20230329_X12/fits"
|
|
147
|
+
out_dir = "./chase_out"
|
|
148
|
+
patch = [940, 1080, 1870, 2040] # [y0, y1, x0, x1]; omit + full_fov=true for whole disk
|
|
149
|
+
contrast = true
|
|
150
|
+
temperature = "double" # halpha + fe_planck
|
|
151
|
+
gif = true
|
|
152
|
+
freeze_clim = true
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 3. CLI — explicit flags (backward compatible)
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
chase /data/20230329_X12/fits \
|
|
159
|
+
--patch 940 1080 1870 2040 \
|
|
160
|
+
--align-patch 980 1040 1920 1990 \
|
|
161
|
+
--contrast --temperature double --freeze-clim
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Every run checkpoints the aligned cubes to `out_dir/aligned_data.npz`, so you
|
|
165
|
+
never have to redo the slow load + align stages just to tweak an output —
|
|
166
|
+
rerun a single step against the checkpoint with `--only` (implies `--resume`):
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
chase /data/20230329_X12/fits --out ./chase_out --only gif # just re-render the animation
|
|
170
|
+
chase /data/20230329_X12/fits --out ./chase_out --only contrast,gif --no-drift
|
|
171
|
+
chase /data/20230329_X12/fits --out ./chase_out --only temperature --temperature fe_voigt
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Steps: `gif`, `contrast`, `temperature`, `npz`, `fits`, `diagnostics`. Plain
|
|
175
|
+
`--resume` keeps your normal toggles and only skips load + align. (Changing the
|
|
176
|
+
patch or alignment options needs a fresh run *without* `--resume` — the
|
|
177
|
+
checkpoint stores the cubes as they were cropped and aligned.)
|
|
178
|
+
|
|
179
|
+
### …and the SDK — call any single stage
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import chase
|
|
183
|
+
|
|
184
|
+
# Load + crop-track a sequence (shift-then-crop, per-frame wavelength resample)
|
|
185
|
+
seq = chase.load_flare_sequence("/data/20230329_X12/fits",
|
|
186
|
+
patch=[940, 1080, 1870, 2040])
|
|
187
|
+
|
|
188
|
+
# Fine-stabilise with optical flow on the flare-free channel; HA stays float32
|
|
189
|
+
aligned = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
|
|
190
|
+
|
|
191
|
+
# Science products
|
|
192
|
+
contrast, _ = chase.contrast_profile(aligned, background=seq["ha_bg"])
|
|
193
|
+
ha_T, _ = chase.halpha_width_temperature(aligned[24], seq["wavelength_ha"]) # Molnar 2019
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Or run the whole thing in one call:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from chase import Config, run_pipeline
|
|
200
|
+
run_pipeline(Config(fits_dir="/data/.../fits", patch=[940,1080,1870,2040],
|
|
201
|
+
contrast=True, temperature="double"))
|
|
202
|
+
|
|
203
|
+
# Later: rerun only the outputs you care about, from the saved checkpoint
|
|
204
|
+
run_pipeline(Config(fits_dir="/data/.../fits", out_dir="./chase_out",
|
|
205
|
+
resume=True, gif=True, temperature="fe_voigt"))
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## What each subpackage does (one figure each)
|
|
209
|
+
|
|
210
|
+
| Subpackage | What it does | |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `chase.io` | Resumable download, folder / list-file / URL discovery, cube + sequence loading | <img src="assets/diag_patch.png" width="220"> |
|
|
213
|
+
| `chase.calib.spatial` / `.align` | Shift-then-crop tracking + optical-flow stabilisation | <img src="assets/diag_optical_flow.png" width="220"> |
|
|
214
|
+
| `chase.calib.wavelength` | Per-frame resampling onto a common grid; spectral-drift xcorr | <img src="assets/diag_qs_spectrum.png" width="220"> |
|
|
215
|
+
| `chase.calib.intensity` | QS norm, integral scaling, FTS-atlas absolute calibration (ISPy) | <img src="assets/atlas_calibration.png" width="220"> |
|
|
216
|
+
| `chase.analysis.contrast` | `(flare−bg)/bg − frame0` wavelength-vs-time profile | <img src="assets/contrast_profile.png" width="220"> |
|
|
217
|
+
| `chase.analysis.temperature` | Hα width→T (Molnar) + Fe I →T (Planck/EB/Voigt) | <img src="assets/double_temp_map_peak.png" width="220"> |
|
|
218
|
+
|
|
219
|
+
<p align="center">
|
|
220
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/temperature.gif" width="720" alt="Double temperature map animation"><br>
|
|
221
|
+
<em>Double temperature map: chromosphere from Hα width (~10⁴ K) and photosphere
|
|
222
|
+
from Fe I Planck inversion (~5,100 K, absolute-calibrated against the FTS atlas).</em>
|
|
223
|
+
</p>
|
|
224
|
+
|
|
225
|
+
## Documentation
|
|
226
|
+
|
|
227
|
+
- **[docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md)** — get from the CHASE portal to a processed cube, end to end.
|
|
228
|
+
- **[docs/SDK.md](docs/SDK.md)** — the Python API, every function + parameters.
|
|
229
|
+
- **[docs/TUI.md](docs/TUI.md)** — TUI walkthrough.
|
|
230
|
+
- **[docs/CALIBRATION.md](docs/CALIBRATION.md)** — the science: what each calibration does and why.
|
|
231
|
+
- **[docs/CONTRIBUTING.md](docs/CONTRIBUTING.md)** — dev setup, tests, style.
|
|
232
|
+
|
|
233
|
+
## Tests
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
pip install -e '.[dev]' && pytest -q # 22 fast tests on synthetic FITS fixtures
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Acknowledgements
|
|
240
|
+
|
|
241
|
+
Built with the guidance and corrections of **Dr. Alexander Pietrow** (AIP) and
|
|
242
|
+
**Dr. Malcolm Druett**. The Hough-circle limb centring, spectral
|
|
243
|
+
cross-correlation, integral intensity scaling, and CSV shift-cache are adapted
|
|
244
|
+
from **Finlay Davis**'s [`satprocess`](https://github.com/FinlayDavis/satprocess)
|
|
245
|
+
(BSD-3-Clause; see [`NOTICE`](NOTICE)). Affiliation support from **Prof. Dr. Sayed
|
|
246
|
+
Amer Mahmood** (University of the Punjab). Data courtesy of the **CHASE/HIS**
|
|
247
|
+
mission and the [Solar Science Data Center, Nanjing University](https://ssdc.nju.edu.cn/NdchaseSatellite).
|
|
248
|
+
|
|
249
|
+
## Citing
|
|
250
|
+
|
|
251
|
+
If you use this pipeline, please cite the methods it builds on:
|
|
252
|
+
|
|
253
|
+
- Molnar et al. 2019, *ApJ* **881**, 99 — Hα width → temperature ([10.3847/1538-4357/ab2ba3](https://doi.org/10.3847/1538-4357/ab2ba3)).
|
|
254
|
+
- The Eddington–Barbier photospheric inversion (A&A 2013, aa21259-13).
|
|
255
|
+
- The **ISPy** FTS solar-atlas calibration ([ISP-SST/ISPy](https://github.com/ISP-SST/ISPy)).
|
|
256
|
+
- CHASE/HIS: Li et al. 2022, *Science China* — the CHASE mission.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT © 2025-2026 Waleed Ajmal. Ported `satprocess` components are BSD-3-Clause © 2025
|
|
261
|
+
Finlay Davis — see [`NOTICE`](NOTICE).
|
chasepy-2.0.0/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# chase-pipeline
|
|
2
|
+
|
|
3
|
+
**A modular calibration and flare-analysis pipeline for CHASE / HIS solar spectroscopy — with a TUI, a Python SDK, and a one-file config workflow.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Majboor/chase-pipeline/actions/workflows/test.yml)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
CHASE (the **Chinese Hα Solar Explorer**) scans the full solar disk in Hα (118
|
|
10
|
+
wavelengths, 6559.4–6565.1 Å) and Fe I (46 wavelengths, ~6569 Å). The raw data is
|
|
11
|
+
scientifically rich but awkward to use — every frame drifts spatially *and*
|
|
12
|
+
spectrally, and turning a stack of `RSM…_HA.fits` cubes into a clean, aligned,
|
|
13
|
+
calibrated data product takes a lot of careful work. `chase-pipeline` does that
|
|
14
|
+
work for you, and lets you do as much or as little of it as you want.
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/flare.gif" width="760" alt="Stabilised 2-panel animation of the 2023-03-29 X2.1 flare"><br>
|
|
18
|
+
<em>The 2023-03-29 <strong>X2.1</strong> flare, stabilised by this pipeline:
|
|
19
|
+
photosphere (Hα continuum) | chromosphere (Hα core). 25 frames, per-frame colour scaling.<br>
|
|
20
|
+
Rendered with <code>--flow-method farneback</code>: TV-L1 optical flow blocks at the flare peak
|
|
21
|
+
(its total-variation regularizer turns the brightening into a rigid motion block); Farneback stays
|
|
22
|
+
clean. See <a href="docs/FLARE_STABILIZATION.md">docs/FLARE_STABILIZATION.md</a>.</em>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why this exists (statement of need)
|
|
28
|
+
|
|
29
|
+
CHASE data is under-used because it is hard to use: the portal ships full-disk
|
|
30
|
+
cubes with no co-alignment, a per-frame wavelength zero-point that drifts by ~0.8
|
|
31
|
+
channels, and no turnkey way to extract a science-ready region. Existing scripts
|
|
32
|
+
(including the original `chase-pipeline` and Finlay Davis's `satprocess`) each
|
|
33
|
+
solved part of the problem. **This package unifies them into one installable
|
|
34
|
+
tool** that:
|
|
35
|
+
|
|
36
|
+
- **just works for the lazy user** — point at a folder, set a field of view and a
|
|
37
|
+
few true/false flags in a text file (or use the TUI), run once, get an aligned
|
|
38
|
+
**FITS/npz cube + images/GIF** out;
|
|
39
|
+
- **stays hackable for the power user** — every stage is an importable function
|
|
40
|
+
(`import chase`), nothing is a forced monolithic pipeline;
|
|
41
|
+
- **preserves the physics** — shift-then-crop tracking, per-frame wavelength
|
|
42
|
+
resampling, flare-free alignment, absolute atlas calibration, and the correct
|
|
43
|
+
temperature inversions per line (it does not "simplify away" the hard-won fixes).
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
From PyPI (the package is named `chasepy`; it installs the `chase` module and
|
|
48
|
+
the `chase` / `chase-tui` commands):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install chasepy # core
|
|
52
|
+
pip install 'chasepy[tui]' # + Textual TUI (chase-tui)
|
|
53
|
+
pip install 'chasepy[atlas]' # + ISPy for absolute Fe I temperature calibration
|
|
54
|
+
pip install 'chasepy[all]' # everything, incl. test deps
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or for development, clone and install editable:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/Majboor/chase-pipeline.git
|
|
61
|
+
cd chase-pipeline
|
|
62
|
+
pip install -e '.[all]'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Three ways to use it
|
|
66
|
+
|
|
67
|
+
### 1. TUI — interactive, no flags to memorise
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
chase-tui
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**End-to-end walkthrough** — the real TUI configured on the 2023-03-29 X2.1
|
|
74
|
+
flare (data source → field of view → calibration → temperature → a before/after
|
|
75
|
+
of the stabilisation → the actual `flare.gif` / `temperature.gif` /
|
|
76
|
+
`contrast_profile.gif` output):
|
|
77
|
+
|
|
78
|
+
<p align="center">
|
|
79
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/tui_e2e_walkthrough.gif" width="800" alt="End-to-end chase-tui walkthrough">
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
> Prefer the full-resolution, scrubbable version?
|
|
83
|
+
> [assets/tui_e2e_walkthrough.mp4](https://github.com/Majboor/chase-pipeline/raw/feat/unified-pipeline-tui-sdk/assets/tui_e2e_walkthrough.mp4).
|
|
84
|
+
|
|
85
|
+
Pick a data source, enter a full FOV or an arbitrary patch (and a separate, smaller
|
|
86
|
+
*alignment* patch if you want), tick the calibrations you want, and hit **Run** —
|
|
87
|
+
live progress streams into the log pane. The TUI is a thin front-end over the SDK;
|
|
88
|
+
it duplicates no logic.
|
|
89
|
+
|
|
90
|
+
**Before / after** — what the spatial calibration buys you. Left: a raw fixed
|
|
91
|
+
crop of the portal data, drifting frame to frame. Right: the same patch after
|
|
92
|
+
shift-then-crop tracking + optical-flow stabilisation (Hα core):
|
|
93
|
+
|
|
94
|
+
<p align="center">
|
|
95
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/before_after.gif" width="760" alt="Raw fixed crop vs tracked + optical-flow stabilised, Hα core">
|
|
96
|
+
</p>
|
|
97
|
+
|
|
98
|
+
### 2. Config file — the "lazy" one-shot workflow
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
chase --config examples/config.toml
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```toml
|
|
105
|
+
# examples/config.toml
|
|
106
|
+
fits_dir = "/data/20230329_X12/fits"
|
|
107
|
+
out_dir = "./chase_out"
|
|
108
|
+
patch = [940, 1080, 1870, 2040] # [y0, y1, x0, x1]; omit + full_fov=true for whole disk
|
|
109
|
+
contrast = true
|
|
110
|
+
temperature = "double" # halpha + fe_planck
|
|
111
|
+
gif = true
|
|
112
|
+
freeze_clim = true
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 3. CLI — explicit flags (backward compatible)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
chase /data/20230329_X12/fits \
|
|
119
|
+
--patch 940 1080 1870 2040 \
|
|
120
|
+
--align-patch 980 1040 1920 1990 \
|
|
121
|
+
--contrast --temperature double --freeze-clim
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Every run checkpoints the aligned cubes to `out_dir/aligned_data.npz`, so you
|
|
125
|
+
never have to redo the slow load + align stages just to tweak an output —
|
|
126
|
+
rerun a single step against the checkpoint with `--only` (implies `--resume`):
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
chase /data/20230329_X12/fits --out ./chase_out --only gif # just re-render the animation
|
|
130
|
+
chase /data/20230329_X12/fits --out ./chase_out --only contrast,gif --no-drift
|
|
131
|
+
chase /data/20230329_X12/fits --out ./chase_out --only temperature --temperature fe_voigt
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Steps: `gif`, `contrast`, `temperature`, `npz`, `fits`, `diagnostics`. Plain
|
|
135
|
+
`--resume` keeps your normal toggles and only skips load + align. (Changing the
|
|
136
|
+
patch or alignment options needs a fresh run *without* `--resume` — the
|
|
137
|
+
checkpoint stores the cubes as they were cropped and aligned.)
|
|
138
|
+
|
|
139
|
+
### …and the SDK — call any single stage
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import chase
|
|
143
|
+
|
|
144
|
+
# Load + crop-track a sequence (shift-then-crop, per-frame wavelength resample)
|
|
145
|
+
seq = chase.load_flare_sequence("/data/20230329_X12/fits",
|
|
146
|
+
patch=[940, 1080, 1870, 2040])
|
|
147
|
+
|
|
148
|
+
# Fine-stabilise with optical flow on the flare-free channel; HA stays float32
|
|
149
|
+
aligned = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
|
|
150
|
+
|
|
151
|
+
# Science products
|
|
152
|
+
contrast, _ = chase.contrast_profile(aligned, background=seq["ha_bg"])
|
|
153
|
+
ha_T, _ = chase.halpha_width_temperature(aligned[24], seq["wavelength_ha"]) # Molnar 2019
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Or run the whole thing in one call:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from chase import Config, run_pipeline
|
|
160
|
+
run_pipeline(Config(fits_dir="/data/.../fits", patch=[940,1080,1870,2040],
|
|
161
|
+
contrast=True, temperature="double"))
|
|
162
|
+
|
|
163
|
+
# Later: rerun only the outputs you care about, from the saved checkpoint
|
|
164
|
+
run_pipeline(Config(fits_dir="/data/.../fits", out_dir="./chase_out",
|
|
165
|
+
resume=True, gif=True, temperature="fe_voigt"))
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## What each subpackage does (one figure each)
|
|
169
|
+
|
|
170
|
+
| Subpackage | What it does | |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `chase.io` | Resumable download, folder / list-file / URL discovery, cube + sequence loading | <img src="assets/diag_patch.png" width="220"> |
|
|
173
|
+
| `chase.calib.spatial` / `.align` | Shift-then-crop tracking + optical-flow stabilisation | <img src="assets/diag_optical_flow.png" width="220"> |
|
|
174
|
+
| `chase.calib.wavelength` | Per-frame resampling onto a common grid; spectral-drift xcorr | <img src="assets/diag_qs_spectrum.png" width="220"> |
|
|
175
|
+
| `chase.calib.intensity` | QS norm, integral scaling, FTS-atlas absolute calibration (ISPy) | <img src="assets/atlas_calibration.png" width="220"> |
|
|
176
|
+
| `chase.analysis.contrast` | `(flare−bg)/bg − frame0` wavelength-vs-time profile | <img src="assets/contrast_profile.png" width="220"> |
|
|
177
|
+
| `chase.analysis.temperature` | Hα width→T (Molnar) + Fe I →T (Planck/EB/Voigt) | <img src="assets/double_temp_map_peak.png" width="220"> |
|
|
178
|
+
|
|
179
|
+
<p align="center">
|
|
180
|
+
<img src="https://raw.githubusercontent.com/Majboor/chase-pipeline/feat/unified-pipeline-tui-sdk/assets/temperature.gif" width="720" alt="Double temperature map animation"><br>
|
|
181
|
+
<em>Double temperature map: chromosphere from Hα width (~10⁴ K) and photosphere
|
|
182
|
+
from Fe I Planck inversion (~5,100 K, absolute-calibrated against the FTS atlas).</em>
|
|
183
|
+
</p>
|
|
184
|
+
|
|
185
|
+
## Documentation
|
|
186
|
+
|
|
187
|
+
- **[docs/DATA_ACQUISITION.md](docs/DATA_ACQUISITION.md)** — get from the CHASE portal to a processed cube, end to end.
|
|
188
|
+
- **[docs/SDK.md](docs/SDK.md)** — the Python API, every function + parameters.
|
|
189
|
+
- **[docs/TUI.md](docs/TUI.md)** — TUI walkthrough.
|
|
190
|
+
- **[docs/CALIBRATION.md](docs/CALIBRATION.md)** — the science: what each calibration does and why.
|
|
191
|
+
- **[docs/CONTRIBUTING.md](docs/CONTRIBUTING.md)** — dev setup, tests, style.
|
|
192
|
+
|
|
193
|
+
## Tests
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
pip install -e '.[dev]' && pytest -q # 22 fast tests on synthetic FITS fixtures
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Acknowledgements
|
|
200
|
+
|
|
201
|
+
Built with the guidance and corrections of **Dr. Alexander Pietrow** (AIP) and
|
|
202
|
+
**Dr. Malcolm Druett**. The Hough-circle limb centring, spectral
|
|
203
|
+
cross-correlation, integral intensity scaling, and CSV shift-cache are adapted
|
|
204
|
+
from **Finlay Davis**'s [`satprocess`](https://github.com/FinlayDavis/satprocess)
|
|
205
|
+
(BSD-3-Clause; see [`NOTICE`](NOTICE)). Affiliation support from **Prof. Dr. Sayed
|
|
206
|
+
Amer Mahmood** (University of the Punjab). Data courtesy of the **CHASE/HIS**
|
|
207
|
+
mission and the [Solar Science Data Center, Nanjing University](https://ssdc.nju.edu.cn/NdchaseSatellite).
|
|
208
|
+
|
|
209
|
+
## Citing
|
|
210
|
+
|
|
211
|
+
If you use this pipeline, please cite the methods it builds on:
|
|
212
|
+
|
|
213
|
+
- Molnar et al. 2019, *ApJ* **881**, 99 — Hα width → temperature ([10.3847/1538-4357/ab2ba3](https://doi.org/10.3847/1538-4357/ab2ba3)).
|
|
214
|
+
- The Eddington–Barbier photospheric inversion (A&A 2013, aa21259-13).
|
|
215
|
+
- The **ISPy** FTS solar-atlas calibration ([ISP-SST/ISPy](https://github.com/ISP-SST/ISPy)).
|
|
216
|
+
- CHASE/HIS: Li et al. 2022, *Science China* — the CHASE mission.
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT © 2025-2026 Waleed Ajmal. Ported `satprocess` components are BSD-3-Clause © 2025
|
|
221
|
+
Finlay Davis — see [`NOTICE`](NOTICE).
|
|
@@ -0,0 +1,104 @@
|
|
|
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.0.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
|
+
atlas_calibrate,
|
|
47
|
+
correct_spectral_drift,
|
|
48
|
+
create_optical_flow_solver,
|
|
49
|
+
find_disk_center,
|
|
50
|
+
hough_disk_center,
|
|
51
|
+
integral_scaling,
|
|
52
|
+
normalize_intensity,
|
|
53
|
+
optical_flow_align,
|
|
54
|
+
phase_shift,
|
|
55
|
+
planck_inversion,
|
|
56
|
+
recenter,
|
|
57
|
+
resample_wavelength,
|
|
58
|
+
track_and_crop,
|
|
59
|
+
warp_cube,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# --- Analysis --------------------------------------------------------------
|
|
63
|
+
from .analysis import (
|
|
64
|
+
contrast_profile,
|
|
65
|
+
fe_eb_temperature,
|
|
66
|
+
fe_planck_temperature,
|
|
67
|
+
fe_voigt_temperature,
|
|
68
|
+
halpha_width_map,
|
|
69
|
+
halpha_width_temperature,
|
|
70
|
+
width_to_temperature,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# --- Visualisation ---------------------------------------------------------
|
|
74
|
+
from .viz import (
|
|
75
|
+
make_animation,
|
|
76
|
+
make_contrast_animation,
|
|
77
|
+
make_temperature_animation,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# --- Orchestration ---------------------------------------------------------
|
|
81
|
+
from .config import Config, load_config
|
|
82
|
+
from .pipeline import run_pipeline
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
"__version__",
|
|
86
|
+
# io
|
|
87
|
+
"download", "download_file", "resolve_inputs", "discover_fits",
|
|
88
|
+
"load_cube", "load_fits_data", "load_flare_sequence", "extract_disk_center",
|
|
89
|
+
"save_cube_fits", "save_npz",
|
|
90
|
+
# calib
|
|
91
|
+
"find_disk_center", "hough_disk_center", "recenter", "phase_shift",
|
|
92
|
+
"track_and_crop", "optical_flow_align", "warp_cube",
|
|
93
|
+
"create_optical_flow_solver", "resample_wavelength", "correct_spectral_drift",
|
|
94
|
+
"align_spectrum", "normalize_intensity", "integral_scaling",
|
|
95
|
+
"atlas_calibrate", "planck_inversion",
|
|
96
|
+
# analysis
|
|
97
|
+
"contrast_profile", "halpha_width_map", "halpha_width_temperature",
|
|
98
|
+
"width_to_temperature", "fe_planck_temperature", "fe_eb_temperature",
|
|
99
|
+
"fe_voigt_temperature",
|
|
100
|
+
# viz
|
|
101
|
+
"make_animation", "make_contrast_animation", "make_temperature_animation",
|
|
102
|
+
# orchestration
|
|
103
|
+
"Config", "load_config", "run_pipeline",
|
|
104
|
+
]
|