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 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
+ [![codecov](https://codecov.io/gh/Lauren4476/sting/graph/badge.svg?token=QABJ72IB52)](https://codecov.io/gh/Lauren4476/sting)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Lauren4476/sting/blob/main/LICENCE)
42
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://github.com/Lauren4476/sting)
43
+ [![Python 3.13](https://img.shields.io/badge/python-3.13-blue.svg)](https://github.com/Lauren4476/sting)
44
+ [![Python 3.14](https://img.shields.io/badge/python-3.14-blue.svg)](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
+ [![codecov](https://codecov.io/gh/Lauren4476/sting/graph/badge.svg?token=QABJ72IB52)](https://codecov.io/gh/Lauren4476/sting)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Lauren4476/sting/blob/main/LICENCE)
7
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://github.com/Lauren4476/sting)
8
+ [![Python 3.13](https://img.shields.io/badge/python-3.13-blue.svg)](https://github.com/Lauren4476/sting)
9
+ [![Python 3.14](https://img.shields.io/badge/python-3.14-blue.svg)](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).