sting 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sting-0.2.0/.DS_Store +0 -0
- sting-0.2.0/.coverage +0 -0
- sting-0.2.0/.github/workflows/test-package.yml +38 -0
- sting-0.2.0/.gitignore +48 -0
- sting-0.2.0/LICENCE +21 -0
- sting-0.2.0/PKG-INFO +251 -0
- sting-0.2.0/README.md +216 -0
- sting-0.2.0/coverage.xml +2253 -0
- sting-0.2.0/examples/data/example_streamer_cluster_data.fits +0 -0
- sting-0.2.0/examples/sting_example_run.ipynb +790 -0
- sting-0.2.0/pyproject.toml +69 -0
- sting-0.2.0/requirements.txt +9 -0
- sting-0.2.0/setup.cfg +4 -0
- sting-0.2.0/src/sting/__init__.py +8 -0
- sting-0.2.0/src/sting/_version.py +24 -0
- sting-0.2.0/src/sting/errors.py +677 -0
- sting-0.2.0/src/sting/extract_streamline.py +425 -0
- sting-0.2.0/src/sting/gradient_descent.py +1776 -0
- sting-0.2.0/src/sting/outputs.py +1705 -0
- sting-0.2.0/src/sting/stream_lines_grad.py +448 -0
- sting-0.2.0/src/sting.egg-info/PKG-INFO +251 -0
- sting-0.2.0/src/sting.egg-info/SOURCES.txt +30 -0
- sting-0.2.0/src/sting.egg-info/dependency_links.txt +1 -0
- sting-0.2.0/src/sting.egg-info/requires.txt +11 -0
- sting-0.2.0/src/sting.egg-info/scm_file_list.json +26 -0
- sting-0.2.0/src/sting.egg-info/scm_version.json +8 -0
- sting-0.2.0/src/sting.egg-info/top_level.txt +1 -0
- sting-0.2.0/tests/test_errors.py +815 -0
- sting-0.2.0/tests/test_extract_streamline.py +693 -0
- sting-0.2.0/tests/test_gradient_descent.py +1544 -0
- sting-0.2.0/tests/test_outputs.py +1054 -0
- sting-0.2.0/tests/test_stream_lines_grad.py +553 -0
sting-0.2.0/.DS_Store
ADDED
|
Binary file
|
sting-0.2.0/.coverage
ADDED
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Run tests and upload coverage
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
name: Run tests and collect coverage
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 2
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install .[dev]
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: pytest
|
|
32
|
+
|
|
33
|
+
- name: Upload results to Codecov
|
|
34
|
+
uses: codecov/codecov-action@v5
|
|
35
|
+
with:
|
|
36
|
+
files: ./coverage.xml
|
|
37
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
38
|
+
fail_ci_if_error: true
|
sting-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
_version.py
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
.Python
|
|
10
|
+
build/
|
|
11
|
+
develop-eggs/
|
|
12
|
+
dist/
|
|
13
|
+
downloads/
|
|
14
|
+
eggs/
|
|
15
|
+
.eggs/
|
|
16
|
+
lib/
|
|
17
|
+
lib64/
|
|
18
|
+
parts/
|
|
19
|
+
sdist/
|
|
20
|
+
var/
|
|
21
|
+
wheels/
|
|
22
|
+
share/python-wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# Environments
|
|
29
|
+
.env
|
|
30
|
+
.venv
|
|
31
|
+
env/
|
|
32
|
+
venv/
|
|
33
|
+
ENV/
|
|
34
|
+
env.bak/
|
|
35
|
+
venv.bak/
|
|
36
|
+
|
|
37
|
+
# IDE
|
|
38
|
+
.idea
|
|
39
|
+
.backup
|
|
40
|
+
.vscode
|
|
41
|
+
|
|
42
|
+
# MacOSX
|
|
43
|
+
**/.DS_Store
|
|
44
|
+
.DS_Store
|
|
45
|
+
pytest.xml
|
|
46
|
+
|
|
47
|
+
# results folder
|
|
48
|
+
examples/sting_results/
|
sting-0.2.0/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lauren Mason
|
|
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.
|
sting-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sting
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: STreamer INfall with Gradients
|
|
5
|
+
Author-email: Lauren Mason <lmason@mpe.mpg.de>
|
|
6
|
+
Maintainer-email: Lauren Mason <lmason@mpe.mpg.de>
|
|
7
|
+
License: MIT Licence
|
|
8
|
+
Project-URL: Homepage, https://github.com/Lauren4476/STING
|
|
9
|
+
Project-URL: Issues, https://github.com/Lauren4476/STING/issues
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENCE
|
|
24
|
+
Requires-Dist: numpy>=2.0.2
|
|
25
|
+
Requires-Dist: astropy>=7.0.0
|
|
26
|
+
Requires-Dist: jax>=0.9.0
|
|
27
|
+
Requires-Dist: optax>=0.2.6
|
|
28
|
+
Requires-Dist: matplotlib>=3.9.0
|
|
29
|
+
Requires-Dist: pandas>=2.2.0
|
|
30
|
+
Requires-Dist: spectral_cube>=0.6.6
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# STING
|
|
37
|
+
|
|
38
|
+
## **ST**reamer **IN**fall with **G**radients
|
|
39
|
+
|
|
40
|
+
[](https://codecov.io/gh/Lauren4476/sting)
|
|
41
|
+
[](https://github.com/Lauren4476/sting/blob/main/LICENCE)
|
|
42
|
+
[](https://github.com/Lauren4476/sting)
|
|
43
|
+
[](https://github.com/Lauren4476/sting)
|
|
44
|
+
[](https://github.com/Lauren4476/sting)
|
|
45
|
+
|
|
46
|
+
STING (STreamer INfall with Gradients) is a Python package for quickly fitting streamline models to molecular line position-position-velocity (PPV) data of asymmetric infalling material around young protostars. It uses a gradient descent method, powered by [JAX](https://github.com/jax-ml/jax) and [Optax](https://github.com/google-deepmind/optax), and can find best-fit streamline parameters of a streamer, and their uncertainties, in 10-20 seconds on a normal astronomy laptop (e.g. MacBook).
|
|
47
|
+
|
|
48
|
+
The streamline model used is the analytic solutions of [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x).
|
|
49
|
+
|
|
50
|
+
## What does STING do?
|
|
51
|
+
|
|
52
|
+
With STING you can:
|
|
53
|
+
|
|
54
|
+
1. **Extract a 1D streamline from a spectral cube.** Given a PPV cube containing candidate streamer emission, and the coordinates of its protostar, STING reduces the PPV cube to a set of `npoints` representative (RA offset, Dec offset, velocity) points with corresponding uncertainties
|
|
55
|
+
2. **Model a streamline.** The streamline model used by STING is taken from [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x) — a ballistic model of rotating infall, dominated by the gravitational force of a sink mass at the star position.
|
|
56
|
+
3. **Quickly fit the model to your data.** Streamline parameters are optimised with the Adam optimiser to minimise a chi-squared loss between the model and the observed streamline. Current parameters supported for optimisation: `r0`, `theta0`, `phi0`, `v_r0`, `omega` or `rc`, `mass`, `inc`, `pa`. You can optimise any combination of these.
|
|
57
|
+
4. **Quantify uncertainties.** Parameter uncertainties are estimated from the Hessian of the loss at the best-fit point, and propagated into the model (e.g. as "streamline spaghetti" plots).
|
|
58
|
+
5. **Visualise the result.** Pre-built plotting functions include: morphology, velocity vs. projected radius on sky plane, loss curves, parameter correlation and uncertainty sampling, and per-epoch animations of the fit converging.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
It is recommended to install STING in a virtual environment:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python -m venv /path/to/new/venv
|
|
67
|
+
source /path/to/new/venv/bin/activate
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### pip (recommended)
|
|
71
|
+
|
|
72
|
+
The quickest way to install STING is
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install sting
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
which also installs all of STING's dependencies automatically.
|
|
79
|
+
|
|
80
|
+
If you want to install with additional development dependencies (e.g. for running the test suite):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install sting[dev]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### from source
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/Lauren4476/sting.git
|
|
90
|
+
cd sting
|
|
91
|
+
pip install .
|
|
92
|
+
```
|
|
93
|
+
installs STING and all of its dependencies.
|
|
94
|
+
|
|
95
|
+
### Notes on dependencies
|
|
96
|
+
|
|
97
|
+
STING requires **Python ≥ Python 3.12**.
|
|
98
|
+
|
|
99
|
+
Dependencies (installed automatically when using either of the methods above):
|
|
100
|
+
- `numpy>=2.0.2`
|
|
101
|
+
- `astropy>=7.0.0`
|
|
102
|
+
- `jax>=0.9.0`
|
|
103
|
+
- `optax>=0.2.6`
|
|
104
|
+
- `matplotlib>=3.9.0`
|
|
105
|
+
- `pandas>=2.2.0`
|
|
106
|
+
- `spectral_cube>=0.6.6`
|
|
107
|
+
|
|
108
|
+
## Quick start
|
|
109
|
+
|
|
110
|
+
A full worked example with data is provided in [`examples/sting_example_run.ipynb`](https://github.com/Lauren4476/sting/tree/main/examples). The core workflow is:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# --- Imports ---
|
|
114
|
+
from astropy import units as u
|
|
115
|
+
from astropy.io import fits
|
|
116
|
+
from astropy.coordinates import SkyCoord
|
|
117
|
+
from spectral_cube import SpectralCube
|
|
118
|
+
from sting import extract_streamline, gradient_descent, outputs
|
|
119
|
+
|
|
120
|
+
# --- Settings ---
|
|
121
|
+
star_position = SkyCoord("3h28m55.569s", "+31d14m37.025s", frame='fk5')
|
|
122
|
+
distance = 293 # distance to protostar star in parsecs
|
|
123
|
+
v_lsr = 7.5 # km/s, systemic velocity
|
|
124
|
+
|
|
125
|
+
# --- 1. Extract 1D streamline from the cube ---
|
|
126
|
+
hdu = fits.open("data/example_streamer_cluster_data.fits")[0]
|
|
127
|
+
cube = SpectralCube.read(hdu).with_spectral_unit(
|
|
128
|
+
u.km / u.s, rest_value=hdu.header["RESTFRQ"] * u.Hz
|
|
129
|
+
)
|
|
130
|
+
streamer_cube = extract_streamline.extract_streamer_subcube(
|
|
131
|
+
cube,
|
|
132
|
+
vmin=6 * u.km / u.s, vmax=8 * u.km / u.s,
|
|
133
|
+
xmin=-5 * u.arcsec, xmax=5 * u.arcsec,
|
|
134
|
+
ymin=-12 * u.arcsec, ymax=0.5 * u.arcsec,
|
|
135
|
+
rms_thresh=4,
|
|
136
|
+
)
|
|
137
|
+
streamer = extract_streamline.reduce_to_1D(streamer_cube, star_position, n_elements=10)
|
|
138
|
+
|
|
139
|
+
# --- 2. Set an initial guess ---
|
|
140
|
+
# parameters you want STING to optimise
|
|
141
|
+
initial_opt_params = {
|
|
142
|
+
'r0': 1500.0 * u.au,
|
|
143
|
+
'theta0': 40.0 * u.deg,
|
|
144
|
+
'phi0': 100.0 * u.deg,
|
|
145
|
+
'omega': 5e-13 * (1 / u.s),
|
|
146
|
+
'v_r0': 0.1 * u.km / u.s,
|
|
147
|
+
'mass': 4.0 * u.Msun,
|
|
148
|
+
'inc': -45 * u.deg,
|
|
149
|
+
'pa': 194 * u.deg
|
|
150
|
+
}
|
|
151
|
+
# parameters you want to keep fixed
|
|
152
|
+
fixed_params = {
|
|
153
|
+
'rmin': 50.0 * u.au,
|
|
154
|
+
'deltar': 30.0 * u.au,
|
|
155
|
+
'v_lsr': v_lsr * u.km / u.s,
|
|
156
|
+
}
|
|
157
|
+
model_params, initial_opt_params, fixed_params = gradient_descent.prepare_model_params(
|
|
158
|
+
initial_opt_params, fixed_params
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# --- 3. Set bounds for 'r0', 'mass', if any of these are in initial_opt_params ---
|
|
162
|
+
param_bounds = {
|
|
163
|
+
'r0': (200.0, 10000.0) * u.au,
|
|
164
|
+
'mass': (3.0, 5.0) * u.Msun,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# --- 4. (optional) It is recommended to set priors for 'mass', 'inc', 'pa' if any of these are in initial_opt_params, to help break degeneracy ---
|
|
168
|
+
# format 'parameter': (mean, sigma) * u.Unit
|
|
169
|
+
priors = {
|
|
170
|
+
'mass': (4.0, 1.0) * u.Msun,
|
|
171
|
+
'inc': (-45, 10) * u.deg,
|
|
172
|
+
'pa': (194, 10) * u.deg
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# --- 5. Fit the streamline ---
|
|
176
|
+
best_opt_params, loss_history, param_errors = gradient_descent.fit_streamline(
|
|
177
|
+
initial_opt_params,
|
|
178
|
+
fixed_params,
|
|
179
|
+
streamer,
|
|
180
|
+
distance,
|
|
181
|
+
param_bounds=param_bounds,
|
|
182
|
+
priors=priors
|
|
183
|
+
n_epochs=1000,
|
|
184
|
+
v_lsr=v_lsr,
|
|
185
|
+
save_folder="sting_results",
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
This saves best-fit parameters, an optimisation log, and diagnostic plots to `sting_results/`. From there, the `outputs` module offers further plotting and analaysis tools.
|
|
190
|
+
|
|
191
|
+
## Recommended use case (best practice)
|
|
192
|
+
|
|
193
|
+
STING is intended for fitting kinematic streamer models to interferometric molecular line observations (e.g. ALMA, NOEMA) of young stellar objects with asymmetric infall candidates visible in PPV space. It is best suited to:
|
|
194
|
+
|
|
195
|
+
- High spectral and spatial resolution observations (~0.1 km/s, ~300 au resolution or better)
|
|
196
|
+
- Sources with a well-constrained distance and systemic velocity
|
|
197
|
+
- Sources with some prior constraints on sink mass, inclination, and position axis.
|
|
198
|
+
- Streamer candidates that have already been isolated from the rest of the cube by preprocessing.
|
|
199
|
+
|
|
200
|
+
### Priors
|
|
201
|
+
|
|
202
|
+
It is possible to run STING without specifying a `priors` argument. However, PPV observations only contain data in 3 dimensions (RA, Dec, LOS velocity), while the streamline model has 6 dimensions (3D position, 3D velocity). As a result, some parameter combinations are "coupled" and may be only weakly constrained by the observations.
|
|
203
|
+
|
|
204
|
+
Common degeneracies:
|
|
205
|
+
- sink mass, `mass`, and angular velocity, `omega` or centrifugal radius, `rc`
|
|
206
|
+
- source inclination, `inc`, and streamline initial polar angle, `theta0`
|
|
207
|
+
- source position angle, `pa`, and streamline starting azimuthal angle, `phi0`
|
|
208
|
+
|
|
209
|
+
As a result, when both parameters in one of these pairs are allowed to vary optimisation may converge more slowly as the optimiser drifts along a nearly-flat direction, and **uncertainty estimates will become large**.
|
|
210
|
+
|
|
211
|
+
STING supports Gaussian priors on any optimisable parameter, specified as (mean, sigma). These are included in the loss function as additional chi-squared penalty terms, penalising the optimiser for adjusting parameters further from the prior mean. Where independent constraints are available from previous work, **I recommend applying priors to `mass`, `inc`, and `pa`**. This helps constrain otherwise degenerate parameter combinations and reduces uncertainties.
|
|
212
|
+
|
|
213
|
+
### Streamer preprocessing
|
|
214
|
+
|
|
215
|
+
STING's `extract_streamline` module assumes you hand it a cube that already contains mostly streamer emission — it only applies simple velocity/spatial cuts and an RMS threshold (in `extract_streamer_subcube`). **It cannot yet** separate streamer emission from overlapping envelope, outflow, or disk emission for you. (stay tuned)
|
|
216
|
+
|
|
217
|
+
The preprocessing steps I use and recommend to isolate a **pure streamer cube** for input to STING are:
|
|
218
|
+
|
|
219
|
+
- **Multi-Gaussian spectral fitting** for the spectrum at each pixel in the original PPV cube to separate kinematically distinct components (e.g. streamer vs. envelope vs. disc) that overlap in velocity. Tools like [`pyspeckit`](https://pyspeckit.readthedocs.io/) or [`scousepy`](https://github.com/jdhenshaw/scousepy) work well for this. I use the [AIC criterion](https://en.wikipedia.org/wiki/Akaike_information_criterion) to determine which number of Gaussians best fit each spectrum.
|
|
220
|
+
- **DBSCAN clustering** (e.g. with [`scikit-learn`](https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html)) on the resulting collection of gaussians, to extract the spatially and kinematically coherent cluster of gaussians that corresponds to the streamer candidate, discarding noise and unrelated structures.
|
|
221
|
+
|
|
222
|
+
## Source code overview
|
|
223
|
+
|
|
224
|
+
| Module | Contains |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `extract_streamline` | Extracts streamer sub-cubes and reduces them to a 1D weighted-mean streamline with uncertainties. |
|
|
227
|
+
| `stream_lines_grad` | JAX-differentiable forward model of the streamline, following [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x). |
|
|
228
|
+
| `gradient_descent` | Main body. Parameter preparation, loss function, and Adam-based `fit_streamline` optimisation loop. |
|
|
229
|
+
| `errors` | Streamline parameter uncertainty estimation at the best fit parameters. |
|
|
230
|
+
| `outputs` | Result-saving and plotting: morphology, position–velocity diagrams, loss curves, correlation/uncertainty plots, and per-epoch animations. |
|
|
231
|
+
|
|
232
|
+
## Contributing / Issues
|
|
233
|
+
|
|
234
|
+
Bug reports are welcome via the [GitHub issue tracker](https://github.com/Lauren4476/sting/issues). If you have any questions about STING or ideas for additions in future releasees, feel free to get in touch at [lmason@mpe.mpg.de](mailto:lmason@mpe.mpg.de)!
|
|
235
|
+
|
|
236
|
+
### Known issues
|
|
237
|
+
|
|
238
|
+
- **The radius sampling grid must reach down to `r_low`.** `r_low = max(rmin, 0.5*rc)`. For jit compilation (what makes STING so fast, see [this explanation](https://docs.jax.dev/en/latest/jit-compilation.html)), arrays must be constant length across optimisation epochs. If `npoints` and `deltar` are too small for the chosen `r0`, the sampled radius grid won't extend down to `r_low`, and the model will raise an error. If you encounter this, increase `npoints` and/or `deltar`.
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
## Citing STING
|
|
242
|
+
|
|
243
|
+
tbc
|
|
244
|
+
|
|
245
|
+
## Credits
|
|
246
|
+
|
|
247
|
+
STING is written and maintained by **Lauren Mason** ([lmason@mpe.mpg.de](mailto:lmason@mpe.mpg.de), [@Lauren4476](https://github.com/Lauren4476)).
|
|
248
|
+
|
|
249
|
+
## Licence
|
|
250
|
+
|
|
251
|
+
STING is released under the [MIT Licence](https://github.com/Lauren4476/sting/blob/main/LICENCE).
|
sting-0.2.0/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# STING
|
|
2
|
+
|
|
3
|
+
## **ST**reamer **IN**fall with **G**radients
|
|
4
|
+
|
|
5
|
+
[](https://codecov.io/gh/Lauren4476/sting)
|
|
6
|
+
[](https://github.com/Lauren4476/sting/blob/main/LICENCE)
|
|
7
|
+
[](https://github.com/Lauren4476/sting)
|
|
8
|
+
[](https://github.com/Lauren4476/sting)
|
|
9
|
+
[](https://github.com/Lauren4476/sting)
|
|
10
|
+
|
|
11
|
+
STING (STreamer INfall with Gradients) is a Python package for quickly fitting streamline models to molecular line position-position-velocity (PPV) data of asymmetric infalling material around young protostars. It uses a gradient descent method, powered by [JAX](https://github.com/jax-ml/jax) and [Optax](https://github.com/google-deepmind/optax), and can find best-fit streamline parameters of a streamer, and their uncertainties, in 10-20 seconds on a normal astronomy laptop (e.g. MacBook).
|
|
12
|
+
|
|
13
|
+
The streamline model used is the analytic solutions of [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x).
|
|
14
|
+
|
|
15
|
+
## What does STING do?
|
|
16
|
+
|
|
17
|
+
With STING you can:
|
|
18
|
+
|
|
19
|
+
1. **Extract a 1D streamline from a spectral cube.** Given a PPV cube containing candidate streamer emission, and the coordinates of its protostar, STING reduces the PPV cube to a set of `npoints` representative (RA offset, Dec offset, velocity) points with corresponding uncertainties
|
|
20
|
+
2. **Model a streamline.** The streamline model used by STING is taken from [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x) — a ballistic model of rotating infall, dominated by the gravitational force of a sink mass at the star position.
|
|
21
|
+
3. **Quickly fit the model to your data.** Streamline parameters are optimised with the Adam optimiser to minimise a chi-squared loss between the model and the observed streamline. Current parameters supported for optimisation: `r0`, `theta0`, `phi0`, `v_r0`, `omega` or `rc`, `mass`, `inc`, `pa`. You can optimise any combination of these.
|
|
22
|
+
4. **Quantify uncertainties.** Parameter uncertainties are estimated from the Hessian of the loss at the best-fit point, and propagated into the model (e.g. as "streamline spaghetti" plots).
|
|
23
|
+
5. **Visualise the result.** Pre-built plotting functions include: morphology, velocity vs. projected radius on sky plane, loss curves, parameter correlation and uncertainty sampling, and per-epoch animations of the fit converging.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
It is recommended to install STING in a virtual environment:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python -m venv /path/to/new/venv
|
|
32
|
+
source /path/to/new/venv/bin/activate
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### pip (recommended)
|
|
36
|
+
|
|
37
|
+
The quickest way to install STING is
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install sting
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
which also installs all of STING's dependencies automatically.
|
|
44
|
+
|
|
45
|
+
If you want to install with additional development dependencies (e.g. for running the test suite):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install sting[dev]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### from source
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/Lauren4476/sting.git
|
|
55
|
+
cd sting
|
|
56
|
+
pip install .
|
|
57
|
+
```
|
|
58
|
+
installs STING and all of its dependencies.
|
|
59
|
+
|
|
60
|
+
### Notes on dependencies
|
|
61
|
+
|
|
62
|
+
STING requires **Python ≥ Python 3.12**.
|
|
63
|
+
|
|
64
|
+
Dependencies (installed automatically when using either of the methods above):
|
|
65
|
+
- `numpy>=2.0.2`
|
|
66
|
+
- `astropy>=7.0.0`
|
|
67
|
+
- `jax>=0.9.0`
|
|
68
|
+
- `optax>=0.2.6`
|
|
69
|
+
- `matplotlib>=3.9.0`
|
|
70
|
+
- `pandas>=2.2.0`
|
|
71
|
+
- `spectral_cube>=0.6.6`
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
A full worked example with data is provided in [`examples/sting_example_run.ipynb`](https://github.com/Lauren4476/sting/tree/main/examples). The core workflow is:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# --- Imports ---
|
|
79
|
+
from astropy import units as u
|
|
80
|
+
from astropy.io import fits
|
|
81
|
+
from astropy.coordinates import SkyCoord
|
|
82
|
+
from spectral_cube import SpectralCube
|
|
83
|
+
from sting import extract_streamline, gradient_descent, outputs
|
|
84
|
+
|
|
85
|
+
# --- Settings ---
|
|
86
|
+
star_position = SkyCoord("3h28m55.569s", "+31d14m37.025s", frame='fk5')
|
|
87
|
+
distance = 293 # distance to protostar star in parsecs
|
|
88
|
+
v_lsr = 7.5 # km/s, systemic velocity
|
|
89
|
+
|
|
90
|
+
# --- 1. Extract 1D streamline from the cube ---
|
|
91
|
+
hdu = fits.open("data/example_streamer_cluster_data.fits")[0]
|
|
92
|
+
cube = SpectralCube.read(hdu).with_spectral_unit(
|
|
93
|
+
u.km / u.s, rest_value=hdu.header["RESTFRQ"] * u.Hz
|
|
94
|
+
)
|
|
95
|
+
streamer_cube = extract_streamline.extract_streamer_subcube(
|
|
96
|
+
cube,
|
|
97
|
+
vmin=6 * u.km / u.s, vmax=8 * u.km / u.s,
|
|
98
|
+
xmin=-5 * u.arcsec, xmax=5 * u.arcsec,
|
|
99
|
+
ymin=-12 * u.arcsec, ymax=0.5 * u.arcsec,
|
|
100
|
+
rms_thresh=4,
|
|
101
|
+
)
|
|
102
|
+
streamer = extract_streamline.reduce_to_1D(streamer_cube, star_position, n_elements=10)
|
|
103
|
+
|
|
104
|
+
# --- 2. Set an initial guess ---
|
|
105
|
+
# parameters you want STING to optimise
|
|
106
|
+
initial_opt_params = {
|
|
107
|
+
'r0': 1500.0 * u.au,
|
|
108
|
+
'theta0': 40.0 * u.deg,
|
|
109
|
+
'phi0': 100.0 * u.deg,
|
|
110
|
+
'omega': 5e-13 * (1 / u.s),
|
|
111
|
+
'v_r0': 0.1 * u.km / u.s,
|
|
112
|
+
'mass': 4.0 * u.Msun,
|
|
113
|
+
'inc': -45 * u.deg,
|
|
114
|
+
'pa': 194 * u.deg
|
|
115
|
+
}
|
|
116
|
+
# parameters you want to keep fixed
|
|
117
|
+
fixed_params = {
|
|
118
|
+
'rmin': 50.0 * u.au,
|
|
119
|
+
'deltar': 30.0 * u.au,
|
|
120
|
+
'v_lsr': v_lsr * u.km / u.s,
|
|
121
|
+
}
|
|
122
|
+
model_params, initial_opt_params, fixed_params = gradient_descent.prepare_model_params(
|
|
123
|
+
initial_opt_params, fixed_params
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# --- 3. Set bounds for 'r0', 'mass', if any of these are in initial_opt_params ---
|
|
127
|
+
param_bounds = {
|
|
128
|
+
'r0': (200.0, 10000.0) * u.au,
|
|
129
|
+
'mass': (3.0, 5.0) * u.Msun,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# --- 4. (optional) It is recommended to set priors for 'mass', 'inc', 'pa' if any of these are in initial_opt_params, to help break degeneracy ---
|
|
133
|
+
# format 'parameter': (mean, sigma) * u.Unit
|
|
134
|
+
priors = {
|
|
135
|
+
'mass': (4.0, 1.0) * u.Msun,
|
|
136
|
+
'inc': (-45, 10) * u.deg,
|
|
137
|
+
'pa': (194, 10) * u.deg
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# --- 5. Fit the streamline ---
|
|
141
|
+
best_opt_params, loss_history, param_errors = gradient_descent.fit_streamline(
|
|
142
|
+
initial_opt_params,
|
|
143
|
+
fixed_params,
|
|
144
|
+
streamer,
|
|
145
|
+
distance,
|
|
146
|
+
param_bounds=param_bounds,
|
|
147
|
+
priors=priors
|
|
148
|
+
n_epochs=1000,
|
|
149
|
+
v_lsr=v_lsr,
|
|
150
|
+
save_folder="sting_results",
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
This saves best-fit parameters, an optimisation log, and diagnostic plots to `sting_results/`. From there, the `outputs` module offers further plotting and analaysis tools.
|
|
155
|
+
|
|
156
|
+
## Recommended use case (best practice)
|
|
157
|
+
|
|
158
|
+
STING is intended for fitting kinematic streamer models to interferometric molecular line observations (e.g. ALMA, NOEMA) of young stellar objects with asymmetric infall candidates visible in PPV space. It is best suited to:
|
|
159
|
+
|
|
160
|
+
- High spectral and spatial resolution observations (~0.1 km/s, ~300 au resolution or better)
|
|
161
|
+
- Sources with a well-constrained distance and systemic velocity
|
|
162
|
+
- Sources with some prior constraints on sink mass, inclination, and position axis.
|
|
163
|
+
- Streamer candidates that have already been isolated from the rest of the cube by preprocessing.
|
|
164
|
+
|
|
165
|
+
### Priors
|
|
166
|
+
|
|
167
|
+
It is possible to run STING without specifying a `priors` argument. However, PPV observations only contain data in 3 dimensions (RA, Dec, LOS velocity), while the streamline model has 6 dimensions (3D position, 3D velocity). As a result, some parameter combinations are "coupled" and may be only weakly constrained by the observations.
|
|
168
|
+
|
|
169
|
+
Common degeneracies:
|
|
170
|
+
- sink mass, `mass`, and angular velocity, `omega` or centrifugal radius, `rc`
|
|
171
|
+
- source inclination, `inc`, and streamline initial polar angle, `theta0`
|
|
172
|
+
- source position angle, `pa`, and streamline starting azimuthal angle, `phi0`
|
|
173
|
+
|
|
174
|
+
As a result, when both parameters in one of these pairs are allowed to vary optimisation may converge more slowly as the optimiser drifts along a nearly-flat direction, and **uncertainty estimates will become large**.
|
|
175
|
+
|
|
176
|
+
STING supports Gaussian priors on any optimisable parameter, specified as (mean, sigma). These are included in the loss function as additional chi-squared penalty terms, penalising the optimiser for adjusting parameters further from the prior mean. Where independent constraints are available from previous work, **I recommend applying priors to `mass`, `inc`, and `pa`**. This helps constrain otherwise degenerate parameter combinations and reduces uncertainties.
|
|
177
|
+
|
|
178
|
+
### Streamer preprocessing
|
|
179
|
+
|
|
180
|
+
STING's `extract_streamline` module assumes you hand it a cube that already contains mostly streamer emission — it only applies simple velocity/spatial cuts and an RMS threshold (in `extract_streamer_subcube`). **It cannot yet** separate streamer emission from overlapping envelope, outflow, or disk emission for you. (stay tuned)
|
|
181
|
+
|
|
182
|
+
The preprocessing steps I use and recommend to isolate a **pure streamer cube** for input to STING are:
|
|
183
|
+
|
|
184
|
+
- **Multi-Gaussian spectral fitting** for the spectrum at each pixel in the original PPV cube to separate kinematically distinct components (e.g. streamer vs. envelope vs. disc) that overlap in velocity. Tools like [`pyspeckit`](https://pyspeckit.readthedocs.io/) or [`scousepy`](https://github.com/jdhenshaw/scousepy) work well for this. I use the [AIC criterion](https://en.wikipedia.org/wiki/Akaike_information_criterion) to determine which number of Gaussians best fit each spectrum.
|
|
185
|
+
- **DBSCAN clustering** (e.g. with [`scikit-learn`](https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html)) on the resulting collection of gaussians, to extract the spatially and kinematically coherent cluster of gaussians that corresponds to the streamer candidate, discarding noise and unrelated structures.
|
|
186
|
+
|
|
187
|
+
## Source code overview
|
|
188
|
+
|
|
189
|
+
| Module | Contains |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `extract_streamline` | Extracts streamer sub-cubes and reduces them to a 1D weighted-mean streamline with uncertainties. |
|
|
192
|
+
| `stream_lines_grad` | JAX-differentiable forward model of the streamline, following [Mendoza et al. (2009)](https://doi.org/10.1111/j.1365-2966.2008.14210.x). |
|
|
193
|
+
| `gradient_descent` | Main body. Parameter preparation, loss function, and Adam-based `fit_streamline` optimisation loop. |
|
|
194
|
+
| `errors` | Streamline parameter uncertainty estimation at the best fit parameters. |
|
|
195
|
+
| `outputs` | Result-saving and plotting: morphology, position–velocity diagrams, loss curves, correlation/uncertainty plots, and per-epoch animations. |
|
|
196
|
+
|
|
197
|
+
## Contributing / Issues
|
|
198
|
+
|
|
199
|
+
Bug reports are welcome via the [GitHub issue tracker](https://github.com/Lauren4476/sting/issues). If you have any questions about STING or ideas for additions in future releasees, feel free to get in touch at [lmason@mpe.mpg.de](mailto:lmason@mpe.mpg.de)!
|
|
200
|
+
|
|
201
|
+
### Known issues
|
|
202
|
+
|
|
203
|
+
- **The radius sampling grid must reach down to `r_low`.** `r_low = max(rmin, 0.5*rc)`. For jit compilation (what makes STING so fast, see [this explanation](https://docs.jax.dev/en/latest/jit-compilation.html)), arrays must be constant length across optimisation epochs. If `npoints` and `deltar` are too small for the chosen `r0`, the sampled radius grid won't extend down to `r_low`, and the model will raise an error. If you encounter this, increase `npoints` and/or `deltar`.
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## Citing STING
|
|
207
|
+
|
|
208
|
+
tbc
|
|
209
|
+
|
|
210
|
+
## Credits
|
|
211
|
+
|
|
212
|
+
STING is written and maintained by **Lauren Mason** ([lmason@mpe.mpg.de](mailto:lmason@mpe.mpg.de), [@Lauren4476](https://github.com/Lauren4476)).
|
|
213
|
+
|
|
214
|
+
## Licence
|
|
215
|
+
|
|
216
|
+
STING is released under the [MIT Licence](https://github.com/Lauren4476/sting/blob/main/LICENCE).
|