viper-pwn 0.3.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.
- viper_pwn-0.3.0/LICENSE +28 -0
- viper_pwn-0.3.0/PKG-INFO +254 -0
- viper_pwn-0.3.0/README.md +205 -0
- viper_pwn-0.3.0/pyproject.toml +66 -0
- viper_pwn-0.3.0/setup.cfg +4 -0
- viper_pwn-0.3.0/tests/test_escape.py +248 -0
- viper_pwn-0.3.0/tests/test_gammapy_wrapper.py +250 -0
- viper_pwn-0.3.0/tests/test_grid_regression.py +147 -0
- viper_pwn-0.3.0/tests/test_injection.py +306 -0
- viper_pwn-0.3.0/tests/test_invariants.py +212 -0
- viper_pwn-0.3.0/tests/test_kernel_equivalence.py +503 -0
- viper_pwn-0.3.0/tests/test_likelihood.py +425 -0
- viper_pwn-0.3.0/tests/test_multi_ic.py +154 -0
- viper_pwn-0.3.0/tests/test_nested.py +353 -0
- viper_pwn-0.3.0/tests/test_observables.py +75 -0
- viper_pwn-0.3.0/tests/test_plotting.py +234 -0
- viper_pwn-0.3.0/tests/test_sed_invariants.py +75 -0
- viper_pwn-0.3.0/tests/test_ssc.py +314 -0
- viper_pwn-0.3.0/tests/test_unit.py +1174 -0
- viper_pwn-0.3.0/viper/__init__.py +88 -0
- viper_pwn-0.3.0/viper/catalog.py +331 -0
- viper_pwn-0.3.0/viper/constants.py +155 -0
- viper_pwn-0.3.0/viper/data/cmb_pow_nu_ic_astropy.npz +0 -0
- viper_pwn-0.3.0/viper/data/cmb_pow_nu_ic_gsz2009.npz +0 -0
- viper_pwn-0.3.0/viper/data/psrcat.db +199688 -0
- viper_pwn-0.3.0/viper/data/psrcat_ref +7494 -0
- viper_pwn-0.3.0/viper/escape.py +126 -0
- viper_pwn-0.3.0/viper/evolution.py +1777 -0
- viper_pwn-0.3.0/viper/fitting.py +774 -0
- viper_pwn-0.3.0/viper/gammapy.py +487 -0
- viper_pwn-0.3.0/viper/grids.py +151 -0
- viper_pwn-0.3.0/viper/injection.py +230 -0
- viper_pwn-0.3.0/viper/legacy_params.py +16 -0
- viper_pwn-0.3.0/viper/model.py +658 -0
- viper_pwn-0.3.0/viper/nested.py +358 -0
- viper_pwn-0.3.0/viper/observables.py +522 -0
- viper_pwn-0.3.0/viper/parameter.py +417 -0
- viper_pwn-0.3.0/viper/particles.py +361 -0
- viper_pwn-0.3.0/viper/plotting/__init__.py +50 -0
- viper_pwn-0.3.0/viper/plotting/analysis.py +123 -0
- viper_pwn-0.3.0/viper/plotting/corner.py +195 -0
- viper_pwn-0.3.0/viper/plotting/data.py +251 -0
- viper_pwn-0.3.0/viper/plotting/electrons.py +436 -0
- viper_pwn-0.3.0/viper/plotting/movie.py +848 -0
- viper_pwn-0.3.0/viper/plotting/particle_sed.py +467 -0
- viper_pwn-0.3.0/viper/plotting/sed.py +476 -0
- viper_pwn-0.3.0/viper/plotting/style.py +155 -0
- viper_pwn-0.3.0/viper/pulsar.py +67 -0
- viper_pwn-0.3.0/viper/radiation.py +991 -0
- viper_pwn-0.3.0/viper/scalar_params.py +317 -0
- viper_pwn-0.3.0/viper/snr.py +295 -0
- viper_pwn-0.3.0/viper_pwn.egg-info/PKG-INFO +254 -0
- viper_pwn-0.3.0/viper_pwn.egg-info/SOURCES.txt +54 -0
- viper_pwn-0.3.0/viper_pwn.egg-info/dependency_links.txt +1 -0
- viper_pwn-0.3.0/viper_pwn.egg-info/requires.txt +36 -0
- viper_pwn-0.3.0/viper_pwn.egg-info/top_level.txt +1 -0
viper_pwn-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Jason Alford, Joseph Gelfand, and contributors
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
viper_pwn-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: viper-pwn
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: VIPER (Volume-Integrated PWN Evolution & Radiation)
|
|
5
|
+
Author: Jason Alford
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/j-alford/viper-pwn
|
|
8
|
+
Project-URL: Documentation, https://viper-pwn.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/j-alford/viper-pwn
|
|
10
|
+
Project-URL: Issues, https://github.com/j-alford/viper-pwn/issues
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.21
|
|
20
|
+
Requires-Dist: scipy>=1.7
|
|
21
|
+
Requires-Dist: astropy>=5.0
|
|
22
|
+
Provides-Extra: plot
|
|
23
|
+
Requires-Dist: matplotlib>=3.5; extra == "plot"
|
|
24
|
+
Provides-Extra: fast
|
|
25
|
+
Requires-Dist: numba>=0.57; extra == "fast"
|
|
26
|
+
Provides-Extra: fit
|
|
27
|
+
Requires-Dist: emcee>=3.0; extra == "fit"
|
|
28
|
+
Requires-Dist: corner; extra == "fit"
|
|
29
|
+
Provides-Extra: gammapy
|
|
30
|
+
Requires-Dist: gammapy>=2.0; extra == "gammapy"
|
|
31
|
+
Requires-Dist: regions<0.12; extra == "gammapy"
|
|
32
|
+
Provides-Extra: nested
|
|
33
|
+
Requires-Dist: ultranest>=4.0; extra == "nested"
|
|
34
|
+
Requires-Dist: scipy>=1.7; extra == "nested"
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: sphinx>=7; extra == "docs"
|
|
37
|
+
Requires-Dist: pydata-sphinx-theme; extra == "docs"
|
|
38
|
+
Requires-Dist: myst-nb; extra == "docs"
|
|
39
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: matplotlib>=3.5; extra == "all"
|
|
42
|
+
Requires-Dist: numba>=0.57; extra == "all"
|
|
43
|
+
Requires-Dist: gammapy>=2.0; extra == "all"
|
|
44
|
+
Requires-Dist: regions<0.12; extra == "all"
|
|
45
|
+
Requires-Dist: emcee>=3.0; extra == "all"
|
|
46
|
+
Requires-Dist: corner; extra == "all"
|
|
47
|
+
Requires-Dist: ultranest>=4.0; extra == "all"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# VIPER - Volume-Integrated PWN Evolution & Radiation
|
|
51
|
+
|
|
52
|
+
A Python implementation of the semi-analytic PWN model described in
|
|
53
|
+
**Gelfand, Slane & Zhang (2009), ApJ 703, 2051**.
|
|
54
|
+
|
|
55
|
+
This code evolves a pulsar wind nebula inside a supernova
|
|
56
|
+
remnant, tracking the PWN radius, magnetic field, particle energy
|
|
57
|
+
spectrum, and broadband photon emission from radio through gamma-rays.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
**Requirements**: Python >= 3.10, NumPy >= 1.21, SciPy >= 1.7, Astropy >= 5.0
|
|
62
|
+
|
|
63
|
+
**Optional**: Numba >= 0.57, emcee >= 3.0, corner, gammapy >= 2.0, matplotlib >= 3.5
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install viper-pwn
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or for a development install (with [uv](https://docs.astral.sh/uv/);
|
|
70
|
+
install via `curl -LsSf https://astral.sh/uv/install.sh | sh`):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/j-alford/viper-pwn
|
|
74
|
+
cd viper-pwn
|
|
75
|
+
uv venv && source .venv/bin/activate
|
|
76
|
+
uv pip install -e ".[fast]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import astropy.units as u
|
|
83
|
+
from viper import PWNModel
|
|
84
|
+
|
|
85
|
+
model = PWNModel()
|
|
86
|
+
model.set_pulsar('J0007+7303') # loads P, Pdot, distance from ATNF catalog
|
|
87
|
+
model.distance.value = 1.4 * u.kpc # override the ATNF catalog value if desired
|
|
88
|
+
|
|
89
|
+
model.esn.value = 1e51 * u.erg
|
|
90
|
+
model.mej.value = 6.0 * u.M_sun
|
|
91
|
+
model.n_ism.value = 0.4 / u.cm**3
|
|
92
|
+
model.braking_index.value = 2.5
|
|
93
|
+
model.tau_sd.value = 3000 * u.yr
|
|
94
|
+
model.eta_b.value = 0.01
|
|
95
|
+
model.e_min.value = 3.0 * u.GeV
|
|
96
|
+
model.e_max.value = 500 * u.TeV
|
|
97
|
+
model.e_break.value = 50.0 * u.GeV
|
|
98
|
+
model.p1.value = 0.8
|
|
99
|
+
model.p2.value = 2.5
|
|
100
|
+
|
|
101
|
+
# model.add_ic_field(temp=2753, norm=23.6137) # optional: add a NIR seed field (norm in eV/cm^3)
|
|
102
|
+
|
|
103
|
+
result = model.run()
|
|
104
|
+
|
|
105
|
+
dyn = result.dyninfo[-1]
|
|
106
|
+
print(f"R_pwn = {dyn.rad_pwn:.2f} pc")
|
|
107
|
+
print(f"B = {dyn.bpwn:.2f} uG")
|
|
108
|
+
print(f"V_pwn = {dyn.vpwn:.0f} km/s")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Plotting the SED and Particle Spectrum
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import matplotlib.pyplot as plt
|
|
115
|
+
from viper.plotting import plot_model_sed
|
|
116
|
+
|
|
117
|
+
fig, _ = plot_model_sed(result, distance=model.distance.value)
|
|
118
|
+
plt.show()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+

|
|
122
|
+
|
|
123
|
+
### Evolution history
|
|
124
|
+
|
|
125
|
+
`result.dyninfo` records the full dynamical history at every timestep:
|
|
126
|
+
radii (`rad_pwn`, `rad_snr`, `rad_rs`, `rad_psr`), velocities (`vpwn`,
|
|
127
|
+
`vejpwn`, `vsnr`), magnetic field (`bpwn`), energies (`epwn_50`,
|
|
128
|
+
`epwnb_50`, `epwnp_50` in units of 10^50 erg), luminosities
|
|
129
|
+
(`synch_lum_40`, `ic_lum_40`, `ad_lum_40`, `edot_40` in 10^40 erg/s),
|
|
130
|
+
pressures (`ppwn`, `psnrpwn`), PWN-swept ejecta mass (`mswpwn`).
|
|
131
|
+
|
|
132
|
+

|
|
133
|
+
|
|
134
|
+
Reproducer: [`docs/scripts/make_evolution_grid.py`](docs/scripts/make_evolution_grid.py).
|
|
135
|
+
|
|
136
|
+
## Physics Notes
|
|
137
|
+
|
|
138
|
+
- **SNR dynamics**: Truelove & McKee (1999) analytic solution for
|
|
139
|
+
non-radiative SNR with power-law ejecta profile (n=9 hardcoded).
|
|
140
|
+
- **PWN-RS collision**: Detected when R_pwn > R_rs; switches to
|
|
141
|
+
Bandiera (1984) self-similar interior pressure.
|
|
142
|
+
- **Radiative losses**: Synchrotron + IC (full Klein-Nishina cross-section).
|
|
143
|
+
Adiabatic losses from PWN expansion included.
|
|
144
|
+
- **IC target fields**: the CMB is always included, and the model
|
|
145
|
+
supports including additional photon fields.
|
|
146
|
+
- **Single-zone**: No spatial structure within the PWN.
|
|
147
|
+
- **SSC**: Synchrotron self-Compton is off by default.
|
|
148
|
+
|
|
149
|
+
## MCMC Fitting with emcee
|
|
150
|
+
|
|
151
|
+
The `fitting` module provides a complete MCMC pipeline using
|
|
152
|
+
[emcee](https://emcee.readthedocs.io). Set up a model, unfreeze
|
|
153
|
+
parameters with bounds, and pass observables in whatever units your
|
|
154
|
+
data are in:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import astropy.units as u
|
|
158
|
+
from viper import PWNModel
|
|
159
|
+
from viper.fitting import run_mcmc
|
|
160
|
+
from viper.observables import Observable
|
|
161
|
+
|
|
162
|
+
model = PWNModel()
|
|
163
|
+
model.set_pulsar('J1930+1852')
|
|
164
|
+
|
|
165
|
+
model.esn.value = 0.95e51 * u.erg
|
|
166
|
+
model.esn.frozen = False
|
|
167
|
+
model.esn.bounds = (1e49 * u.erg, 3e51 * u.erg)
|
|
168
|
+
|
|
169
|
+
model.mej.value = 21 * u.M_sun
|
|
170
|
+
model.mej.frozen = False
|
|
171
|
+
model.mej.bounds = (1 * u.M_sun, 100 * u.M_sun)
|
|
172
|
+
|
|
173
|
+
model.n_ism.value = 0.008 / u.cm**3
|
|
174
|
+
model.n_ism.frozen = False
|
|
175
|
+
model.n_ism.bounds = (1e-4 / u.cm**3, 1 / u.cm**3)
|
|
176
|
+
|
|
177
|
+
# ... set bounds for all free params ...
|
|
178
|
+
|
|
179
|
+
model.add_ic_field(temp=2753, norm=23.6137, frozen=False) # norm in eV/cm^3
|
|
180
|
+
model.parameters['ic_temp_1'].bounds = (1000 * u.K, 5000 * u.K)
|
|
181
|
+
model.parameters['ic_norm_1'].bounds = (1.0, 100.0) # eV/cm^3
|
|
182
|
+
|
|
183
|
+
observables = [
|
|
184
|
+
Observable.angular_size_snr(6.6, 0.4, 'arcmin'),
|
|
185
|
+
Observable.angular_size_pwn(1.14, 0.04, 'arcmin'),
|
|
186
|
+
Observable.flux_density(1.4, 'GHz', 433, 30, 'mJy'),
|
|
187
|
+
Observable.flux_density(4.7, 'GHz', 327, 25, 'mJy'),
|
|
188
|
+
Observable.band_flux(2, 10, 'keV', 5.43e-12, 0.035e-12),
|
|
189
|
+
Observable.spectral_index(2, 10, 'keV', 2.09, 0.01),
|
|
190
|
+
Observable.flux_density(311, 'GeV', 1.10e-11, 0.56e-11,
|
|
191
|
+
'photons/cm2/s/TeV'),
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
sampler, spec = run_mcmc(model, observables, nwalkers=32, nsteps=5000, n_cores=12)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Observable types: `angular_size_snr`, `angular_size_pwn`, `flux_density`
|
|
198
|
+
(at any frequency/energy), `band_flux` (integrated), `spectral_index`.
|
|
199
|
+
Supports units: Hz/GHz/MHz, eV/keV/MeV/GeV/TeV, Jy/mJy,
|
|
200
|
+
erg/s/cm2/Hz, photons/cm2/s/TeV, erg/s/cm2, arcmin/arcsec/deg.
|
|
201
|
+
|
|
202
|
+
## Gammapy Integration
|
|
203
|
+
|
|
204
|
+
VIPER includes the class `PWNSpectralModel`, which can be used
|
|
205
|
+
directly in [gammapy](https://gammapy.org):
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from viper.gammapy import PWNSpectralModel
|
|
209
|
+
import astropy.units as u
|
|
210
|
+
|
|
211
|
+
# Pre-configured for CTA 1
|
|
212
|
+
model = PWNSpectralModel.for_cta1()
|
|
213
|
+
print(model)
|
|
214
|
+
|
|
215
|
+
# Plot
|
|
216
|
+
model.plot(energy_bounds=[1e-7, 100] * u.TeV)
|
|
217
|
+
|
|
218
|
+
# Or for G54.1+0.3 (with NIR IC field)
|
|
219
|
+
model = PWNSpectralModel.for_g54()
|
|
220
|
+
|
|
221
|
+
# Custom source
|
|
222
|
+
model = PWNSpectralModel(
|
|
223
|
+
period=0.136, p_dot=7.5112e-13,
|
|
224
|
+
esn=0.95, mej=21.0, n_ism=0.008, distance=6223.0,
|
|
225
|
+
braking_index=1.9, tau_sd=3574.0, eta_b=0.0024,
|
|
226
|
+
e_min=10.0, e_max=4e5, e_break=3232.0,
|
|
227
|
+
p1=2.29, p2=2.42,
|
|
228
|
+
ic_temp_1=2753.0, ic_norm_1=23.6137, # ic_norm_1 in eV/cm^3
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Freeze/thaw parameters for fitting
|
|
232
|
+
model.distance.frozen = True
|
|
233
|
+
model.braking_index.frozen = True
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The model caches results so repeated `evaluate` calls with unchanged
|
|
237
|
+
parameters (e.g., during gammapy's numerical integration) are instant.
|
|
238
|
+
|
|
239
|
+
## References
|
|
240
|
+
|
|
241
|
+
- Gelfand, J. D., Slane, P. O., & Zhang, W. 2009, ApJ, 703, 2051.
|
|
242
|
+
[doi:10.1088/0004-637X/703/2/2051](https://doi.org/10.1088/0004-637X/703/2/2051)
|
|
243
|
+
|
|
244
|
+
- Truelove, J. K. & McKee, C. F. 1999, ApJS, 120, 299.
|
|
245
|
+
[doi:10.1086/313176](https://doi.org/10.1086/313176)
|
|
246
|
+
|
|
247
|
+
The Gelfand et al. (2009) model has been applied to constrain the
|
|
248
|
+
properties of numerous PWNe. Recent applications include:
|
|
249
|
+
|
|
250
|
+
- Gelfand, J. D., Slane, P. O., & Temim, T. 2015, ApJ, 807, 30.
|
|
251
|
+
[doi:10.1088/0004-637X/807/1/30](https://doi.org/10.1088/0004-637X/807/1/30)
|
|
252
|
+
|
|
253
|
+
- Alford, J. A. J., Zhang, G.-B., & Gelfand, J. D. 2025, ApJ, 987, 63.
|
|
254
|
+
[doi:10.3847/1538-4357/add92e](https://doi.org/10.3847/1538-4357/add92e)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# VIPER - Volume-Integrated PWN Evolution & Radiation
|
|
2
|
+
|
|
3
|
+
A Python implementation of the semi-analytic PWN model described in
|
|
4
|
+
**Gelfand, Slane & Zhang (2009), ApJ 703, 2051**.
|
|
5
|
+
|
|
6
|
+
This code evolves a pulsar wind nebula inside a supernova
|
|
7
|
+
remnant, tracking the PWN radius, magnetic field, particle energy
|
|
8
|
+
spectrum, and broadband photon emission from radio through gamma-rays.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
**Requirements**: Python >= 3.10, NumPy >= 1.21, SciPy >= 1.7, Astropy >= 5.0
|
|
13
|
+
|
|
14
|
+
**Optional**: Numba >= 0.57, emcee >= 3.0, corner, gammapy >= 2.0, matplotlib >= 3.5
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install viper-pwn
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or for a development install (with [uv](https://docs.astral.sh/uv/);
|
|
21
|
+
install via `curl -LsSf https://astral.sh/uv/install.sh | sh`):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/j-alford/viper-pwn
|
|
25
|
+
cd viper-pwn
|
|
26
|
+
uv venv && source .venv/bin/activate
|
|
27
|
+
uv pip install -e ".[fast]"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import astropy.units as u
|
|
34
|
+
from viper import PWNModel
|
|
35
|
+
|
|
36
|
+
model = PWNModel()
|
|
37
|
+
model.set_pulsar('J0007+7303') # loads P, Pdot, distance from ATNF catalog
|
|
38
|
+
model.distance.value = 1.4 * u.kpc # override the ATNF catalog value if desired
|
|
39
|
+
|
|
40
|
+
model.esn.value = 1e51 * u.erg
|
|
41
|
+
model.mej.value = 6.0 * u.M_sun
|
|
42
|
+
model.n_ism.value = 0.4 / u.cm**3
|
|
43
|
+
model.braking_index.value = 2.5
|
|
44
|
+
model.tau_sd.value = 3000 * u.yr
|
|
45
|
+
model.eta_b.value = 0.01
|
|
46
|
+
model.e_min.value = 3.0 * u.GeV
|
|
47
|
+
model.e_max.value = 500 * u.TeV
|
|
48
|
+
model.e_break.value = 50.0 * u.GeV
|
|
49
|
+
model.p1.value = 0.8
|
|
50
|
+
model.p2.value = 2.5
|
|
51
|
+
|
|
52
|
+
# model.add_ic_field(temp=2753, norm=23.6137) # optional: add a NIR seed field (norm in eV/cm^3)
|
|
53
|
+
|
|
54
|
+
result = model.run()
|
|
55
|
+
|
|
56
|
+
dyn = result.dyninfo[-1]
|
|
57
|
+
print(f"R_pwn = {dyn.rad_pwn:.2f} pc")
|
|
58
|
+
print(f"B = {dyn.bpwn:.2f} uG")
|
|
59
|
+
print(f"V_pwn = {dyn.vpwn:.0f} km/s")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Plotting the SED and Particle Spectrum
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import matplotlib.pyplot as plt
|
|
66
|
+
from viper.plotting import plot_model_sed
|
|
67
|
+
|
|
68
|
+
fig, _ = plot_model_sed(result, distance=model.distance.value)
|
|
69
|
+
plt.show()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
### Evolution history
|
|
75
|
+
|
|
76
|
+
`result.dyninfo` records the full dynamical history at every timestep:
|
|
77
|
+
radii (`rad_pwn`, `rad_snr`, `rad_rs`, `rad_psr`), velocities (`vpwn`,
|
|
78
|
+
`vejpwn`, `vsnr`), magnetic field (`bpwn`), energies (`epwn_50`,
|
|
79
|
+
`epwnb_50`, `epwnp_50` in units of 10^50 erg), luminosities
|
|
80
|
+
(`synch_lum_40`, `ic_lum_40`, `ad_lum_40`, `edot_40` in 10^40 erg/s),
|
|
81
|
+
pressures (`ppwn`, `psnrpwn`), PWN-swept ejecta mass (`mswpwn`).
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
Reproducer: [`docs/scripts/make_evolution_grid.py`](docs/scripts/make_evolution_grid.py).
|
|
86
|
+
|
|
87
|
+
## Physics Notes
|
|
88
|
+
|
|
89
|
+
- **SNR dynamics**: Truelove & McKee (1999) analytic solution for
|
|
90
|
+
non-radiative SNR with power-law ejecta profile (n=9 hardcoded).
|
|
91
|
+
- **PWN-RS collision**: Detected when R_pwn > R_rs; switches to
|
|
92
|
+
Bandiera (1984) self-similar interior pressure.
|
|
93
|
+
- **Radiative losses**: Synchrotron + IC (full Klein-Nishina cross-section).
|
|
94
|
+
Adiabatic losses from PWN expansion included.
|
|
95
|
+
- **IC target fields**: the CMB is always included, and the model
|
|
96
|
+
supports including additional photon fields.
|
|
97
|
+
- **Single-zone**: No spatial structure within the PWN.
|
|
98
|
+
- **SSC**: Synchrotron self-Compton is off by default.
|
|
99
|
+
|
|
100
|
+
## MCMC Fitting with emcee
|
|
101
|
+
|
|
102
|
+
The `fitting` module provides a complete MCMC pipeline using
|
|
103
|
+
[emcee](https://emcee.readthedocs.io). Set up a model, unfreeze
|
|
104
|
+
parameters with bounds, and pass observables in whatever units your
|
|
105
|
+
data are in:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
import astropy.units as u
|
|
109
|
+
from viper import PWNModel
|
|
110
|
+
from viper.fitting import run_mcmc
|
|
111
|
+
from viper.observables import Observable
|
|
112
|
+
|
|
113
|
+
model = PWNModel()
|
|
114
|
+
model.set_pulsar('J1930+1852')
|
|
115
|
+
|
|
116
|
+
model.esn.value = 0.95e51 * u.erg
|
|
117
|
+
model.esn.frozen = False
|
|
118
|
+
model.esn.bounds = (1e49 * u.erg, 3e51 * u.erg)
|
|
119
|
+
|
|
120
|
+
model.mej.value = 21 * u.M_sun
|
|
121
|
+
model.mej.frozen = False
|
|
122
|
+
model.mej.bounds = (1 * u.M_sun, 100 * u.M_sun)
|
|
123
|
+
|
|
124
|
+
model.n_ism.value = 0.008 / u.cm**3
|
|
125
|
+
model.n_ism.frozen = False
|
|
126
|
+
model.n_ism.bounds = (1e-4 / u.cm**3, 1 / u.cm**3)
|
|
127
|
+
|
|
128
|
+
# ... set bounds for all free params ...
|
|
129
|
+
|
|
130
|
+
model.add_ic_field(temp=2753, norm=23.6137, frozen=False) # norm in eV/cm^3
|
|
131
|
+
model.parameters['ic_temp_1'].bounds = (1000 * u.K, 5000 * u.K)
|
|
132
|
+
model.parameters['ic_norm_1'].bounds = (1.0, 100.0) # eV/cm^3
|
|
133
|
+
|
|
134
|
+
observables = [
|
|
135
|
+
Observable.angular_size_snr(6.6, 0.4, 'arcmin'),
|
|
136
|
+
Observable.angular_size_pwn(1.14, 0.04, 'arcmin'),
|
|
137
|
+
Observable.flux_density(1.4, 'GHz', 433, 30, 'mJy'),
|
|
138
|
+
Observable.flux_density(4.7, 'GHz', 327, 25, 'mJy'),
|
|
139
|
+
Observable.band_flux(2, 10, 'keV', 5.43e-12, 0.035e-12),
|
|
140
|
+
Observable.spectral_index(2, 10, 'keV', 2.09, 0.01),
|
|
141
|
+
Observable.flux_density(311, 'GeV', 1.10e-11, 0.56e-11,
|
|
142
|
+
'photons/cm2/s/TeV'),
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
sampler, spec = run_mcmc(model, observables, nwalkers=32, nsteps=5000, n_cores=12)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Observable types: `angular_size_snr`, `angular_size_pwn`, `flux_density`
|
|
149
|
+
(at any frequency/energy), `band_flux` (integrated), `spectral_index`.
|
|
150
|
+
Supports units: Hz/GHz/MHz, eV/keV/MeV/GeV/TeV, Jy/mJy,
|
|
151
|
+
erg/s/cm2/Hz, photons/cm2/s/TeV, erg/s/cm2, arcmin/arcsec/deg.
|
|
152
|
+
|
|
153
|
+
## Gammapy Integration
|
|
154
|
+
|
|
155
|
+
VIPER includes the class `PWNSpectralModel`, which can be used
|
|
156
|
+
directly in [gammapy](https://gammapy.org):
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from viper.gammapy import PWNSpectralModel
|
|
160
|
+
import astropy.units as u
|
|
161
|
+
|
|
162
|
+
# Pre-configured for CTA 1
|
|
163
|
+
model = PWNSpectralModel.for_cta1()
|
|
164
|
+
print(model)
|
|
165
|
+
|
|
166
|
+
# Plot
|
|
167
|
+
model.plot(energy_bounds=[1e-7, 100] * u.TeV)
|
|
168
|
+
|
|
169
|
+
# Or for G54.1+0.3 (with NIR IC field)
|
|
170
|
+
model = PWNSpectralModel.for_g54()
|
|
171
|
+
|
|
172
|
+
# Custom source
|
|
173
|
+
model = PWNSpectralModel(
|
|
174
|
+
period=0.136, p_dot=7.5112e-13,
|
|
175
|
+
esn=0.95, mej=21.0, n_ism=0.008, distance=6223.0,
|
|
176
|
+
braking_index=1.9, tau_sd=3574.0, eta_b=0.0024,
|
|
177
|
+
e_min=10.0, e_max=4e5, e_break=3232.0,
|
|
178
|
+
p1=2.29, p2=2.42,
|
|
179
|
+
ic_temp_1=2753.0, ic_norm_1=23.6137, # ic_norm_1 in eV/cm^3
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Freeze/thaw parameters for fitting
|
|
183
|
+
model.distance.frozen = True
|
|
184
|
+
model.braking_index.frozen = True
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The model caches results so repeated `evaluate` calls with unchanged
|
|
188
|
+
parameters (e.g., during gammapy's numerical integration) are instant.
|
|
189
|
+
|
|
190
|
+
## References
|
|
191
|
+
|
|
192
|
+
- Gelfand, J. D., Slane, P. O., & Zhang, W. 2009, ApJ, 703, 2051.
|
|
193
|
+
[doi:10.1088/0004-637X/703/2/2051](https://doi.org/10.1088/0004-637X/703/2/2051)
|
|
194
|
+
|
|
195
|
+
- Truelove, J. K. & McKee, C. F. 1999, ApJS, 120, 299.
|
|
196
|
+
[doi:10.1086/313176](https://doi.org/10.1086/313176)
|
|
197
|
+
|
|
198
|
+
The Gelfand et al. (2009) model has been applied to constrain the
|
|
199
|
+
properties of numerous PWNe. Recent applications include:
|
|
200
|
+
|
|
201
|
+
- Gelfand, J. D., Slane, P. O., & Temim, T. 2015, ApJ, 807, 30.
|
|
202
|
+
[doi:10.1088/0004-637X/807/1/30](https://doi.org/10.1088/0004-637X/807/1/30)
|
|
203
|
+
|
|
204
|
+
- Alford, J. A. J., Zhang, G.-B., & Gelfand, J. D. 2025, ApJ, 987, 63.
|
|
205
|
+
[doi:10.3847/1538-4357/add92e](https://doi.org/10.3847/1538-4357/add92e)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "viper-pwn"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "VIPER (Volume-Integrated PWN Evolution & Radiation)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "Jason Alford" }]
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
dependencies = ["numpy>=1.21", "scipy>=1.7", "astropy>=5.0"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
plot = ["matplotlib>=3.5"]
|
|
25
|
+
fast = ["numba>=0.57"]
|
|
26
|
+
fit = ["emcee>=3.0", "corner"]
|
|
27
|
+
gammapy = [
|
|
28
|
+
"gammapy>=2.0",
|
|
29
|
+
# gammapy 2.1 imports a private regions API removed in regions 0.12
|
|
30
|
+
# (pixel_scale_angle_at_skycoord); lift when gammapy stops doing so.
|
|
31
|
+
"regions<0.12",
|
|
32
|
+
]
|
|
33
|
+
nested = ["ultranest>=4.0", "scipy>=1.7"]
|
|
34
|
+
docs = [
|
|
35
|
+
"sphinx>=7",
|
|
36
|
+
"pydata-sphinx-theme",
|
|
37
|
+
"myst-nb",
|
|
38
|
+
"sphinx-copybutton",
|
|
39
|
+
]
|
|
40
|
+
all = [
|
|
41
|
+
"matplotlib>=3.5",
|
|
42
|
+
"numba>=0.57",
|
|
43
|
+
"gammapy>=2.0",
|
|
44
|
+
"regions<0.12",
|
|
45
|
+
"emcee>=3.0",
|
|
46
|
+
"corner",
|
|
47
|
+
"ultranest>=4.0",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.urls]
|
|
51
|
+
Homepage = "https://github.com/j-alford/viper-pwn"
|
|
52
|
+
Documentation = "https://viper-pwn.readthedocs.io"
|
|
53
|
+
Repository = "https://github.com/j-alford/viper-pwn"
|
|
54
|
+
Issues = "https://github.com/j-alford/viper-pwn/issues"
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.packages.find]
|
|
57
|
+
include = ["viper*"]
|
|
58
|
+
|
|
59
|
+
[tool.setuptools.package-data]
|
|
60
|
+
viper = ["data/*.db", "data/psrcat_ref", "data/*.npz"]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
testpaths = ["tests"]
|
|
64
|
+
markers = [
|
|
65
|
+
"slow: marks tests that run the full evolution model",
|
|
66
|
+
]
|