├── .github
└── workflows
│ └── buildwheels.yml
├── .gitignore
├── .readthedocs.yaml
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── afterglowpy
├── __init__.py
├── cocoon.py
├── flux.py
├── integrate.c
├── integrate.h
├── interval.c
├── interval.h
├── jetmodule.c
├── offaxis_struct.h
├── offaxis_struct_funcs.c
├── shockEvolution.c
├── shockEvolution.h
├── shockmodule.c
└── version.py
├── docs
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── conf.py
│ ├── index.rst
│ ├── modules.rst
│ ├── modules
│ ├── afterglowpy.cocoon.rst
│ ├── afterglowpy.flux.rst
│ ├── afterglowpy.jet.rst
│ ├── afterglowpy.rst
│ └── afterglowpy.shock.rst
│ └── quickstart.rst
├── examples
├── plotCentroidAndSize.py
├── plotLateTime.py
├── plotLightCurve.py
├── plotMultibandLightCurve.py
├── plotMultibandLightCurveBatch.py
├── plotSpecType.py
└── plotSpectrum.py
├── pyproject.toml
├── setup.py
└── test
├── __init__.py
└── test_flux.py
/.github/workflows/buildwheels.yml:
--------------------------------------------------------------------------------
1 | name: Build wheels
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - dev
7 | tags:
8 | - v*
9 |
10 | env:
11 | CIBW_SKIP: cp2* pp* cp35* cp36* cp37*
12 | CIBW_ARCHS_LINUX: auto64
13 |
14 | jobs:
15 | build_wheels:
16 | name: Build wheels on ${{ matrix.os }}
17 | runs-on: ${{ matrix.os }}
18 | strategy:
19 | matrix:
20 | os: [ubuntu-latest, windows-latest, macos-13, macos-latest]
21 | steps:
22 | - uses: actions/checkout@v4
23 |
24 | # - name: Set up Python
25 | # uses: actions/setup-python@v2
26 | # with:
27 | # python-version: '3.9'
28 |
29 | - name: Build wheels
30 | uses: pypa/cibuildwheel@v2.23.0
31 |
32 | - name: Upload wheels
33 | uses: actions/upload-artifact@v4
34 | with:
35 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
36 | path: ./wheelhouse/*.whl
37 |
38 | build_sdist:
39 | name: Build source distribution
40 | runs-on: ubuntu-latest
41 | steps:
42 | - uses: actions/checkout@v4
43 |
44 | # - name: Install Python
45 | # uses: actions/setup-python@v4
46 | # with:
47 | # python-version: '3.8'
48 |
49 | - name: Install afterglowpy
50 | run: pipx run build --sdist
51 |
52 | # - name: Build sdist
53 | # run: python setup.py sdist
54 |
55 | - name: Upload sdist
56 | uses: actions/upload-artifact@v4
57 | with:
58 | name: cibw-sdist
59 | path: dist/*.tar.gz
60 |
61 | upload_pypi:
62 | needs: [build_wheels, build_sdist]
63 | runs-on: ubuntu-latest
64 | environment: pypi
65 | permissions:
66 | id-token: write
67 |
68 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
69 |
70 | steps:
71 | - uses: actions/download-artifact@v4
72 | with:
73 | pattern: cibw-*
74 | path: dist
75 | merge-multiple: true
76 |
77 | - uses: pypa/gh-action-pypi-publish@release/v1
78 | with:
79 | user: __token__
80 | password: ${{ secrets.PYPI_API_TOKEN }}
81 |
82 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swp
3 | *.so
4 | *.pyc
5 | build/
6 | dist/
7 | *egg-info*
8 | *.png
9 | *.json
10 | *.h5
11 | *.pdf
12 | *.txt
13 | scratch/
14 | wheelhouse
15 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | build:
9 | os: ubuntu-22.04
10 |
11 | tools:
12 | python: "3.12"
13 |
14 | # Build documentation in the docs/ directory with Sphinx
15 | sphinx:
16 | configuration: docs/source/conf.py
17 |
18 | python:
19 | install:
20 | - requirements: docs/requirements.txt
21 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Geoffrey Ryan
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.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include afterglowpy/version.py afterglowpy/*.h
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Numeric GRB Afterglow models
2 |
3 | A Python 3 module to calculate GRB afterglow light curves and spectra. Details of the methods can be found in [Ryan et al 2020](https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract) and [Ryan et al 2024](https://ui.adsabs.harvard.edu/abs/2024ApJ...975..131R/abstract). Builds on [van Eerten & MacFadyen 2010](https://arxiv.org/abs/1006.5125) and [van Eerten 2018](https://arxiv.org/abs/1801.01848). This code is under active development.
4 |
5 | Documentation available at
6 |
7 | ## Attribution
8 |
9 | If you use this code in a publication, please refer to the package by name and cite "Ryan, G., van Eerten, H., Piro, L. and Troja, E., Astrophysical Journal *896*, 166 (2020)" [ADS link](https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract). Upgrades including centroid motion, size, and the deep Newtonian phase are presented in "Ryan, G., van Eerten, H., Troja, E., Piro, L., O'Connor, B., and Ricci, R., Astrophysical Journal *975*, 131 (2024)" [ADS link](https://ui.adsabs.harvard.edu/abs/2024ApJ...975..131R/abstract).
10 |
11 | ## Acknowledgements
12 |
13 | This work is funded in part by the European Union’s Horizon 2020 Programme under the AHEAD2020 project (grant agreement n. 871158).
14 |
15 | ## Features
16 |
17 | _afterglowpy_ computes synchrotron emission from the forward shock of a relativistic blast wave. It includes:
18 | - Fully trans-relativistic shock evolution through a constant density medium.
19 | - On-the-fly integration over the equal-observer-time slices of the shock surface.
20 | - Approximate prescription for jet spreading.
21 | - Arbitrary viewing angles.
22 | - Angularly structured jets, ie. E(θ)
23 | - Spherical velocity-stratified outflows, ie. E(u)
24 | - Counter-jet emission.
25 | - Deep Newtonian emission.
26 | - Image moments suitable for astrometry: centroid position and image size.
27 |
28 | It has limited support (these should be considered experimental) for:
29 | - Initial energy injection
30 | - Inverse comption spectra
31 | - Early coasting phase
32 |
33 | It does not include (yet):
34 | - External wind medium, ie. n ∝ r-2
35 | - Synchrotron self-absorbtion
36 | - Reverse shock emission
37 |
38 | _afterglowpy_ has been calibrated to the BoxFit code ([van Eerten, van der Horst, & Macfadyen 2011](https://arxiv.org/abs/1110.5089), available at the [Afterglow Library](https://cosmo.nyu.edu/afterglowlibrary/boxfit2011.html)) and produces similar light curves for top hat jets (within 50% when same parameters are used) both on- and off-axis. Its jet models by default do not include an initial coasting phase, which may effect predictions for early observations.
39 |
40 | ## Changelog
41 |
42 | ### New in v0.8.1
43 | - Numpy 2.0 compatibility
44 | - `ignoreBounds` Boolean keyword argument to ignore built-in bounds checking on parameters.
45 |
46 | ### New in v0.8.0
47 | - Image size and position via the `moment` keyword.
48 | - Deep Newtonian spectral evolution at late times via `specType=grb.jet.DeepNewtonian`
49 |
50 | ## Installation/Building
51 |
52 | _afterglowpy_ is available via `pip`:
53 | ```bash
54 | $ pip install afterglowpy
55 | ```
56 |
57 | _afterglowpy_ is compatible with Numpy v1 and v2, Python 3.8+, and runs on MacOS, Linux, and Windows.
58 |
59 | If you are working on a local copy of this repo and would like to install from source, you can the run the following from the top level directory of the project.
60 | ```bash
61 | $ pip install -e .
62 | ```
63 |
64 | ## Using
65 |
66 | In your python code, import the library with `import afterglowpy as grb`.
67 |
68 | The main function of interest is`grb.fluxDensity(t, nu, **kwargs)`. See `examples/plotLightCurve.py` for a simple example.
69 |
70 | For jet-like afterglows there are up to 13 required keyword arguments:
71 |
72 | - `jetType` an integer code setting the jet structure. It can be `grb.jet.TopHat`, `grb.jet.Gaussian`, `grb.jet.PowerLawCore`, `grb.jet.GaussianCore`, `grb.jet.Spherical`, or `grb.jet.PowerLaw`.
73 | - `specType` an integer code specifying flags for the emissivity function and spectrum. Can be `grb.jet.SimpleSpec` (basic spectrum with νm and νc), `grb.jet.DeepNewtonian`, `grb.jet.EpsEBar` to interpret `epsilon_e` as ε̅e = εe(p-2)/(p-1), `grb.jet.ICCooling` (simple inverse Compton effects on the cooling frequency, experimental). Multiple options can be combined with the `|` operator.
74 | - `thetaObs` viewing angle in radians
75 | - `E0` on-axis isotropic equivalent energy in erg
76 | - `thetaCore` half-width of the jet core in radians (jetType specific)
77 | - `thetaWing` "wing" truncation angle of the jet, in radians
78 | - `b` power for power-law structure, θ-b
79 | - `n0` Number density of ISM, in cm-3
80 | - `p` Electron distribution power-law index (p>2)
81 | - `epsilon_e` Thermal energy fraction in electrons
82 | - `epsilon_B` Thermal energy fraction in magnetic field
83 | - `xi_N` Fraction of electrons that get accelerated
84 | - `d_L` Luminosity distance in cm
85 |
86 | Optional keyword arguments for all models are:
87 | - `z` redshift (defaults to 0)
88 | - `spread` boolean (defaults to True), whether to allow the jet to spread.
89 | - `counterjet` boolean (defaults to False), whether to include the counterjet
90 | - `moment` array (integer dtype, same shape as t and nu) which sky moment to compute.
91 | - `ignoreBounds` boolean (defaults to False), whether to ignore the built in paramter bounds checking.
92 | - `L0` Fiducial luminosity for energy injection, in erg/s, default 0.0.
93 | - `q` Temporal power-law index for energy injection, default 0.0.
94 | - `ts` Fiducial time-scale for energy injection, in seconds, default 0.
95 | - `tRes` time resolution of shock-evolution scheme, number of sample points per decade in time
96 | - `latRes` latitudinal resolution for structured jets, number of shells per `thetaC`
97 | - `rtol` target relative tolerance of flux integration
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/afterglowpy/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | r"""
3 | ===========
4 | afterglowpy
5 | ===========
6 |
7 | Top-level module for *afterglowpy*, computes light curves and spectra of
8 | Gamma-ray burst (GRB) afterglows.
9 |
10 | This module provides the primary user-facing functions provided by afterglowpy,
11 | useful physical constants, and conversion factors. Internal computations are
12 | performed in the submodules.
13 |
14 | Functions
15 | ---------
16 |
17 | These are the primary interface into *afterglowpy*. If you just want to compute
18 | light curves, spectra, and intensity maps, and are unconcerned with the
19 | underlying algorithm, these are all you need.
20 |
21 | =================== =========================================================
22 | :func:`fluxDensity` Compute the flux density F_nu of a GRB afterglow.
23 | :func:`intensity` Compute the specific intensity I_nu of a GRB afterglow.
24 | =================== =========================================================
25 |
26 | Submodules
27 | -----------
28 |
29 | These submodules perform the internal computations involved with calculating
30 | synchrotron emission from a blast wave: calculating the evolution of a blast
31 | wave with time, computing the synchrotron emissivity, integrating over
32 | equal-observer-time hypersurfaces, and constructing structured jets.
33 |
34 | =========================== ===================================================
35 | :mod:`afterglowpy.shock` Routines for computing evolution of a relativistic
36 | shock
37 | :mod:`afterglowpy.jet` Routines for computing synchrotron emission from a
38 | jet
39 | :mod:`afterglowpy.cocoon` Routines for computing synctrotron emission from a
40 | spherical shell.
41 | =========================== ===================================================
42 |
43 | """
44 | from .version import __version__
45 | from . import shock
46 | from . import cocoon
47 | from . import jet
48 | from . import flux
49 | from .flux import fluxDensity, intensity
50 | from .cocoon import (Hz2eV, Msun, c, cgs2mJy, day2sec, eV2Hz, ee, h, hbar,
51 | mJy2cgs, me, mp, parsec, sec2day, sigmaT)
52 | from .jet import (Cone, TopHat, Gaussian, PowerLaw, GaussianCore, PowerLawCore,
53 | Spherical)
54 |
55 | __all__ = ['__version__',
56 | 'shock', 'cocoon', 'jet', 'flux', 'fluxDensity', 'intensity',
57 | 'Hz2eV', 'Msun', 'c', 'cgs2mJy', 'day2sec', 'eV2Hz', 'ee', 'h',
58 | 'hbar', 'mJy2cgs',
59 | 'me', 'mp', 'parsec', 'sec2day', 'sigmaT']
60 |
--------------------------------------------------------------------------------
/afterglowpy/cocoon.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 | import scipy.integrate as integrate
4 | from . import shock
5 | from . import jet
6 |
7 | c = 2.99792458e10
8 | me = 9.1093897e-28
9 | mp = 1.6726231e-24
10 | h = 6.6260755e-27
11 | hbar = 1.05457266e-27
12 | ee = 4.803e-10
13 | sigmaT = 6.65e-25
14 |
15 | Msun = 1.98892e33
16 | cgs2mJy = 1.0e26
17 | mJy2cgs = 1.0e-26
18 | deg2rad = np.pi/180.0
19 | rad2deg = 180.0/np.pi
20 | day2sec = 86400.0
21 | sec2day = 1.0/day2sec
22 | parsec = 3.0857e18
23 | Hz2eV = 4.13566553853599e-15
24 | eV2Hz = 1.0/Hz2eV
25 |
26 |
27 | def dP(costheta, amu, ate, au, ar, nu, n0, p, epsE, epsB, ksiN, specType):
28 |
29 | mu = costheta
30 | ib = np.searchsorted(amu, mu)
31 | N = amu.shape[0]
32 | if ib <= 0:
33 | ib = 1
34 | elif ib >= N:
35 | ib = N-1
36 | ia = ib-1
37 |
38 | te = ((mu-amu[ia])*ate[ib] + (amu[ib]-mu)*ate[ia]) / (amu[ib]-amu[ia])
39 | u = au[ia]*math.pow(te/ate[ia], math.log(au[ib]/au[ia])
40 | / math.log(ate[ib]/ate[ia]))
41 | r = ar[ia]*math.pow(te/ate[ia], math.log(ar[ib]/ar[ia])
42 | / math.log(ate[ib]/ate[ia]))
43 |
44 | g = math.sqrt(u*u+1)
45 |
46 | us = 4*u*g / math.sqrt(8*u*u+9)
47 |
48 | rho0 = n0 * mp
49 | Msw = rho0 * 4.0/3.0 * np.pi * r**3
50 |
51 | em = jet.emissivity(nu, r, mu, te, u, us, rho0, Msw, p, epsE,
52 | epsB, ksiN, specType)
53 |
54 | return 2*np.pi * em
55 |
56 |
57 | def fluxDensity(t, nu, **kwargs):
58 |
59 | t = np.array(t)
60 |
61 | specType = kwargs['specType']
62 | uMax = kwargs['uMax']
63 | uMin = kwargs['uMin']
64 | Er = kwargs['Er']
65 | k = kwargs['k']
66 | MFast_solar = kwargs['MFast_solar']
67 | n0 = kwargs['n0']
68 | p = kwargs['p']
69 | epsilon_e = kwargs['epsilon_e']
70 | epsilon_B = kwargs['epsilon_B']
71 | ksiN = kwargs['xi_N']
72 | dL = kwargs['d_L']
73 |
74 | # Energy injection variables (off by default)
75 | L0 = kwargs['L0'] if 'L0' in kwargs else 0.0
76 | q = kwargs['q'] if 'q' in kwargs else 0.0
77 | ts = kwargs['ts'] if 'ts' in kwargs else 0.0
78 |
79 | # Numerical integration variables
80 | rtol = kwargs['rtol'] if 'rtol' in kwargs else 1.0e-3
81 | tRes = kwargs['tRes'] if 'tRes' in kwargs else 1000
82 | latRes = kwargs['latRes'] if 'latRes' in kwargs else 0
83 |
84 | # Environment Variables
85 | envType = kwargs['envType'] if 'envType' in kwargs else jet.EnvISM
86 | R0Env = kwargs['R0Env'] if 'R0Env' in kwargs else 1.0e18
87 | kEnv = kwargs['kEnv'] if 'kEnv' in kwargs else 0.0
88 | rho1Env = kwargs['rho1Env'] if 'rho1Env' in kwargs else 1.0
89 |
90 | rho0 = mp * n0
91 | Mej = MFast_solar * Msun
92 | u0 = uMax
93 | g0 = math.sqrt(1+u0*u0)
94 | bes0 = 4*u0*g0 / (4*u0*u0+3)
95 | Rd = math.pow(9*g0*g0*Mej / (4*np.pi*(g0+1)*(4*u0*u0+3)*rho0), 1./3.)
96 | td = Rd / (bes0 * c)
97 |
98 | t0 = min(1.0e-2*td, 5.0e-1 * g0*g0*t.min(), 5.0e-1 * t.min()/(1+bes0))
99 | t1 = 2. * g0*g0*t.max()
100 |
101 | NT = int(tRes * math.log10(t1/t0))
102 | # print("{0:.3e} {1:.3e} {2:.3e} {3:d}".format(t0, t1, t1/t0, NT))
103 |
104 | r0 = bes0*c*t0
105 |
106 | # Vej0 = 4.0/3.0*np.pi*r0*r0*r0
107 |
108 | ate = np.logspace(math.log10(t0), math.log10(t1), num=NT, base=10.0)
109 |
110 | ar, au = shock.shockEvolRK4(ate, r0, uMax,
111 | MFast_solar*Msun,
112 | envType, rho0, R0Env, kEnv, rho1Env,
113 | Er, k, uMin, L0, q, ts)
114 |
115 | P = np.zeros(t.shape)
116 |
117 | wopts = None
118 |
119 | for i in range(len(t)):
120 | amu = c * (ate - t[i]) / ar
121 |
122 | args = (amu, ate, au, ar, nu[i], n0, p, epsilon_e, epsilon_B, ksiN,
123 | specType)
124 |
125 | res = integrate.quad(dP, 0.0, 1.0, args, full_output=1, wopts=wopts,
126 | epsrel=rtol)
127 | P[i] = res[0]
128 |
129 | Fnu = cgs2mJy * P / (4*np.pi*dL*dL)
130 |
131 | return Fnu
132 |
--------------------------------------------------------------------------------
/afterglowpy/flux.py:
--------------------------------------------------------------------------------
1 | from . import cocoon
2 | from . import jet
3 | import numpy as np
4 | import warnings
5 |
6 |
7 | def fluxDensity(t, nu, *args, **kwargs):
8 | r"""
9 | Compute the flux density :math:`F_{\nu}` of a GRB afterglow.
10 |
11 | Utiliizes the single shell approximation described in Ryan et al 2020
12 | to compute the synchrotron emission from the forward shock of a blast
13 | wave at the specified times and frequencies.
14 |
15 | ``fluxDensity`` takes many model-dependent parameters which are specified
16 | via keyword arguments. It is recommended to collect these parameters in a
17 | dictionary ``Z`` and call fluxDensity() by::
18 |
19 | Fnu = fluxDensity(t, nu, **Z)
20 |
21 | Alternately model parameters may be specified as positional arguments,
22 | although this interface is **not recommended** and may be deprecated in
23 | the future. To call a jetted model with positional and keyword arguments::
24 |
25 | Fnu = fluxDensity(t, nu, jetType, specType, thetaObs, E0, thetaCore,
26 | thetaWing, b, L0, q, ts, n0, p, epsilon_e, epsilon_B,
27 | xi_N, d_L, **Z)
28 |
29 | To call a spherical refreshed shock model with positional arguments::
30 |
31 | Fnu = fluxDensity(t, nu, jet.Spherical, specType, uMax, uMin, Er, k,
32 | MFast_solar, L0, q, ts, n0, p, epsilon_e, epsilon_B,
33 | xi_N, d_L, **Z)
34 |
35 |
36 | Parameters
37 | ----------
38 | t : array_like or scalar
39 | Time since burst in observer frame, measured in seconds.
40 | nu : array_like or scalar
41 | Frequency of flux in observer frame, measured in Hz, same size as t.
42 | jetType : int
43 | Code for type of jet. Model codes are available in ``afterglowpy.jet``
44 | and include: ``jet.TopHat``, ``jet.Cone``, ``jet.Gaussian``,
45 | ``jet.PowerLaw``, ``jet.GaussianCore``, ``jet.PowerLawCore``, and
46 | ``jet.Spherical``.
47 | specType : int
48 | Flags for type of spectrum/emissivity function. Spectrum flags are
49 | available in ``afterglowpy.jet`` and include:
50 |
51 | - ``jet.SimpleSpec`` broken power law with nu_m and nu_c (Ryan+ 2020,
52 | default),
53 | - ``jet.DeepNewtonian`` better handling of late-time emission when
54 | some electrons become non-relativistic (e.g. Sironi+ 2013),
55 | - ``jet.EpsEBar`` interpret the epsilon_e parameter as
56 | :math:`\bar{\epsilon}_e = \epsilon_e (p-2) / (p-1)`
57 | (e.g. Granot & Sari 2002),
58 | - ``jet.ICCooling`` simple inverse-compton contribution to cooling
59 | (experimental).
60 |
61 | Flags can be combined with the | operator.
62 | thetaObs : float
63 | Viewing angle in radians. Jet models only.
64 | E0: float
65 | Isotropic-equivalent energy along the jet axis in ergs. Jet models
66 | only.
67 | thetaCore: float
68 | Half opening angle of jet core in radians. Jet models only.
69 | thetaWing: float
70 | Outer truncation angle of the jet in radians. Ignored by
71 | ``jet.TopHat``, jet models only.
72 | b: float
73 | Power law index of jet angular energy distribution. Only used by
74 | ``jet.PowerLaw`` and ``jet.PowerLawCore``.
75 | n0 : float
76 | Number density of protons in circumburst medium in cm^{-3}.
77 | p : float
78 | Power law index of relativistic electron energy distribution,
79 | generally p > 2.
80 | epsilon_e : float
81 | Fraction of thermal energy in relativistic electrons, epsilon_e <= 1.
82 | epsilon_B : float
83 | Fraction of thermal energy in magnetic field, epsilon_B <= 1.
84 | xi_N : float
85 | Fraction of electrons that get accelerated, xi_N <= 1.
86 | d_L : float
87 | Luminosity distance to burst, in cm.
88 | z : float, optional
89 | Redshift of burst, defaults to 0.
90 |
91 | L0: float, optional
92 | Luminosity of energy injection, in erg/s. Default 0.0.
93 | q: float, optional
94 | Power law index of energy injection: L = L0 (t/t0)^{-q}, t0 = 1 ks.
95 | Default 0.0.
96 | ts: float, optional
97 | Time energy injection ends in burster frame, in seconds. Default 0.0.
98 | g0 : float, optional
99 | EXPERIMENTAL. Initial Lorentz factor of outflow along jet axis,
100 | defaults to -1 (unset, jet has deceleration radius 0). Do not use with
101 | jet spreading enabled.
102 |
103 | uMax : float
104 | Maximum 4-velocity of outflow. Only for spherical models.
105 | uMin : float
106 | Minimum 4-velocity of outflow. Only for spherical models
107 | Er : float
108 | Normalization of outflow's energy distribution in ergs. Only for
109 | spherical models.
110 | E(u>U) = Er * U^{-k}
111 | k : float
112 | Power law index of outflow's energy distribution. Only for spherical
113 | models
114 | MFast_solar : float
115 | Mass of material at u_max in solar masses. Only for spherical models.
116 |
117 | Other Parameters
118 | ----------------
119 |
120 | spread : {True, False}, optional
121 | Whether to include jet spreading. Defaults to True.
122 | counterjet : {'True', 'False'}, optional
123 | Whether to include counterjet emission. Defaults to False.
124 | ignoreBounds : {'True', 'False'}, optional
125 | Whether to ignore built-in parameter bounds checking.
126 | moment : array_like, optional
127 | An integer array the same shape as the larger of `t` or `nu`. Selects
128 | which image moment to compute. Observer's sky is on the x-y plane,
129 | with the jet propagating in the x-direction. Moments are in terms of
130 | proper length (in cm) Options are: `jet.MOM_0` Flux, default,
131 | `jet.MOM_X` x^1 moment, `jet.MOM_Y` y^1 moment, `jet.MOM_Z` z^1 moment,
132 | `jet.MOM_XX` x^2 moment, `jet.MOM_YY` y^2 moment, `jet.MOM_ZZ`, z^2
133 | moment, `jet.MOM_XY` x^1 y^1, `jet.MOM_YZ`, `jet.MOM_XZ`.
134 | tRes : int, optional
135 | Time resolution, number of points per decade in t, for shock evolution.
136 | Defaults to 1000.
137 | latRes : int, optional
138 | Lateral resolution of structured jet, number of conical sections per
139 | thetaCore-sized interval. Defaults to 5.
140 | intType : int, optional
141 | Integration scheme to use when computing flux. Defaults to
142 | ``jet.Cadre``. Changing this may result in longer run times or larger
143 | than expected numerical errors.
144 | rtolStruct : float, optional
145 | Overall relative tolerance of flux integration for structured jets.
146 | Defaults to 1.0e-2.
147 | rtolTheta : float, optional
148 | Relative tolerance of flux integration over theta within each
149 | conical section. Defaults to 1.0e-2.
150 | rtolPhi : float, optional
151 | Relative tolerance of flux integration over phi within
152 | each conical section. Defaults to 1.0e-2.
153 | NPhi : int, optional
154 | Maximum number of evaluations to perform in phi direction during
155 | numerical integration. Default 1000.
156 | NTheta : int, optional
157 | Maximum number of evaluations to perform in theta direction during
158 | numerical integration. Default 1000.
159 |
160 | Returns
161 | -------
162 | Fnu: array
163 | The flux density F_nu in the observer frame, same shape as t and nu.
164 |
165 | Raises
166 | ------
167 |
168 | ValueError
169 | If t, nu are the wrong shape or arguments take illegal values.
170 | """
171 |
172 | argsDict = parseArgs(args, kwargs)
173 |
174 | # Check Arguments, will raise ValueError if args are bad
175 | t, nu = checkTNu(t, nu)
176 |
177 | jetType = argsDict['jetType']
178 |
179 | # Check arguments have valid values, raise ValueError if not.
180 | # if ignoreBounds is True, these checks will be skipped.
181 | if jetType == jet.Spherical:
182 | checkCocoonArgs(argsDict)
183 | else:
184 | checkJetArgs(argsDict)
185 |
186 | # arguments are good, full steam ahead!
187 | z = argsDict.pop('z') if 'z' in argsDict else 0.0
188 |
189 | tz = t / (1+z)
190 | nuz = nu * (1+z)
191 |
192 | # Default spreading method
193 | if 'spread' in argsDict:
194 | if argsDict['spread'] is True:
195 | if jetType == -2 and 'thetaCoreGlobal' in argsDict:
196 | argsDict['spread'] = 8
197 | else:
198 | argsDict['spread'] = 7
199 |
200 | # This was a bad idea to add to this function, but is kept for
201 | # backwards compatibility. Please don't use these.
202 | LR = argsDict.pop('LR') if 'LR' in argsDict else 0.0
203 | LO = argsDict.pop('LO') if 'LO' in argsDict else 0.0
204 | LX = argsDict.pop('LX') if 'LX' in argsDict else 0.0
205 | tAdd = argsDict.pop('tAdd') if 'tAdd' in argsDict else 0.0
206 |
207 | # timeA = time.time()
208 |
209 | Fnu = np.empty(tz.shape)
210 |
211 | if jetType == jet.Spherical:
212 | Fnu.flat[:] = cocoon.fluxDensity(tz.flat, nuz.flat, **argsDict)
213 | else:
214 | Fnu.flat[:] = jet.fluxDensity(tz.flat, nuz.flat, **argsDict)
215 | # timeB = time.time()
216 | # print("Eval took: {0:f} s".format(timeB - timeA))
217 |
218 | # Adding background luminosities.
219 | L_to_flux = cocoon.cgs2mJy / (4*np.pi * argsDict['d_L']**2)
220 |
221 | if LR > 0.0:
222 | rad = (nuz < 3.0e11) & (tz > tAdd) # radio < 300 GHz
223 | Lnu = LR / 1.0e10 # 10 GHz bandwidth
224 | Fnu[rad] += Lnu*L_to_flux
225 | if LO > 0.0:
226 | # 300GHz= 3.0e11) & (nuz < 100*cocoon.eV2Hz) & (tz > tAdd)
228 | Lnu = LO * 2.32478e-5 / cocoon.c # 2324.78 A bandwidth
229 | Fnu[opt] += Lnu*L_to_flux
230 | if LX > 0.0:
231 | xry = (nuz >= 100*cocoon.eV2Hz) & (tz > tAdd) # xray > 100eV
232 | Lnu = LX / (9.7e3 * cocoon.eV2Hz) # 9.7 keV bandwidth
233 | Fnu[xry] += Lnu*L_to_flux
234 |
235 | # K-correct the flux
236 | Fnu *= 1+z
237 |
238 | return Fnu
239 |
240 |
241 | def intensity(theta, phi, t, nu, *args, **kwargs):
242 | r"""
243 | Compute the intensity I_nu of a GRB afterglow.
244 |
245 | Utiliizes the single shell approximation described in Ryan et al 2019
246 | to compute the synchrotron emission from the forward shock of a blast
247 | wave at the specified angular coordinates, times, and frequencies.
248 |
249 | The returned intensity is that emitted by the blast wave, not that
250 | directly observed from Earth. To get the observed flux, integrate over
251 | the surface of the blast wave in the frame of the burst.
252 |
253 | .. math::
254 | F_\nu = \int \! d\Omega\ I_\nu
255 |
256 | Angular coordinates are in a spherical coordinate system, centered on the
257 | burst, with z-axis aligned on the jet axis.
258 |
259 | ``intensity`` takes many model-dependent parameters which are specified
260 | via keyword arguments. It is recommended to collect these parameters in a
261 | dictionary ``Z`` and call fluxDensity() by::
262 |
263 | Inu = intensity(theta, phi, t, nu, **Z)
264 |
265 | Alternately model parameters may be specified as positional arguments,
266 | although this interface is **not recommended** and may be deprecated in
267 | the future. To call a jetted model with positional and keyword arguments::
268 |
269 | Inu = fluxDensity(theta, phi, t, nu, jetType, specType, thetaObs, E0,
270 | thetaCore, thetaWing, b, L0, q, ts, n0, p, epsilon_e,
271 | epsilon_B, xi_N, d_L, **Z)
272 |
273 | This is currently only implemented for jetted models. Do not use with
274 | ``jetType=jet.Spherical``.
275 |
276 |
277 | Parameters
278 | ----------
279 | theta: array_like or scalar
280 | Polar angle from jet axis in radians. Scalar, or array of same shape
281 | as phi, t, and nu.
282 | phi: array_like or scalar
283 | Azimuthal angle around jet axis in radians. Observer is at phi = 0.
284 | Scalar, or array of same shape as theta, t, and nu.
285 | t : array_like or scalar
286 | Time since burst in observer frame, measured in seconds.
287 | nu : array_like or scalar
288 | Frequency of flux in observer frame, measured in Hz, same size as t.
289 | jetType : int
290 | Code for type of jet. Model codes are available in ``afterglowpy.jet``
291 | and include: ``jet.TopHat``, ``jet.Cone``, ``jet.Gaussian``,
292 | ``jet.PowerLaw``, ``jet.GaussianCore``, ``jet.PowerLawCore``, and
293 | ``jet.Spherical``.
294 | specType : int
295 | Flags for type of spectrum/emissivity function. Spectrum flags are
296 | available in ``afterglowpy.jet`` and include:
297 |
298 | - ``jet.SimpleSpec`` broken power law with nu_m and nu_c
299 | (Ryan+ 2020, default),
300 | - ``jet.DeepNewtonian`` better handling of late-time emission when
301 | some electrons become non-relativistic (e.g. Sironi+ 2013),
302 | - ``jet.EpsEBar`` interpret the epsilon_e parameter as
303 | :math:`\bar{\epsilon}_e = \epsilon_e (p-2) / (p-1)`
304 | (e.g. Granot & Sari 2002),
305 | - ``jet.ICCooling`` simple inverse-compton contribution to cooling
306 | (experimental).
307 |
308 | Flags can be combined with the | operator.
309 | thetaObs : float
310 | Viewing angle in radians. Jet models only.
311 | E0: float
312 | Isotropic-equivalent energy along the jet axis in ergs. Jet models
313 | only.
314 | thetaCore: float
315 | Half opening angle of jet core in radians. Jet models only.
316 | thetaWing: float
317 | Outer truncation angle of the jet in radians. Ignored by
318 | ``jet.TopHat``, jet models only.
319 | b: float
320 | Power law index of jet angular energy distribution. Only used by
321 | ``jet.PowerLaw`` and ``jet.PowerLawCore``.
322 | n0 : float
323 | Number density of protons in circumburst medium in cm^{-3}.
324 | p : float
325 | Power law index of relativistic electron energy distribution,
326 | generally p > 2.
327 | epsilon_e : float
328 | Fraction of thermal energy in relativistic electrons, epsilon_e <= 1.
329 | epsilon_B : float
330 | Fraction of thermal energy in magnetic field, epsilon_B <= 1.
331 | xi_N : float
332 | Fraction of electrons that get accelerated, xi_N <= 1.
333 | d_L : float
334 | Luminosity distance to burst, in cm.
335 | z : float, optional
336 | Redshift of burst, defaults to 0.
337 |
338 | L0: float, optional
339 | Luminosity of energy injection, in erg/s. Default 0.0.
340 | q: float, optional
341 | Power law index of energy injection: L = L0 (t/t0)^{-q}, t0 = 1 ks.
342 | Default 0.0.
343 | ts: float, optional
344 | Time energy injection ends in burster frame, in seconds. Default 0.0.
345 | g0 : float, optional
346 | EXPERIMENTAL. Initial Lorentz factor of outflow along jet axis,
347 | defaults to -1 (unset, jet has deceleration radius 0). Do not use with
348 | jet spreading enabled.
349 |
350 | Other Parameters
351 | ----------------
352 |
353 | spread : {True, False}, optional
354 | Whether to include jet spreading. Defaults to True.
355 | counterjet : {'True', 'False'}, optional
356 | Whether to include counterjet emission. Defaults to False.
357 | ignoreBounds : {'True', 'False'}, optional
358 | Whether to ignore built-in parameter bounds checking.
359 | tRes : int, optional
360 | Time resolution, number of points per decade in t, for shock evolution.
361 | Defaults to 1000.
362 | latRes : int, optional
363 | Lateral resolution of structured jet, number of conical sections per
364 | thetaCore-sized interval. Defaults to 5.
365 | intType : int, optional
366 | ``fluxDensity()`` parameter ignored by ``intensity()``
367 | rtolStruct : float, optional
368 | ``fluxDensity()`` parameter ignored by ``intensity()``
369 | rtolTheta : float, optional
370 | ``fluxDensity()`` parameter ignored by ``intensity()``
371 | rtolPhi : float, optional
372 | ``fluxDensity()`` parameter ignored by ``intensity()``
373 | NPhi : int, optional
374 | ``fluxDensity()`` parameter ignored by ``intensity()``
375 | NTheta : int, optional
376 | ``fluxDensity()`` parameter ignored by ``intensity()``
377 |
378 | Returns
379 | -------
380 | Inu : array
381 | The specific intensity I_nu in the observer frame, same shape as
382 | theta, phi, t, and nu.
383 |
384 | Raises
385 | ------
386 |
387 | ValueError
388 | If theta, phi, t, nu are the wrong shape or arguments take illegal
389 | values.
390 | """
391 |
392 | argsDict = parseArgs(args, kwargs)
393 |
394 | # Check Arguments, will raise ValueError if args are bad
395 | theta, phi, t, nu = checkThetaPhiTNu(theta, phi, t, nu)
396 |
397 | jetType = argsDict['jetType']
398 |
399 | # Check arguments have valid values, raise ValueError if not.
400 | # if ignoreBounds is True, these checks will be skipped.
401 | if jetType == jet.Spherical:
402 | checkCocoonArgs(argsDict)
403 | else:
404 | checkJetArgs(argsDict)
405 |
406 | # arguments are good, full steam ahead!
407 |
408 | z = argsDict.pop('z') if 'z' in argsDict else 0.0
409 |
410 | tz = t / (1+z)
411 | nuz = nu * (1+z)
412 |
413 | # Default spreading method
414 | if 'spread' in argsDict:
415 | if argsDict['spread'] is True:
416 | if jetType == -2 and 'thetaCoreGlobal' in argsDict:
417 | argsDict['spread'] = 8
418 | else:
419 | argsDict['spread'] = 7
420 |
421 | # Intercept background luminosities, then ignore them.
422 | _ = argsDict.pop('LR') if 'LR' in argsDict else 0.0
423 | _ = argsDict.pop('LO') if 'LO' in argsDict else 0.0
424 | _ = argsDict.pop('LX') if 'LX' in argsDict else 0.0
425 | _ = argsDict.pop('tAdd') if 'tAdd' in argsDict else 0.0
426 |
427 | Inu = np.empty(theta.shape)
428 | Inu.flat[:] = jet.intensity(theta.flat, phi.flat, tz.flat, nuz.flat,
429 | **argsDict)
430 |
431 | # K-correct the intensity
432 | # I'm only using the flux correction here, which leaves the angular
433 | # part of the intensity uncorrected. Best be careful.
434 |
435 | Inu *= 1+z
436 |
437 | return Inu
438 |
439 |
440 | def checkTNu(t, nu):
441 | # Make sure t and nu are array_like or castable to an array.
442 | t = np.atleast_1d(t)
443 | nu = np.atleast_1d(nu)
444 |
445 | # Check shapes, if scalars make into right size array
446 | if t.shape != nu.shape:
447 | if t.shape == (1, ):
448 | T = t[0]
449 | t = np.empty(nu.shape)
450 | t[:] = T
451 | elif nu.shape == (1, ):
452 | NU = nu[0]
453 | nu = np.empty(t.shape)
454 | nu[:] = NU
455 | else:
456 | raise ValueError("t and nu must be same shape or scalars")
457 |
458 | return t, nu
459 |
460 |
461 | def checkThetaPhiTNu(theta, phi, t, nu):
462 | # Make sure args are array_like or castable to an array.
463 | theta = np.atleast_1d(theta)
464 | phi = np.atleast_1d(phi)
465 | t = np.atleast_1d(t)
466 | nu = np.atleast_1d(nu)
467 |
468 | shape = (1, )
469 | if shape != theta.shape:
470 | shape = theta.shape
471 | elif shape != phi.shape:
472 | shape = phi.shape
473 | elif shape != t.shape:
474 | shape = t.shape
475 | else:
476 | shape = nu.shape
477 |
478 | if theta.shape != shape:
479 | if theta.shape == (1, ):
480 | TH = theta[0]
481 | theta = np.empty(shape)
482 | theta[:] = TH
483 | else:
484 | msg = "theta must be scalar or same shape as phi, t, nu"
485 | raise ValueError(msg)
486 |
487 | if phi.shape != shape:
488 | if phi.shape == (1, ):
489 | PH = phi[0]
490 | phi = np.empty(shape)
491 | phi[:] = PH
492 | else:
493 | msg = "phi must be scalar or same shape as theta, t, nu"
494 | raise ValueError(msg)
495 |
496 | if t.shape != shape:
497 | if t.shape == (1, ):
498 | T = t[0]
499 | t = np.empty(shape)
500 | t[:] = T
501 | else:
502 | msg = "t must be scalar or same shape as theta, phi, nu"
503 | raise ValueError(msg)
504 |
505 | if nu.shape != shape:
506 | if nu.shape == (1, ):
507 | NU = nu[0]
508 | nu = np.empty(shape)
509 | nu[:] = NU
510 | else:
511 | msg = "nu must be scalar or same shape as theta, phi, t"
512 | raise ValueError(msg)
513 |
514 | return theta, phi, t, nu
515 |
516 |
517 | def checkJetArgs(argsDict):
518 |
519 | if 'ignoreBounds' in argsDict:
520 | ignore = argsDict.pop('ignoreBounds')
521 | if ignore:
522 | return
523 |
524 | jetType = argsDict['jetType']
525 | specType = argsDict['specType']
526 |
527 | theta_obs = argsDict['thetaObs']
528 | E0 = argsDict['E0']
529 | theta_c = argsDict['thetaCore']
530 | n0 = argsDict['n0']
531 | p = argsDict['p']
532 | epse = argsDict['epsilon_e']
533 | epsB = argsDict['epsilon_B']
534 | xiN = argsDict['xi_N']
535 | dL = argsDict['d_L']
536 |
537 | # More-or-less universal bounds
538 | if theta_obs < 0.0 or theta_obs > np.pi:
539 | raise ValueError("theta_obs must be in [0.0, pi]")
540 | if E0 <= 0.0:
541 | raise ValueError("E0 must be positive")
542 | if jetType != jet.Cone and (theta_c <= 0.0 or theta_c > 0.5*np.pi):
543 | raise ValueError("theta_c must be in (0.0, pi/2]")
544 | if jetType == jet.Cone and (theta_c < 0.0 or theta_c > 0.5*np.pi):
545 | raise ValueError("theta_c must be in [0.0, pi/2]")
546 | if n0 <= 0.0:
547 | raise ValueError("n0 must be positive")
548 | if (specType & jet.EpsEBar) == 0 and p <= 2.0:
549 | raise ValueError("p must be in (2, inf)")
550 | if (specType & jet.EpsEBar) != 0 and p <= 1.0:
551 | raise ValueError("p must be in (1, inf)")
552 | if epse <= 0.0 or epse > 1.0:
553 | raise ValueError("epsilon_e must be in (0, 1]")
554 | if epsB <= 0.0 or epsB > 1.0:
555 | raise ValueError("epsilon_B must be in (0, 1]")
556 | if xiN <= 0.0 or xiN > 1.0:
557 | raise ValueError("xi_N must be in (0, 1]")
558 | if dL <= 0.0:
559 | raise ValueError("d_L must be positive")
560 |
561 | # Bounds on optional parameters
562 |
563 | if jetType != jet.TopHat:
564 | if 'thetaWing' not in argsDict:
565 | raise KeyError('This jet type requires thetaWing')
566 | else:
567 | theta_w = argsDict['thetaWing']
568 | if (theta_w <= 0.0 or theta_w > 0.5*np.pi):
569 | raise ValueError("thetaWing must be in (0.0, pi/2]")
570 |
571 | if jetType == jet.PowerLaw or jetType == jet.PowerLawCore:
572 | if 'b' not in argsDict:
573 | raise KeyError('This jet type requires b')
574 | else:
575 | b = argsDict['b']
576 | if (b <= 0.0):
577 | raise ValueError("b must be positive")
578 |
579 | # Energy Injection
580 | if 'L0' in argsDict:
581 | L0 = argsDict['L0']
582 | if L0 < 0.0:
583 | raise ValueError("L0 must be non-negative")
584 | if 'ts' in argsDict:
585 | ts = argsDict['ts']
586 | if ts < 0.0:
587 | raise ValueError("ts must be non-negative")
588 |
589 | # Additional Luminosity
590 | if 'LR' in argsDict:
591 | LR = argsDict['LR']
592 | if LR < 0.0:
593 | raise ValueError("LR must be non-negative")
594 | if 'LO' in argsDict:
595 | LO = argsDict['LO']
596 | if LO < 0.0:
597 | raise ValueError("LO must be non-negative")
598 | if 'LX' in argsDict:
599 | LX = argsDict['LX']
600 | if LX < 0.0:
601 | raise ValueError("LX must be non-negative")
602 |
603 | if 'z' in argsDict:
604 | if argsDict['z'] < 0.0:
605 | raise ValueError("z must be non-negative")
606 |
607 | # Model Specific bounds
608 | if jetType == jet.Cone and argsDict['thetaCore'] > argsDict['thetaWing']:
609 | raise ValueError("thetaWing must be larger than thetaCore"
610 | "for cone model")
611 |
612 | return
613 |
614 |
615 | def checkCocoonArgs(argsDict):
616 |
617 | if 'ignoreBounds' in argsDict:
618 | ignore = argsDict.pop('ignoreBounds')
619 | if ignore:
620 | return
621 |
622 | for _, x in argsDict.items():
623 | if not np.isfinite(x):
624 | raise ValueError("All parameters must be finite")
625 |
626 | specType = argsDict['specType']
627 |
628 | u_max = argsDict['uMax']
629 | u_min = argsDict['uMin']
630 | Er = argsDict['Er']
631 | MFast = argsDict['MFast_solar']
632 | n0 = argsDict['n0']
633 | p = argsDict['p']
634 | epse = argsDict['epsilon_e']
635 | epsB = argsDict['epsilon_B']
636 | xiN = argsDict['xi_N']
637 | dL = argsDict['d_L']
638 |
639 | if u_max <= 0.0:
640 | raise ValueError("u_max must be positive")
641 | if u_min <= 0.0:
642 | raise ValueError("u_min must be positive")
643 | if Er <= 0.0:
644 | raise ValueError("Er must be positive")
645 | if MFast <= 0.0:
646 | raise ValueError("MFast must be positive")
647 | if n0 <= 0.0:
648 | raise ValueError("n0 must be positive")
649 | if specType != 2 and p <= 2.0:
650 | raise ValueError("p must be in (2, inf)")
651 | if specType == 2 and p <= 1.0:
652 | raise ValueError("p must be in (1, inf)")
653 | if epse <= 0.0 or epse > 1.0:
654 | raise ValueError("epsilon_e must be in (0, 1]")
655 | if epsB <= 0.0 or epsB > 1.0:
656 | raise ValueError("epsilon_B must be in (0, 1]")
657 | if xiN <= 0.0 or xiN > 1.0:
658 | raise ValueError("xi_N must be in (0, 1]")
659 | if dL <= 0.0:
660 | raise ValueError("d_L must be positive")
661 |
662 | if 'z' in argsDict:
663 | if argsDict['z'] < 0.0:
664 | raise ValueError("z must be non-negative")
665 |
666 | # Energy Injection
667 | if 'L0' in argsDict:
668 | L0 = argsDict['L0']
669 | if L0 < 0.0:
670 | raise ValueError("L0 must be non-negative")
671 | if 'ts' in argsDict:
672 | ts = argsDict['ts']
673 | if ts < 0.0:
674 | raise ValueError("ts must be non-negative")
675 |
676 | # Additional Luminosity
677 | if 'LR' in argsDict:
678 | LR = argsDict['LR']
679 | if LR < 0.0:
680 | raise ValueError("LR must be non-negative")
681 | if 'LO' in argsDict:
682 | LO = argsDict['LO']
683 | if LO < 0.0:
684 | raise ValueError("LO must be non-negative")
685 | if 'LX' in argsDict:
686 | LX = argsDict['LX']
687 | if LX < 0.0:
688 | raise ValueError("LX must be non-negative")
689 |
690 | return
691 |
692 |
693 | def parseArgs(args, kwargs):
694 | r"""
695 | Parse the arguments to fluxDensity() or intensity(). Supports both
696 | positional and keyword arguments for now.
697 | """
698 |
699 | argsDict = kwargs.copy()
700 |
701 | # If there were no extra positional arguments, things are easy.
702 | if len(args) == 0:
703 | return argsDict
704 |
705 | # Recommend users not to do this
706 | warnings.warn("Positional argument inferface to afterglowpy"
707 | + " may not be supported in the future. Use keyword"
708 | + " arguments instead, see documentation for details.",
709 | FutureWarning)
710 |
711 | # Now for the fun part
712 |
713 | jetKeys = ['jetType', 'specType', 'thetaObs', 'E0', 'thetaCore',
714 | 'thetaWing', 'b', 'L0', 'q', 'ts', 'n0', 'p', 'epsilon_e',
715 | 'epsilon_B', 'xi_N', 'd_L', 'g0', 'LR', 'LO', 'LX', 'tAdd', 'z',
716 | 'envType', 'R0Env', 'kEnv', 'rho1Env']
717 | sphKeys = ['jetType', 'specType', 'uMax', 'uMin', 'Er',
718 | 'k', 'MFast_solar', 'L0', 'q', 'ts', 'n0', 'p', 'epsilon_e',
719 | 'epsilon_B', 'xi_N', 'd_L', 'g0', 'LR', 'LO', 'LX', 'tAdd', 'z',
720 | 'envType', 'R0Env', 'kEnv', 'rho1Env']
721 |
722 | jetType = args[0]
723 |
724 | if jetType == jet.Spherical:
725 | argsDict.update(zip(sphKeys, args))
726 | else:
727 | argsDict.update(zip(jetKeys, args))
728 |
729 | return argsDict
730 |
--------------------------------------------------------------------------------
/afterglowpy/integrate.h:
--------------------------------------------------------------------------------
1 | #ifndef AFTERGLOWPY_INTEGRATE
2 | #define AFTERGLOWPY_INTEGRATE
3 |
4 | #include "interval.h"
5 |
6 | /*
7 | * Various routines for integrating 1D functions.
8 | * trap() and simp() are fixed stencil implementations of the Trapezoid Rule
9 | * and Simpson's Rule respectively.
10 | *
11 | * romb() is an adaptive Romberg integrator.
12 | *
13 | * trap_adapt() and simp_adapt() are globally adaptive integrators based on
14 | * the Trapezoid and Simpson's rule, respectively. They successively bisect
15 | * the integration domain into subintervals, prioritizing the subintervals
16 | * with largest (estimated) error, until the total absolute error estimate
17 | * is within tolerance.
18 | */
19 |
20 | /*
21 | * Integration routines
22 | */
23 | double trap(double (*f)(double, void *), double xa, double xb, int N,
24 | void *args, int (*errf)(void *));
25 | double simp(double (*f)(double, void *), double xa, double xb, int N,
26 | void *args, int (*errf)(void *));
27 | double romb(double (*f)(double, void *), double xa, double xb, int N,
28 | double atol, double rtol, void *args, int *Neval, double *eps,
29 | int verbose, int (*errf)(void *), double *pfa, double *pfb);
30 |
31 | double trap_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
32 | double atol, double rtol, void *args, int *Neval,
33 | double *eps, struct Mesh3 *mout, int verbose,
34 | int (*errf)(void *), double *pfa, double *pfb);
35 | double simp_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
36 | double atol, double rtol, void *args, int *Neval,
37 | double *eps, struct Mesh5 *mout, int verbose,
38 | int (*errf)(void *), double *pfa, double *pfb);
39 | double trapNL_adapt(double (*f)(double, void *), double xa, double xb,int Nmax,
40 | double atol, double rtol, void *args, int *Neval,
41 | double *eps, struct Mesh5 *mout, int verbose,
42 | int (*errf)(void *), double *pfa, double *pfb);
43 | double hybrid_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
44 | double atol, double rtol, void *args, int *Neval,
45 | double *eps, int verbose, int (*errf)(void *),
46 | double *pfa, double *pfb);
47 | double cadre_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
48 | double atol, double rtol, void *args, int *Neval,
49 | double *eps, int verbose, int (*errf)(void *),
50 | double *pfa, double *pfb, Mesh9 *mout);
51 | double gk49_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
52 | double atol, double rtol, void *args, int *Neval,
53 | double *eps, int verbose, int (*errf)(void *));
54 | double gk715_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
55 | double atol, double rtol, void *args, int *Neval,
56 | double *eps, int verbose, int (*errf)(void *));
57 | double gk1021_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
58 | double atol, double rtol, void *args, int *Neval,
59 | double *eps, int verbose, int (*errf)(void *));
60 |
61 | /*
62 | * Internal functions for trap_adapt and simp_adapt.
63 | */
64 |
65 | double m_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
66 | int (*processInterval)(double (*f)(double, void*), void *,
67 | Interval *, int (*errf)(void *)),
68 | int (*splitInterval)(double (*f)(double, void *), void *,
69 | Interval *, Interval *, Interval *,
70 | int (*errf)(void *)),
71 | double atol, double rtol, void *args, int *Neval,
72 | double *eps, struct Mesh *mout, int verbose,
73 | int (*errf)(void *));
74 | double m3_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
75 | int (*initInterval)(double (*f)(double, void*), void *,
76 | Interval3 *, int (*errf)(void *),
77 | double *pfa, double *pfb),
78 | int (*processInterval)(double (*f)(double, void*), void *,
79 | Interval3 *, int (*errf)(void *)),
80 | int (*splitInterval)(double (*f)(double, void *), void *,
81 | Interval3 *, Interval3 *, Interval3 *,
82 | int (*errf)(void *)),
83 | double atol, double rtol, void *args, int *Neval,
84 | double *eps, struct Mesh3 *mout, int verbose,
85 | int (*errf)(void *), double *pfa, double *pfb);
86 | double m5_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
87 | int (*initInterval)(double (*f)(double, void*), void *,
88 | Interval5 *, int (*errf)(void *),
89 | double *pfa, double *pfb),
90 | int (*processInterval)(double (*f)(double, void*), void *,
91 | Interval5 *, int (*errf)(void *)),
92 | int (*splitInterval)(double (*f)(double, void *), void *,
93 | Interval5 *, Interval5 *, Interval5 *,
94 | int (*errf)(void *)),
95 | double atol, double rtol, void *args, int *Neval,
96 | double *eps, struct Mesh5 *mout, int verbose,
97 | int (*errf)(void *), double *pfa, double *pbf);
98 | double m9_adapt(double (*f)(double, void *), double xa, double xb, int Nmax,
99 | int (*initInterval)(double (*f)(double, void*), void *,
100 | Interval9 *, int (*errf)(void *),
101 | double *pfa, double *pfb),
102 | int (*processInterval)(double (*f)(double, void*), void *,
103 | Interval9 *, int (*errf)(void *)),
104 | int (*splitInterval)(double (*f)(double, void *), void *,
105 | Interval9 *, Interval9 *, Interval9 *,
106 | int (*errf)(void *)),
107 | double atol, double rtol, void *args, int *Neval,
108 | double *eps, struct Mesh9 *mout, int verbose,
109 | int (*errf)(void *), double *pfa, double *pbf);
110 |
111 | int trapInitInterval(double (*f)(double, void *), void *args, Interval3 *i,
112 | int (*errf)(void *), double *pfa, double *pfb);
113 | int trapProcessInterval(double (*f)(double, void *), void *args, Interval3 *i,
114 | int (*errf)(void *));
115 | int trapSplitInterval(double (*f)(double, void *), void *args,
116 | Interval3 *i0, Interval3 *i1, Interval3 *i2,
117 | int (*errf)(void *));
118 |
119 | int simpInitInterval(double (*f)(double, void *), void *args, Interval5 *i,
120 | int (*errf)(void *), double *pfa, double *pfb);
121 | int simpProcessInterval(double (*f)(double, void *), void *args, Interval5 *i,
122 | int (*errf)(void *));
123 | int simpSplitInterval(double (*f)(double, void *), void *args,
124 | Interval5 *i0, Interval5 *i1, Interval5 *i2,
125 | int (*errf)(void *));
126 |
127 | int trapNLInitInterval(double (*f)(double, void *), void *args, Interval5 *i,
128 | int (*errf)(void *), double *pfa, double *pfb);
129 | int trapNLProcessInterval(double (*f)(double, void *), void *args,
130 | Interval5 *i, int (*errf)(void *));
131 | int trapNLSplitInterval(double (*f)(double, void *), void *args,
132 | Interval5 *i0, Interval5 *i1, Interval5 *i2,
133 | int (*errf)(void *));
134 |
135 | int cadreInitInterval(double (*f)(double, void *), void *args, Interval9 *i,
136 | int (*errf)(void *), double *pfa, double *pfb);
137 | int cadreProcessInterval(double (*f)(double, void *), void *args,
138 | Interval9 *i, int (*errf)(void *));
139 | int cadreSplitInterval(double (*f)(double, void *), void *args,
140 | Interval9 *i0, Interval9 *i1, Interval9 *i2,
141 | int (*errf)(void *));
142 |
143 | int gk49ProcessInterval(double (*f)(double, void *), void *args,
144 | Interval *i, int (*errf)(void *));
145 | int gk49SplitInterval(double (*f)(double, void *), void *args,
146 | Interval *i0, Interval *i1, Interval *i2,
147 | int (*errf)(void *));
148 |
149 | int gk715ProcessInterval(double (*f)(double, void *), void *args,
150 | Interval *i, int (*errf)(void *));
151 | int gk715SplitInterval(double (*f)(double, void *), void *args,
152 | Interval *i0, Interval *i1, Interval *i2,
153 | int (*errf)(void *));
154 |
155 | int gk1021ProcessInterval(double (*f)(double, void *), void *args,
156 | Interval *i, int (*errf)(void *));
157 | int gk1021SplitInterval(double (*f)(double, void *), void *args,
158 | Interval *i0, Interval *i1, Interval *i2,
159 | int (*errf)(void *));
160 |
161 | int gk_compute(double (*f)(double, void *), void *args, int (*errf)(void *),
162 | double c, double z0, const double xg[], const double xk[],
163 | const double wg[], const double wgk[], int ng,
164 | double *I, double *err);
165 | #endif
166 |
--------------------------------------------------------------------------------
/afterglowpy/interval.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "interval.h"
4 |
5 | /*
6 | * Here are 3 helper structs for performing global adaptive integration.
7 | * Each Mesh struct is a priority queue implemented with as a simple
8 | * binary heap. Elements may be added, and the element with the worst error can
9 | * be retrieved.
10 | *
11 | * Each mesh is a container of Intervals. An Interval is a primitive struct
12 | * contains only its left and right bounds, the value of the integral and its
13 | * error over that interval. The Interval3 and Interval5 are identical to
14 | * Interval but contain 3 (or 5) extra slots to contain function memorized
15 | * function values.
16 | *
17 | * The Mesh3 and Mesh5 are priority queues of Interval3s and Interval5s
18 | * respectively.
19 | */
20 |
21 | /*** Mesh ***/
22 |
23 | void meshInit(struct Mesh *m)
24 | {
25 | size_t size = 4;
26 | m->totalSize = size;
27 | m->N = 0;
28 | m->heap = (struct Interval *)malloc(size * sizeof(struct Interval));
29 | }
30 |
31 | void meshFree(struct Mesh *m)
32 | {
33 | m->totalSize = 0;
34 | m->N = 0;
35 | free(m->heap);
36 | m->heap = NULL;
37 | }
38 |
39 | void meshInsert(struct Mesh *m, struct Interval *i)
40 | {
41 | // Resize if necessary
42 | while(m->N >= m->totalSize)
43 | {
44 | m->totalSize *= 2;
45 | m->heap = (struct Interval *)realloc(m->heap,
46 | m->totalSize * sizeof(struct Interval));
47 | }
48 | // Add interval to end of heap
49 | m->heap[m->N] = *i;
50 | (m->N)++;
51 |
52 | // Restore ordering
53 | meshHeapifyUp(m);
54 | }
55 |
56 | void meshExtract(struct Mesh *m, struct Interval *worst)
57 | {
58 | *worst = m->heap[0];
59 |
60 | m->heap[0] = m->heap[m->N-1];
61 | (m->N)--;
62 |
63 | meshHeapifyDown(m);
64 |
65 | }
66 |
67 | double meshTotalIntegral(struct Mesh *m)
68 | {
69 | double I = 0.0;
70 |
71 | size_t i = 0;
72 | for(i=0; iN; i++)
73 | I += m->heap[i].I;
74 |
75 | return I;
76 | }
77 |
78 | double meshTotalError(struct Mesh *m)
79 | {
80 | double err = 0.0;
81 |
82 | size_t i = 0;
83 | for(i=0; iN; i++)
84 | err += m->heap[i].err;
85 |
86 | return err;
87 | }
88 |
89 | void meshHeapifyUp(struct Mesh *m)
90 | {
91 | size_t c = m->N-1;
92 | size_t p = (c-1)/2;
93 |
94 | while(c != 0 && m->heap[p].err < m->heap[c].err)
95 | {
96 | struct Interval tempP = m->heap[p];
97 | m->heap[p] = m->heap[c];
98 | m->heap[c] = tempP;
99 | c = p;
100 | p = (c-1)/2;
101 | }
102 | }
103 |
104 | void meshHeapifyDown(struct Mesh *m)
105 | {
106 | size_t p = 0;
107 | size_t c1 = 2*p + 1;
108 | size_t c2 = c1 + 1;
109 |
110 |
111 | while(c1 < m->N)
112 | {
113 | //Find the child with largest error
114 | size_t c = c1;
115 | double e = m->heap[c1].err;
116 |
117 | if(c2 < m->N && m->heap[c2].err > e)
118 | {
119 | c = c2;
120 | e = m->heap[c2].err;
121 | }
122 |
123 | // If the child is already in order then we're done.
124 | if(e <= m->heap[p].err)
125 | break;
126 |
127 | // Otherwise, swap with the child.
128 | struct Interval tempP = m->heap[p];
129 | m->heap[p] = m->heap[c];
130 | m->heap[c] = tempP;
131 | p = c;
132 | c1 = 2*p + 1;
133 | c2 = c1 + 1;
134 | }
135 | }
136 |
137 | int meshCheck(struct Mesh *m)
138 | {
139 | size_t p;
140 | for(p=0; p<=(m->N-2)/2; p++)
141 | {
142 | size_t c1 = 2*p+1;
143 | size_t c2 = c1 + 1;
144 |
145 | if(c1 < m->N && m->heap[c1].err > m->heap[p].err)
146 | return 0;
147 |
148 | if(c2 < m->N && m->heap[c2].err > m->heap[p].err)
149 | return 0;
150 |
151 | }
152 |
153 | return 1;
154 | }
155 |
156 | void meshWrite(struct Mesh *m, char **buf)
157 | {
158 | *buf = (char *) malloc((m->N * 4 * 30 + 12) * sizeof(char));
159 |
160 | size_t i;
161 | int c = sprintf(*buf, "%lu", m->N);
162 | for(i=0; i < m->N; i++)
163 | {
164 | struct Interval *in = &(m->heap[i]);
165 | c += sprintf(*buf + c, " %.16e %.16e %.16e %.16e",
166 | in->a, in->b, in->I, in->err);
167 | }
168 | *buf = (char *) realloc(*buf, (c+1) * sizeof(char));
169 | }
170 |
171 | /*** Mesh 3 ***/
172 |
173 | void mesh3Init(struct Mesh3 *m)
174 | {
175 | size_t size = 4;
176 | m->totalSize = size;
177 | m->N = 0;
178 | m->heap = (struct Interval3 *)malloc(size * sizeof(struct Interval3));
179 | }
180 |
181 | void mesh3Free(struct Mesh3 *m)
182 | {
183 | m->totalSize = 0;
184 | m->N = 0;
185 | free(m->heap);
186 | m->heap = NULL;
187 | }
188 |
189 | void mesh3Insert(struct Mesh3 *m, struct Interval3 *i)
190 | {
191 | // Resize if necessary
192 | while(m->N >= m->totalSize)
193 | {
194 | m->totalSize *= 2;
195 | m->heap = (struct Interval3 *)realloc(m->heap,
196 | m->totalSize * sizeof(struct Interval3));
197 | }
198 | // Add interval to end of heap
199 | m->heap[m->N] = *i;
200 | (m->N)++;
201 |
202 | // Restore ordering
203 | mesh3HeapifyUp(m);
204 | }
205 |
206 | void mesh3Extract(struct Mesh3 *m, struct Interval3 *worst)
207 | {
208 | *worst = m->heap[0];
209 |
210 | m->heap[0] = m->heap[m->N-1];
211 | (m->N)--;
212 |
213 | mesh3HeapifyDown(m);
214 | }
215 |
216 | double mesh3TotalIntegral(struct Mesh3 *m)
217 | {
218 | double I = 0.0;
219 |
220 | size_t i = 0;
221 | for(i=0; iN; i++)
222 | I += m->heap[i].I;
223 |
224 | return I;
225 | }
226 |
227 | double mesh3TotalError(struct Mesh3 *m)
228 | {
229 | double err = 0.0;
230 |
231 | size_t i = 0;
232 | for(i=0; iN; i++)
233 | err += m->heap[i].err;
234 |
235 | return err;
236 | }
237 |
238 | void mesh3HeapifyUp(struct Mesh3 *m)
239 | {
240 | size_t c = m->N-1;
241 | size_t p = (c-1)/2;
242 |
243 | while(c != 0 && m->heap[p].err < m->heap[c].err)
244 | {
245 | struct Interval3 tempP = m->heap[p];
246 | m->heap[p] = m->heap[c];
247 | m->heap[c] = tempP;
248 | c = p;
249 | p = (c-1)/2;
250 | }
251 | }
252 |
253 | void mesh3HeapifyDown(struct Mesh3 *m)
254 | {
255 | size_t p = 0;
256 | size_t c1 = 2*p + 1;
257 | size_t c2 = c1 + 1;
258 |
259 |
260 | while(c1 < m->N)
261 | {
262 | //Find the child with largest error
263 | size_t c = c1;
264 | double e = m->heap[c1].err;
265 |
266 | if(c2 < m->N && m->heap[c2].err > e)
267 | {
268 | c = c2;
269 | e = m->heap[c2].err;
270 | }
271 |
272 | // If the child is already in order then we're done.
273 | if(e <= m->heap[p].err)
274 | break;
275 |
276 | // Otherwise, swap with the child.
277 | struct Interval3 tempP = m->heap[p];
278 | m->heap[p] = m->heap[c];
279 | m->heap[c] = tempP;
280 | p = c;
281 | c1 = 2*p + 1;
282 | c2 = c1 + 1;
283 | }
284 | }
285 |
286 | int mesh3Check(struct Mesh3 *m)
287 | {
288 | size_t p;
289 | for(p=0; p<=(m->N-2)/2; p++)
290 | {
291 | size_t c1 = 2*p+1;
292 | size_t c2 = c1 + 1;
293 |
294 | if(c1 < m->N && m->heap[c1].err > m->heap[p].err)
295 | return 0;
296 |
297 | if(c2 < m->N && m->heap[c2].err > m->heap[p].err)
298 | return 0;
299 |
300 | }
301 |
302 | return 1;
303 | }
304 |
305 | void mesh3Write(struct Mesh3 *m, char **buf)
306 | {
307 | *buf = (char *) malloc((m->N * 4 * 30 + 12) * sizeof(char));
308 |
309 | size_t i;
310 | int c = sprintf(*buf, "%lu", m->N);
311 | for(i=0; i < m->N; i++)
312 | {
313 | struct Interval3 *in = &(m->heap[i]);
314 | c += sprintf(*buf + c, " %.16e %.16e %.16e %.16e",
315 | in->a, in->b, in->I, in->err);
316 | }
317 | *buf = (char *) realloc(*buf, (c+1) * sizeof(char));
318 | }
319 |
320 | /*** Mesh 5 ***/
321 |
322 | void mesh5Init(struct Mesh5 *m)
323 | {
324 | size_t size = 4;
325 | m->totalSize = size;
326 | m->N = 0;
327 | m->heap = (struct Interval5 *)malloc(size * sizeof(struct Interval5));
328 | }
329 |
330 | void mesh5Free(struct Mesh5 *m)
331 | {
332 | m->totalSize = 0;
333 | m->N = 0;
334 | free(m->heap);
335 | m->heap = NULL;
336 | }
337 |
338 | void mesh5Insert(struct Mesh5 *m, struct Interval5 *i)
339 | {
340 | // Resize if necessary
341 | while(m->N >= m->totalSize)
342 | {
343 | m->totalSize *= 2;
344 | m->heap = (struct Interval5 *)realloc(m->heap,
345 | m->totalSize * sizeof(struct Interval5));
346 | }
347 | // Add interval to end of heap
348 | m->heap[m->N] = *i;
349 | (m->N)++;
350 |
351 | // Restore ordering
352 | mesh5HeapifyUp(m);
353 | }
354 |
355 | void mesh5Extract(struct Mesh5 *m, struct Interval5 *worst)
356 | {
357 | *worst = m->heap[0];
358 |
359 | m->heap[0] = m->heap[m->N-1];
360 | (m->N)--;
361 |
362 | mesh5HeapifyDown(m);
363 | }
364 |
365 | double mesh5TotalIntegral(struct Mesh5 *m)
366 | {
367 | double I = 0.0;
368 |
369 | size_t i = 0;
370 | for(i=0; iN; i++)
371 | I += m->heap[i].I;
372 |
373 | return I;
374 | }
375 |
376 | double mesh5TotalError(struct Mesh5 *m)
377 | {
378 | double err = 0.0;
379 |
380 | size_t i = 0;
381 | for(i=0; iN; i++)
382 | err += m->heap[i].err;
383 |
384 | return err;
385 | }
386 |
387 | void mesh5HeapifyUp(struct Mesh5 *m)
388 | {
389 | size_t c = m->N-1;
390 | size_t p = (c-1)/2;
391 |
392 | while(c != 0 && m->heap[p].err < m->heap[c].err)
393 | {
394 | struct Interval5 tempP = m->heap[p];
395 | m->heap[p] = m->heap[c];
396 | m->heap[c] = tempP;
397 | c = p;
398 | p = (c-1)/2;
399 | }
400 | }
401 |
402 | void mesh5HeapifyDown(struct Mesh5 *m)
403 | {
404 | size_t p = 0;
405 | size_t c1 = 2*p + 1;
406 | size_t c2 = c1 + 1;
407 |
408 |
409 | while(c1 < m->N)
410 | {
411 | //Find the child with largest error
412 | size_t c = c1;
413 | double e = m->heap[c1].err;
414 |
415 | if(c2 < m->N && m->heap[c2].err > e)
416 | {
417 | c = c2;
418 | e = m->heap[c2].err;
419 | }
420 |
421 | // If the child is already in order then we're done.
422 | if(e <= m->heap[p].err)
423 | break;
424 |
425 | // Otherwise, swap with the child.
426 | struct Interval5 tempP = m->heap[p];
427 | m->heap[p] = m->heap[c];
428 | m->heap[c] = tempP;
429 | p = c;
430 | c1 = 2*p + 1;
431 | c2 = c1 + 1;
432 | }
433 | }
434 |
435 | int mesh5Check(struct Mesh5 *m)
436 | {
437 | size_t p;
438 | for(p=0; p<=(m->N-2)/2; p++)
439 | {
440 | size_t c1 = 2*p+1;
441 | size_t c2 = c1 + 1;
442 |
443 | if(c1 < m->N && m->heap[c1].err > m->heap[p].err)
444 | return 0;
445 |
446 | if(c2 < m->N && m->heap[c2].err > m->heap[p].err)
447 | return 0;
448 |
449 | }
450 |
451 | return 1;
452 | }
453 |
454 | void mesh5Write(struct Mesh5 *m, char **buf)
455 | {
456 | *buf = (char *) malloc((m->N * 4 * 30 + 12) * sizeof(char));
457 |
458 | size_t i;
459 | int c = sprintf(*buf, "%lu", m->N);
460 | for(i=0; i < m->N; i++)
461 | {
462 | struct Interval5 *in = &(m->heap[i]);
463 | c += sprintf(*buf + c, " %.16e %.16e %.16e %.16e",
464 | in->a, in->b, in->I, in->err);
465 | }
466 | *buf = (char *) realloc(*buf, (c+1) * sizeof(char));
467 | }
468 |
469 | /*** Mesh 9 ***/
470 |
471 | void mesh9Init(struct Mesh9 *m)
472 | {
473 | size_t size = 4;
474 | m->totalSize = size;
475 | m->N = 0;
476 | m->heap = (struct Interval9 *)malloc(size * sizeof(struct Interval9));
477 | }
478 |
479 | void mesh9Free(struct Mesh9 *m)
480 | {
481 | m->totalSize = 0;
482 | m->N = 0;
483 | if(m->heap != NULL)
484 | {
485 | free(m->heap);
486 | m->heap = NULL;
487 | }
488 | }
489 |
490 | void mesh9Insert(struct Mesh9 *m, struct Interval9 *i)
491 | {
492 | // Resize if necessary
493 | while(m->N >= m->totalSize)
494 | {
495 | m->totalSize *= 2;
496 | m->heap = (struct Interval9 *)realloc(m->heap,
497 | m->totalSize * sizeof(struct Interval9));
498 | }
499 | // Add interval to end of heap
500 | m->heap[m->N] = *i;
501 | (m->N)++;
502 |
503 | // Restore ordering
504 | mesh9HeapifyUp(m);
505 | }
506 |
507 | void mesh9Extract(struct Mesh9 *m, struct Interval9 *worst)
508 | {
509 | *worst = m->heap[0];
510 |
511 | m->heap[0] = m->heap[m->N-1];
512 | (m->N)--;
513 |
514 | mesh9HeapifyDown(m);
515 | }
516 |
517 | double mesh9TotalIntegral(struct Mesh9 *m)
518 | {
519 | double I = 0.0;
520 |
521 | size_t i = 0;
522 | for(i=0; iN; i++)
523 | I += m->heap[i].I;
524 |
525 | return I;
526 | }
527 |
528 | double mesh9TotalError(struct Mesh9 *m)
529 | {
530 | double err = 0.0;
531 |
532 | size_t i = 0;
533 | for(i=0; iN; i++)
534 | err += m->heap[i].err;
535 |
536 | return err;
537 | }
538 |
539 | void mesh9HeapifyUp(struct Mesh9 *m)
540 | {
541 | size_t c = m->N-1;
542 | size_t p = (c-1)/2;
543 |
544 | while(c != 0 && m->heap[p].err < m->heap[c].err)
545 | {
546 | struct Interval9 tempP = m->heap[p];
547 | m->heap[p] = m->heap[c];
548 | m->heap[c] = tempP;
549 | c = p;
550 | p = (c-1)/2;
551 | }
552 | }
553 |
554 | void mesh9HeapifyDown(struct Mesh9 *m)
555 | {
556 | size_t p = 0;
557 | size_t c1 = 2*p + 1;
558 | size_t c2 = c1 + 1;
559 |
560 |
561 | while(c1 < m->N)
562 | {
563 | //Find the child with largest error
564 | size_t c = c1;
565 | double e = m->heap[c1].err;
566 |
567 | if(c2 < m->N && m->heap[c2].err > e)
568 | {
569 | c = c2;
570 | e = m->heap[c2].err;
571 | }
572 |
573 | // If the child is already in order then we're done.
574 | if(e <= m->heap[p].err)
575 | break;
576 |
577 | // Otherwise, swap with the child.
578 | struct Interval9 tempP = m->heap[p];
579 | m->heap[p] = m->heap[c];
580 | m->heap[c] = tempP;
581 | p = c;
582 | c1 = 2*p + 1;
583 | c2 = c1 + 1;
584 | }
585 | }
586 |
587 | int mesh9Check(struct Mesh9 *m)
588 | {
589 | size_t p;
590 | if(m->N <= 1)
591 | return 1;
592 |
593 | for(p=0; p<=(m->N-2)/2; p++)
594 | {
595 | size_t c1 = 2*p+1;
596 | size_t c2 = c1 + 1;
597 |
598 | if(c1 < m->N && m->heap[c1].err > m->heap[p].err)
599 | return 0;
600 |
601 | if(c2 < m->N && m->heap[c2].err > m->heap[p].err)
602 | return 0;
603 |
604 | }
605 |
606 | return 1;
607 | }
608 |
609 | void mesh9Write(struct Mesh9 *m, char **buf)
610 | {
611 | *buf = (char *) malloc((m->N * 4 * 30 + 12) * sizeof(char));
612 |
613 | size_t i;
614 | int c = sprintf(*buf, "%lu", m->N);
615 | for(i=0; i < m->N; i++)
616 | {
617 | struct Interval9 *in = &(m->heap[i]);
618 | c += sprintf(*buf + c, " %.16e %.16e %.16e %.16e",
619 | in->a, in->b, in->I, in->err);
620 | }
621 | *buf = (char *) realloc(*buf, (c+1) * sizeof(char));
622 | }
623 |
624 | void interval9Write(struct Interval9 *i, FILE *stream)
625 | {
626 | fprintf(stream, "(%.3le, %.3le) %.12le +/- %.3le %d\n",
627 | i->a, i->b, i->I, i->err, i->refinement);
628 | fprintf(stream, " [%.6le %.6le %.6le %.6le %.6le %.6le"
629 | " %.6le %.6le %.6le]\n", i->fa, i->fll, i->fl, i->flr,
630 | i->fm, i->frl, i->fr, i->frr, i->fb);
631 | }
632 |
--------------------------------------------------------------------------------
/afterglowpy/interval.h:
--------------------------------------------------------------------------------
1 | #ifndef AFTERGLOWPY_INTERVAL
2 | #define AFTERGLOWPY_INTERVAL
3 |
4 | /*
5 | * Here are 3 helper structs for performing global adaptive integration.
6 | * Each Mesh struct is a priority queue implemented with as a simple
7 | * binary heap. Elements may be added, and the element with the worst error can
8 | * be retrieved.
9 | *
10 | * Each mesh is a container of Intervals. An Interval is a primitive struct
11 | * contains only its left and right bounds, the value of the integral and its
12 | * error over that interval. The Interval3 and Interval5 are identical to
13 | * Interval but contain 3 (or 5) extra slots to contain function memorized
14 | * function values.
15 | *
16 | * The Mesh3 and Mesh5 are priority queues of Interval3s and Interval5s
17 | * respectively.
18 | */
19 |
20 | #undef I
21 |
22 | struct Interval
23 | {
24 | double a;
25 | double b;
26 | double I;
27 | double err;
28 | };
29 | typedef struct Interval Interval;
30 |
31 | struct Mesh
32 | {
33 | size_t totalSize;
34 | size_t N;
35 | struct Interval *heap;
36 | };
37 | typedef struct Mesh Mesh;
38 |
39 | struct Interval3
40 | {
41 | double a;
42 | double b;
43 | double I;
44 | double err;
45 | double fa;
46 | double fb;
47 | double fm;
48 | };
49 | typedef struct Interval3 Interval3;
50 |
51 | struct Mesh3
52 | {
53 | size_t totalSize;
54 | size_t N;
55 | struct Interval3 *heap;
56 | };
57 | typedef struct Mesh3 Mesh3;
58 |
59 | struct Interval5
60 | {
61 | double a;
62 | double b;
63 | double I;
64 | double err;
65 | double fa;
66 | double fb;
67 | double fl;
68 | double fm;
69 | double fr;
70 | };
71 | typedef struct Interval5 Interval5;
72 |
73 | struct Mesh5
74 | {
75 | size_t totalSize;
76 | size_t N;
77 | struct Interval5 *heap;
78 | };
79 | typedef struct Mesh5 Mesh5;
80 |
81 | struct Interval9
82 | {
83 | double a;
84 | double b;
85 | double I;
86 | double err;
87 | double fa;
88 | double fll;
89 | double fl;
90 | double flr;
91 | double fm;
92 | double frl;
93 | double fr;
94 | double frr;
95 | double fb;
96 | int refinement;
97 | };
98 | typedef struct Interval9 Interval9;
99 |
100 | struct Mesh9
101 | {
102 | size_t totalSize;
103 | size_t N;
104 | struct Interval9 *heap;
105 | };
106 | typedef struct Mesh9 Mesh9;
107 |
108 | void meshInit(struct Mesh *m);
109 | void meshFree(struct Mesh *m);
110 | void meshInsert(struct Mesh *m, struct Interval *i);
111 | void meshExtract(struct Mesh *m, struct Interval *worst);
112 | double meshTotalIntegral(struct Mesh *m);
113 | double meshTotalError(struct Mesh *m);
114 | void meshHeapifyUp(struct Mesh *m);
115 | void meshHeapifyDown(struct Mesh *m);
116 | int meshCheck(struct Mesh *m);
117 | void meshWrite(struct Mesh *m, char **buf);
118 |
119 | void mesh3Init(struct Mesh3 *m);
120 | void mesh3Free(struct Mesh3 *m);
121 | void mesh3Insert(struct Mesh3 *m, struct Interval3 *i);
122 | void mesh3Extract(struct Mesh3 *m, struct Interval3 *worst);
123 | double mesh3TotalIntegral(struct Mesh3 *m);
124 | double mesh3TotalError(struct Mesh3 *m);
125 | void mesh3HeapifyUp(struct Mesh3 *m);
126 | void mesh3HeapifyDown(struct Mesh3 *m);
127 | int mesh3Check(struct Mesh3 *m);
128 | void mesh3Write(struct Mesh3 *m, char **buf);
129 |
130 | void mesh5Init(struct Mesh5 *m);
131 | void mesh5Free(struct Mesh5 *m);
132 | void mesh5Insert(struct Mesh5 *m, struct Interval5 *i);
133 | void mesh5Extract(struct Mesh5 *m, struct Interval5 *worst);
134 | double mesh5TotalIntegral(struct Mesh5 *m);
135 | double mesh5TotalError(struct Mesh5 *m);
136 | void mesh5HeapifyUp(struct Mesh5 *m);
137 | void mesh5HeapifyDown(struct Mesh5 *m);
138 | int mesh5Check(struct Mesh5 *m);
139 | void mesh5Write(struct Mesh5 *m, char **buf);
140 |
141 | void mesh9Init(struct Mesh9 *m);
142 | void mesh9Free(struct Mesh9 *m);
143 | void mesh9Insert(struct Mesh9 *m, struct Interval9 *i);
144 | void mesh9Extract(struct Mesh9 *m, struct Interval9 *worst);
145 | double mesh9TotalIntegral(struct Mesh9 *m);
146 | double mesh9TotalError(struct Mesh9 *m);
147 | void mesh9HeapifyUp(struct Mesh9 *m);
148 | void mesh9HeapifyDown(struct Mesh9 *m);
149 | int mesh9Check(struct Mesh9 *m);
150 | void mesh9Write(struct Mesh9 *m, char **buf);
151 |
152 | void interval9Write(struct Interval9 *i, FILE *stream);
153 | #endif
154 |
--------------------------------------------------------------------------------
/afterglowpy/offaxis_struct.h:
--------------------------------------------------------------------------------
1 | #ifndef AFTERGLOWPY_STRUCT
2 | #define AFTERGLOWPY_STRUCT
3 |
4 | // offaxis.h
5 |
6 | #include
7 | #include
8 | #include
9 | #include "integrate.h"
10 | #include "interval.h"
11 |
12 | #define ERR_CHK_VOID(pars) if(pars->error){ return;}
13 | #define ERR_CHK_INT(pars) if(pars->error){ return 0;}
14 | #define ERR_CHK_DBL(pars) if(pars->error){ return 0.0;}
15 | #define MSG_LEN 4096
16 | #define DUMP_MSG_LEN_MAX 16384 //overkill: 200 lines * 80c per line = 16000
17 |
18 | #ifndef M_PI
19 | #define M_PI 3.14159265358979323846
20 | #endif
21 |
22 | // some physical and mathematical constants
23 | #define PI 3.14159265358979323846
24 | #define v_light 2.99792458e10 // speed of light in cm / s
25 | #define invv_light 3.335640952e-11 // inverse speed of light s / cm
26 | #define m_e 9.1093897e-28 // electron mass in g
27 | #define m_p 1.6726231e-24 // proton mass in g
28 | #define invm_e 1.097768383e27 // inverse electron mass in 1/g
29 | #define invm_p 5.978633202e23 // inverse proton mass in 1/g
30 | #define h_planck 6.6260755e-27 // Planck's constant in erg / s
31 | #define h_bar 1.05457266e-27 // Planck's constant / 2 PI in erg /s
32 | #define k_B 1.380658e-16 // Boltzmann's constant in erg / K
33 | #define e_e 4.803e-10 // electron charge in Gaussian cgs units
34 | #define sigma_T 6.65e-25 // Thomson cross section free electron cm^2
35 | #define cgs2mJy 1e26 // quantity in cgs to mJy
36 | #define mJy2cgs 1e-26 // quantity in mJy to cgs
37 | #define deg2rad 0.017453292 // quantity in degrees to radians
38 | #define rad2deg 57.29577951 // quantity in radians to degrees
39 | #define sec2day 0.000011574 // quantity in seconds to days
40 | #define day2sec 86400 // quantity in days to seconds
41 | #define parsec 3.0857e18 // quantity in parsec to cm
42 | #define Hz2eV 4.13566553853599E-15
43 | #define eV2Hz 2.417991e+14
44 |
45 | #define _cone -2
46 | #define _tophat -1
47 | #define _Gaussian 0
48 | #define _powerlaw_core 1 //has a core as well
49 | #define _Gaussian_core 2 // has a core as well
50 | #define _spherical 3
51 | #define _powerlaw 4
52 | #define _exponential 5
53 | #define _twocomponent 6
54 | #define _exponential2 7
55 |
56 | #define SIMPLE_SPEC 0
57 | #define IC_COOLING_FLAG 1
58 | #define EPS_E_BAR_FLAG 2
59 | #define SSA_SMOOTH_FLAG 4
60 | #define SSA_SHARP_FLAG 8
61 | #define NO_COOLING_FLAG 16
62 | #define DEEP_NEWTONIAN_FLAG 32
63 | #define BULK_BM_FLAG 64
64 | #define FIXED_PL_FLAG 128
65 |
66 | enum{INT_TRAP_FIXED, INT_TRAP_ADAPT, INT_SIMP_FIXED, INT_SIMP_ADAPT,
67 | INT_ROMB_ADAPT, INT_TRAP_NL, INT_HYBRID, INT_CADRE,
68 | INT_GK49_ADAPT, INT_GK715_ADAPT, INT_GK1021_ADAPT,
69 | INT_UNDEFINED};
70 |
71 | enum{GAMMA_INF, GAMMA_FLAT, GAMMA_EVENMASS, GAMMA_STRUCT};
72 |
73 | enum{MOM_0, MOM_X, MOM_Y, MOM_Z, MOM_XX, MOM_YY, MOM_ZZ,
74 | MOM_XY, MOM_YZ, MOM_XZ};
75 |
76 | struct fluxParams
77 | {
78 | double theta;
79 | double phi;
80 | double cp;
81 | double sp;
82 | double ct;
83 | double st;
84 | double cto;
85 | double sto;
86 |
87 | double theta_obs;
88 | double t_obs;
89 | double nu_obs;
90 | double d_L;
91 | long moment;
92 |
93 | double E_iso;
94 | double n_0;
95 | double g_init;
96 |
97 | double p;
98 | double epsilon_E;
99 | double epsilon_B;
100 | double ksi_N;
101 |
102 | double theta_h;
103 | double E_iso_core;
104 | double theta_core;
105 | double theta_wing;
106 | double b;
107 | double E_tot;
108 | double g_core;
109 | double E_core_global;
110 | double theta_core_global;
111 |
112 | int envType;
113 | double R0_env;
114 | double k_env;
115 | double rho1_env;
116 |
117 | double L0_inj;
118 | double q_inj;
119 | double t0_inj;
120 | double ts_inj;
121 |
122 | double current_theta_cone_hi;
123 | double current_theta_cone_low;
124 | double current_theta_b;
125 | double current_theta_a;
126 | double theta_obs_cur;
127 | int tRes;
128 | int latRes;
129 | int spread;
130 | int counterjet;
131 |
132 | int int_type;
133 | double rtol_struct;
134 | double rtol_theta;
135 | double rtol_phi;
136 | int nmax_theta;
137 | int nmax_phi;
138 |
139 | double atol_theta;
140 |
141 | double Rt0;
142 | double Rt1;
143 | double ta;
144 | double tb;
145 |
146 | double C_BMsqrd;
147 | double C_STsqrd;
148 |
149 | double t_NR;
150 | int cur_entry;
151 |
152 | double *t_table;
153 | double *R_table;
154 | double *u_table;
155 | double *th_table;
156 | double *mu_table;
157 | double *cth_table;
158 | double *sth_table;
159 | int table_entries;
160 |
161 | double *t_table_inner;
162 | double *R_table_inner;
163 | double *u_table_inner;
164 | double *th_table_inner;
165 | double *mu_table_inner;
166 | double *cth_table_inner;
167 | double *sth_table_inner;
168 | int table_entries_inner;
169 |
170 | int idx_mu_neg1;
171 | int idx_mu_pos1;
172 | int idx_mu_neg1_inner;
173 | int idx_mu_pos1_inner;
174 |
175 | Mesh9 phi_mesh;
176 | Mesh9 theta_mesh;
177 |
178 | int spec_type;
179 | int gamma_type;
180 |
181 | double (*f_E)(double, void *);
182 |
183 | double *mask;
184 | int nmask;
185 |
186 | long nevals;
187 |
188 | int error;
189 | char *error_msg;
190 | };
191 |
192 |
193 | double dmin(const double a, const double b);
194 |
195 |
196 | double f_E_tophat(double theta, void *params);
197 | double f_E_Gaussian(double theta, void *params);
198 | double f_E_powerlaw(double theta, void *params);
199 | double f_E_twocomponent(double theta, void *params);
200 | double f_E_exponential(double theta, void *params);
201 | double f_Etot_tophat(void *params);
202 | double f_Etot_Gaussian(void *params);
203 | double f_Etot_powerlaw(void *params);
204 |
205 | void make_R_table(struct fluxParams *pars);
206 | void make_mu_table(struct fluxParams *pars);
207 | double check_t_e(double t_e, double mu, double t_obs, double *mu_table, int N);
208 | int searchSorted(double x, const double *arr, int N);
209 | double interpolateLin(int a, int b, double x, double *X, double *Y, int N);
210 | double interpolateLog(int a, int b, double x, double *X, double *Y, int N);
211 | double find_jet_edge_old(double phi, double cto, double sto, double theta0,
212 | const double *a_mu, const double *a_thj, int N,
213 | int idx_mu_neg1, int idx_mu_pos1,
214 | const double *a_cthj, const double *a_sthj);
215 | double find_jet_edge(double phi, double cto, double sto, double theta0,
216 | const double *a_mu, const double *a_thj, int N,
217 | int idx_mu_neg1, int idx_mu_pos1,
218 | const double *a_cthj, const double *a_sthj);
219 | double costheta_integrand(double a_theta, void* params); // inner integral
220 | double phi_integrand(double a_phi, void* params); // outer integral
221 | double emissivity(double nu, double R, double mu, double te,
222 | double u, double us, double rho0, double Msw, double p,
223 | double epse, double epsB, double ksiN,
224 | int specType); //emissivity of
225 | // a zone.
226 |
227 | void calc_absorption_length(double R, double mu, double delta,
228 | double betaS, double uS,
229 | double *length_back, double *length_front);
230 | double absorption_integral(double Rb, double dR, double taua, double taub,
231 | int order);
232 | double absorption_integral_core(double a, double b, int order);
233 |
234 | double flux(struct fluxParams *pars, double atol); // determine flux for a given t_obs
235 |
236 | double flux_cone(double t_obs, double nu_obs, long moment,
237 | double E_iso, double theta_h,
238 | double theta_cone_low, double theta_cone_hi,
239 | double atol, struct fluxParams *pars);
240 | double intensity(double theta, double phi, double tobs, double nuobs,
241 | double theta_obs, double theta_cone_hi, double theta_cone_low,
242 | struct fluxParams *pars);
243 | void shockVals(double theta, double phi, double tobs,
244 | double *t, double *R, double *u, double *thj,
245 | double theta_obs, double theta_cone_hi, double theta_cone_low,
246 | struct fluxParams *pars);
247 | void intensity_cone(double *theta, double *phi, double *t, double *nu,
248 | double *I, int N, double E_iso_core,
249 | double theta_h_core, double theta_h_wing,
250 | struct fluxParams *pars);
251 | void intensity_struct(double *theta, double *phi, double *t, double *nu,
252 | double *I, int N,
253 | double E_iso_core,
254 | double theta_h_core, double theta_h_wing,
255 | int res_cones, double (*f_E)(double,void *),
256 | struct fluxParams *pars);
257 | void intensity_structCore(double *theta, double *phi, double *t, double *nu,
258 | double *I, int N,
259 | double E_iso_core,
260 | double theta_h_core, double theta_h_wing,
261 | int res_cones, double (*f_E)(double,void *),
262 | struct fluxParams *pars);
263 | void shockVals_cone(double *theta, double *phi, double *tobs,
264 | double *t, double *R, double *u, double *thj, int N,
265 | double E_iso_core, double theta_h_core, double theta_h_wing,
266 | struct fluxParams *pars);
267 | void shockVals_struct(double *theta, double *phi, double *tobs,
268 | double *t, double *R, double *u, double *thj, int N,
269 | double E_iso_core,
270 | double theta_h_core, double theta_h_wing,
271 | int res_cones, double (*f_E)(double,void *),
272 | struct fluxParams *pars);
273 | void shockVals_structCore(double *theta, double *phi, double *tobs,
274 | double *t, double *R, double *u, double *thj, int N,
275 | double E_iso_core,
276 | double theta_h_core, double theta_h_wing,
277 | int res_cones, double (*f_E)(double,void *),
278 | struct fluxParams *pars);
279 | void lc_tophat(double *t, double *nu, double *F, long *moment, int Nt,
280 | double E_iso, double theta_h, struct fluxParams *pars);
281 | void lc_cone(double *t, double *nu, double *F, long *moment, int Nt,
282 | double E_iso, double theta_h, double theta_wing,
283 | struct fluxParams *pars);
284 | void lc_struct(double *t, double *nu, double *F, long *moment, int Nt,
285 | double E_iso_core,
286 | double theta_h_core, double theta_h_wing,
287 | double *theta_c_arr, double *E_iso_arr,
288 | int res_cones, double (*f_E)(double,void *),
289 | struct fluxParams *pars);
290 | void lc_structCore(double *t, double *nu, double *F, long *moment, int Nt,
291 | double E_iso_core,
292 | double theta_h_core, double theta_h_wing,
293 | double *theta_c_arr, double *E_iso_arr,
294 | int res_cones, double (*f_E)(double,void *),
295 | struct fluxParams *pars);
296 |
297 | void calc_flux_density(int jet_type, int spec_type,
298 | double *t, double *nu, double *Fnu, long *moment,
299 | int N, struct fluxParams *fp);
300 | void calc_intensity(int jet_type, int spec_type, double *theta, double *phi,
301 | double *t, double *nu, double *Inu, int N,
302 | struct fluxParams *fp);
303 | void calc_shockVals(int jet_type, double *theta, double *phi, double *tobs,
304 | double *t, double *R, double *u, double *thj, int N,
305 | struct fluxParams *fp);
306 |
307 | void setup_fluxParams(struct fluxParams *pars,
308 | double d_L,
309 | double theta_obs,
310 | double E_iso_core, double theta_core, double theta_wing,
311 | double b,
312 | double L0_inj, double q_inj, double t0_inj, double ts_inj,
313 | double n_0,
314 | double p,
315 | double epsilon_E,
316 | double epsilon_B,
317 | double ksi_N,
318 | double g0,
319 | int envType, double R0_env, double k_env, double rho1_env,
320 | double E_core_global,
321 | double theta_core_global,
322 | double ta, double tb,
323 | int tRes, int latRes, int int_type,
324 | double rtol_struct, double rtol_phi, double rtol_theta,
325 | int nmax_phi, int nmax_theta,
326 | int spec_type,
327 | double *mask, int nmask,
328 | int spread, int counterjet, int gamma_type);
329 |
330 | void set_jet_params(struct fluxParams *pars, double E_iso, double theta_h);
331 | void set_obs_params(struct fluxParams *pars,
332 | double t_obs, double nu_obs, long moment,
333 | double theta_obs_cur, double current_theta_cone_hi,
334 | double current_theta_cone_low);
335 | int check_error(void *params);
336 | void set_error(struct fluxParams *pars, char msg[]);
337 | void free_fluxParams(struct fluxParams *pars);
338 |
339 | #endif
340 |
--------------------------------------------------------------------------------
/afterglowpy/shockEvolution.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "offaxis_struct.h"
4 | #include "shockEvolution.h"
5 |
6 |
7 | double shockVel(double u)
8 | {
9 | return 4*u*sqrt((u*u+1) / (8*u*u+9));
10 | }
11 |
12 | double E_inj(double te, double L0, double q, double ts)
13 | {
14 | if(te < T0_inj)
15 | return L0*te;
16 |
17 | if (te > ts)
18 | te = ts;
19 |
20 | double E = L0*T0_inj;
21 |
22 | if(q == 0.0)
23 | return L0*te;
24 | else if(q == 1.0)
25 | return E + L0*T0_inj*log(te/T0_inj);
26 | else
27 | return E + L0*T0_inj*(pow(te/T0_inj,1-q) - 1.0)/(1-q);
28 |
29 | return 0.0;
30 | }
31 |
32 | double L_inj(double te, double L0, double q, double ts)
33 | {
34 | if(te <= T0_inj)
35 | return L0;
36 | else if(te < ts)
37 | {
38 | if(q == 0.0)
39 | return L0;
40 | else
41 | return L0*pow(te/T0_inj,-q);
42 | }
43 |
44 | return 0.0;
45 | }
46 |
47 | double te_inj(double Ei, double L0, double q, double ts)
48 | {
49 | if(L0*T0_inj >= Ei)
50 | return Ei / L0;
51 | Ei -= L0*T0_inj;
52 |
53 | double te;
54 |
55 | if(q == 0)
56 | te = Ei/L0;
57 | else if (q == 1)
58 | te = T0_inj * exp(Ei/(L0*T0_inj));
59 | else
60 | te = T0_inj * pow((1-q)*Ei/(L0*T0_inj)+1, 1.0/(1-q));
61 |
62 | if(te > ts)
63 | return -1.0;
64 |
65 | return te;
66 | }
67 |
68 | double t_inj(double te, double t0, double te0, double L0, double q, double ts)
69 | {
70 | return t0*pow(1 + 16*(te-te0)/((2-q)*t0), 0.25*(2-q));
71 | }
72 |
73 |
74 | double envDensityPar(double R, struct shockParams *par)
75 | {
76 | return envDensity(R, par->envType, par->rho0_env, par->R0_env, par->k_env,
77 | par->rho1_env);
78 | }
79 |
80 | double envMassPar(double R, struct shockParams *par)
81 | {
82 | return envMass(R, par->envType, par->rho0_env, par->R0_env, par->k_env,
83 | par->rho1_env);
84 | }
85 |
86 | double envRadiusPar(double M, struct shockParams *par)
87 | {
88 | return envRadius(M, par->envType, par->rho0_env, par->R0_env, par->k_env,
89 | par->rho1_env);
90 | }
91 |
92 | double envDensity(double R, int envType, double rho0, double R0, double k,
93 | double rho1)
94 | {
95 | if(envType == ENV_ISM)
96 | return rho0;
97 | else if(envType == ENV_WIND)
98 | return rho0 * R0*R0/(R*R);
99 | else if(envType == ENV_PL)
100 | return rho0 * pow(R/R0, -k);
101 | else if(envType == ENV_STEP)
102 | return R < R0 ? rho0 : rho1;
103 | else
104 | return 0;
105 | }
106 |
107 | double envMass(double R, int envType, double rho0, double R0, double k,
108 | double rho1)
109 | {
110 | if(envType == ENV_ISM)
111 | return 4.0/3.0 * M_PI * rho0 * R*R*R;
112 | else if(envType == ENV_WIND)
113 | return 4.0*M_PI * rho0 * R0*R0 * R;
114 | else if(envType == ENV_PL)
115 | return 4.0*M_PI / (3.0-k) * rho0 * pow(R/R0, 3-k) * R0*R0*R0;
116 | else if (envType == ENV_STEP)
117 | {
118 | if(R < R0)
119 | return 4.0/3.0 * M_PI * rho0 * R*R*R;
120 | else
121 | return 4.0/3.0 * M_PI * (rho0 * R0*R0*R0
122 | + rho1 * (R-R0)*(R-R0)*(R-R0));
123 | }
124 | else
125 | return 0;
126 | }
127 |
128 | double envRadius(double M, int envType, double rho0, double R0, double k,
129 | double rho1)
130 | {
131 | if(envType == ENV_ISM)
132 | return pow(0.75 * M / (M_PI * rho0), 1.0/3.0);
133 | else if(envType == ENV_WIND)
134 | return M / (4.0*M_PI * rho0 * R0*R0);
135 | else if(envType == ENV_PL)
136 | return R0 * pow((3.0-k) * M / (4.0*M_PI * rho0 * R0*R0*R0), 1.0/(3-k));
137 | else if(envType == ENV_STEP)
138 | {
139 | double M0 = 4.0/3.0 * M_PI * rho0 * R0*R0*R0;
140 | if(M <= M0)
141 | return pow(0.75 * M / (M_PI * rho0), 1.0/3.0);
142 | else
143 | return R0 + pow(0.75 * (M-M0) / (M_PI * rho1), 1.0/3.0);
144 | }
145 | else
146 | return 0;
147 | }
148 |
149 |
150 | void shockInitDecel(double t0, double *R0, double *u0, void *argv)
151 | {
152 | double *args = (double *)argv;
153 | double E0 = args[0];
154 | double rho0 = args[2];
155 | double L0 = args[6];
156 | double q = args[7];
157 | double ts = args[8];
158 |
159 | double dr, R, u;
160 | double c = v_light;
161 | double c5 = c*c*c*c*c;
162 |
163 | //First pass: assume no energy injection
164 | double C = sqrt(9/(16.0*M_PI) * E0/(rho0*c5));
165 | u = C * pow(t0, -1.5);
166 | dr = 1.0/(u*u*16.0);
167 | R = c*t0*(1-dr);
168 |
169 | if(L0 < 0.0 || ts < 0.0)
170 | {
171 | //printf("No energy injection! E=%.3le\n", E0);
172 | *R0 = R;
173 | *u0 = u;
174 | return;
175 | }
176 |
177 | //Check if energy injection is important
178 | double te = t0*dr;
179 | double Ei = E_inj(te, L0, q, ts);
180 |
181 | if(Ei <= E0)
182 | {
183 | //printf("Energy injection not important! E0=%.3le Ei=%.3le\n", E0, Ei);
184 | *R0 = R;
185 | *u0 = u;
186 | return;
187 | }
188 |
189 | //Second pass: use energy injection solution.
190 |
191 | //Time for transition
192 | double teb = te_inj(E0, L0, q, ts);
193 | double t1 = pow(16*C*C*teb, 0.25);
194 |
195 | //position at transition
196 | double u1 = C * pow(t1, -1.5);
197 | double dr1 = 1.0/(u1*u1*16.0);
198 |
199 | //Account for initial constant Luminosity
200 | if(teb < T0_inj)
201 | {
202 | //Lab time for T0_inj
203 | double t0i = t_inj(T0_inj, t1, teb, L0, 0.0, ts);
204 |
205 | if(t0 < t0i) //Praise be
206 | {
207 | //printf("Early energy injection.\n");
208 | u = u1*pow(t0/t1, -0.5);
209 | dr = (t1*dr1 + 2*(t0/(u*u)-t1/(u1*u1))/16.0) / t0;
210 | *R0 = c*t0*(1-dr);
211 | *u0 = u;
212 | return;
213 | }
214 |
215 | //Move solution forwards.
216 | u = u1*pow(t0i/t1, -0.5);
217 | dr = (t1*dr1 + 2*(t0i/(u*u)-t1/(u1*u1))/16.0) / t0i;
218 |
219 | t1 = t0i;
220 | u1 = u;
221 | dr1 = dr;
222 | }
223 |
224 | double te1 = dr1*t1;
225 |
226 | //Get lab time when energy injection ends.
227 | double tls = t_inj(ts, t1, te1, L0, q, ts);
228 | if(t0 < tls)
229 | {
230 | //printf("Late energy injection.\n");
231 | u = u1*pow(t0/t1, -0.5*(2+q)/(2-q));
232 | dr = (t1*dr1 + (2-q)*(t0/(u*u)-t1/(u1*u1))/16.0) / t0;
233 |
234 | *R0 = c*t0*(1-dr);
235 | *u0 = u;
236 | return;
237 | }
238 |
239 | //Energy injection is over!
240 | //Just use original solution with boosted energy.
241 | //printf("All energy injected.\n");
242 | Ei = E_inj(ts, L0, q, ts);
243 | C = sqrt(9/(16.0*M_PI) * (E0+Ei)/(rho0*c5));
244 | u = C * pow(t0, -1.5);
245 |
246 | *R0 = c*t0*(1 - 1/(16*u*u));
247 | *u0 = u;
248 | }
249 |
250 | void shockInitFind(double t0, double *R0, double *u0, double tRes, void *argv)
251 | {
252 | struct shockParams *par = (struct shockParams *)argv;
253 | if(par->envType == ENV_ISM)
254 | {
255 | shockInitFindISM(t0, R0, u0, tRes, argv);
256 | return;
257 | }
258 |
259 |
260 | double E0 = par->E0;
261 | double Mej = par->Mej;
262 | double c2 = v_light * v_light;
263 |
264 | double t00, R00, u00;
265 |
266 | if(Mej > 0.0)
267 | {
268 | double gm1_coast = E0 / (Mej * c2);
269 | double g_coast = gm1_coast + 1;
270 | double u2_coast = gm1_coast * (gm1_coast + 2);
271 | double be2_coast = u2_coast / (g_coast * g_coast);
272 |
273 | double Msw = 3.0 * E0 / ((4*u2_coast + 3) * be2_coast * c2);
274 |
275 | //Rough deceleration radius
276 | double Rd = envRadiusPar(Msw, par);
277 |
278 | //shock 4-velocity
279 | double u_coast = sqrt(u2_coast);
280 | double uS = shockVel(u_coast);
281 | double beS = uS / sqrt(uS*uS + 1);
282 | double td = Rd / (beS * v_light);
283 |
284 | if(t0 < 0.001*td)
285 | {
286 | *R0 = beS * v_light * t0;
287 | *u0 = u_coast;
288 | return;
289 | }
290 |
291 | t00 = 0.001*td;
292 | R00 = beS * v_light * t00;
293 | u00 = u_coast;
294 | }
295 | else
296 | {
297 | //Setting ref ultra-relativistic velocity
298 | double u_UR = 1000;
299 | double g2_UR = u_UR*u_UR + 1;
300 | double be2_UR = u_UR*u_UR / g2_UR;
301 |
302 | //swept mass at u_UR;
303 | double M_UR = 3.0 * E0 / ((4*u_UR*u_UR + 3) * be2_UR * c2);
304 |
305 | //Rough UR radius
306 | double R_UR = envRadiusPar(M_UR, par);
307 | double rho_UR = envDensityPar(R_UR, par);
308 |
309 | // = 3-k for a power law rho ~ R^-k
310 | double volIdx = 4*M_PI * R_UR*R_UR*R_UR * rho_UR / M_UR;
311 |
312 | double t_UR = R_UR/v_light * (1.0 + 1.0/(4*(1+volIdx) * g2_UR));
313 |
314 | if(t0 < t_UR)
315 | {
316 | double R_apprx = v_light * t0;
317 | double M0 = envMassPar(R_apprx, par);
318 | double g2 = 0.75 * E0 / (M0 * c2);
319 | *R0 = R_apprx * (1 - 1.0/(4 * (1+volIdx) * g2));
320 | *u0 = sqrt(g2 - 1.0);
321 | return;
322 | }
323 |
324 | t00 = t_UR;
325 | R00 = R_UR;
326 | u00 = u_UR;
327 | }
328 |
329 | double dt, x[2], x0[2], k1[2], k2[2], k3[2], k4[2];
330 |
331 | x[0] = R00;
332 | x[1] = u00;
333 |
334 | double t = t00;
335 | double fac = pow(10, 1.0/tRes);
336 | int j;
337 |
338 | while(t < t0)
339 | {
340 | dt = (fac-1)*t;
341 | if(fac*t >= t0)
342 | dt = t0-t;
343 | x0[0] = x[0];
344 | x0[1] = x[1];
345 | Rudot2D(t, x, argv, k1);
346 |
347 | for(j=0; j<2; j++)
348 | x[j] = x0[j] + 0.5*dt*k1[j];
349 | Rudot2D(t, x, argv, k2);
350 |
351 | for(j=0; j<2; j++)
352 | x[j] = x0[j] + 0.5*dt*k2[j];
353 | Rudot2D(t, x, argv, k3);
354 |
355 | for(j=0; j<2; j++)
356 | x[j] = x0[j] + dt*k3[j];
357 | Rudot2D(t, x, argv, k4);
358 |
359 | for(j=0; j<2; j++)
360 | x[j] = x0[j] + dt*(k1[j]+2*k2[j]+2*k3[j]+k4[j])/6.0;
361 | t *= fac;
362 | }
363 |
364 | *R0 = x[0];
365 | *u0 = x[1];
366 | }
367 |
368 | void shockInitFindISM(double t0, double *R0, double *u0, double tRes,
369 | void *argv)
370 | {
371 | struct shockParams *par = (struct shockParams *)argv;
372 |
373 | double E0 = par->E0;
374 | double Mej = par->Mej;
375 | double rho0 = par->rho0_env;
376 | double L0 = par->L0_inj;
377 | double q = par->q_inj;
378 | double ts = par->ts_inj;
379 |
380 | //printf("shockInitFind: t0=%.6lg E0=%.6lg Mej=%.6lg rho0=%.6lg\n",
381 | // t0, E0, Mej, rho0);
382 | //printf(" L0=%.6lg q=%.6lg ts=%.6lg\n", L0, q, ts);
383 |
384 | double u;
385 | double c = v_light;
386 | double c5 = c*c*c*c*c;
387 |
388 | double C = sqrt(9/(16.0*M_PI) * E0/(rho0*c5));
389 | double tNR = pow(C, 2.0/3.0);
390 |
391 | //Time for deceleration
392 | double td;
393 | if(Mej > 0.0)
394 | {
395 | // gc, uc, beSc = coasting gamma, u, beta_shock
396 | double gcmo = E0/(Mej *c*c);
397 | double uc = sqrt(gcmo * (gcmo+2));
398 | double gc = sqrt(1+uc*uc);
399 | double beSc = 4*uc*gc / (4*uc*uc+3);
400 | double Rd = pow(9*gc*gc*Mej / (4*M_PI*(gc+1)*(4*uc*uc+3)*rho0), 1./3.);
401 | td = Rd / (beSc * c);
402 | }
403 | else
404 | {
405 | td = 0.0;
406 | }
407 |
408 | //Time for transition
409 | double ti;
410 | if(L0 > 0 && ts > 0)
411 | {
412 | double tei = te_inj(E0, L0, q, ts);
413 | ti = pow(16*C*C*tei, 0.25);
414 | if(tei < 0.0)
415 | ti = 1.0e20 * tNR; //Arbitrary val
416 | }
417 | else
418 | ti = 1.0e20 * tNR; //Arbitrary val
419 |
420 | //printf(" td=%.6lg ti=%.6lg tNR=%.6lg\n", td, ti, tNR);
421 |
422 | if(t0 < 0.01*tNR && t0 < 0.01*ti && t0 > 100*td)
423 | {
424 | //printf("the EASY way\n");
425 | u = C*pow(t0, -1.5);
426 | *R0 = c*t0*(1-1/(16*u*u));
427 | *u0 = u;
428 | return;
429 | }
430 | else if(t0 < 0.01 * td)
431 | {
432 | double gcmo = E0/(Mej *c*c);
433 | double uc = sqrt(gcmo * (gcmo+2));
434 | double gc = sqrt(1+uc*uc);
435 | double beSc = 4*uc*gc / (4*uc*uc+3);
436 |
437 | *R0 = beSc * c * t0;
438 | *u0 = uc;
439 | return;
440 | }
441 |
442 | double dt, x[2], x0[2], k1[2], k2[2], k3[2], k4[2];
443 |
444 | double t00, u00, R00;
445 |
446 | if(td > 0.0)
447 | {
448 | double gcmo = E0/(Mej *c*c);
449 | double uc = sqrt(gcmo * (gcmo+2));
450 | double gc = sqrt(1+uc*uc);
451 | double beSc = 4*uc*gc / (4*uc*uc+3);
452 |
453 | t00 = td= t0)
477 | dt = t0-t;
478 | x0[0] = x[0];
479 | x0[1] = x[1];
480 | Rudot2D(t, x, argv, k1);
481 |
482 | for(j=0; j<2; j++)
483 | x[j] = x0[j] + 0.5*dt*k1[j];
484 | Rudot2D(t, x, argv, k2);
485 |
486 | for(j=0; j<2; j++)
487 | x[j] = x0[j] + 0.5*dt*k2[j];
488 | Rudot2D(t, x, argv, k3);
489 |
490 | for(j=0; j<2; j++)
491 | x[j] = x0[j] + dt*k3[j];
492 | Rudot2D(t, x, argv, k4);
493 |
494 | for(j=0; j<2; j++)
495 | x[j] = x0[j] + dt*(k1[j]+2*k2[j]+2*k3[j]+k4[j])/6.0;
496 | t *= fac;
497 | }
498 |
499 | *R0 = x[0];
500 | *u0 = x[1];
501 | }
502 |
503 | void Rudot2D(double t, double *x, void *argv, double *xdot)
504 | {
505 | struct shockParams *par = (struct shockParams *)argv;
506 |
507 | double Mej = par->Mej;
508 | double Einj = par->E0_refresh;
509 | double k = par->k_refresh;
510 | double umin = par->umin_refresh;
511 | double L0 = par->L0_inj;
512 | double q = par->q_inj;
513 | double ts = par->ts_inj;
514 |
515 | double R = x[0];
516 | double u = x[1];
517 |
518 | double g = sqrt(1+u*u);
519 | double be = u/g;
520 |
521 | double bes = 4*u*g/(4*u*u+3);
522 |
523 | double dRdt = bes * v_light;
524 |
525 | double dEdu = 0.0;
526 | if(Einj > 0.0 && u>umin)
527 | dEdu = -k*Einj*pow(u,-k-1);
528 |
529 | double dEdt = 0.0;
530 | double te = t - R/v_light;
531 | if(L0 > 0.0 && te < ts)
532 | {
533 | double gs2 = (4*u*u+3)*(4*u*u+3) / (8*u*u+9);
534 | dEdt = L_inj(te, L0, q, ts) / (gs2*(1+bes)); // Doppler factor (1-bes)
535 | }
536 |
537 | double rho = envDensityPar(R, par);
538 | double M = envMassPar(R, par);
539 |
540 | double dR_Esh = 4*M_PI/3.0 * (4*u*u+3)*be*be * rho * R*R * v_light*v_light;
541 | double du_Esh = 2.0 * (2*u*u+1)*(2*u*u+3)*u * M * v_light*v_light
542 | / (3.0 * g*g*g*g);
543 | double du_Eej = be * Mej * v_light*v_light;
544 | double dudt = (-dR_Esh * dRdt + dEdt) / (du_Eej + du_Esh - dEdu);
545 |
546 | //double num = -16*M_PI/3.0 * rho0*R*R * be*u*u * v_light
547 | // + dEdt/(v_light*v_light);
548 | //double denom = be*Mej
549 | // + 8*M_PI*rho0*R*R*R*u*(2*u*u+1)*(2*u*u+3)/(9*g*g*g*g)
550 | // - dEdu/(v_light*v_light);
551 | //double dudt = num/denom;
552 |
553 | xdot[0] = dRdt;
554 | xdot[1] = dudt;
555 | }
556 |
557 | void RuThdot3D(double t, double *x, void *argv, double *xdot)
558 | {
559 | struct shockParams *par = (struct shockParams *)argv;
560 |
561 | double Mej = par->Mej;
562 | //double rho0 = par->rho0_env;
563 | double Einj = par->E0_refresh;
564 | double k = par->k_refresh;
565 | double umin = par->umin_refresh;
566 | double L0 = par->L0_inj;
567 | double q = par->q_inj;
568 | double ts = par->ts_inj;
569 | double thC = par->thetaCore;
570 | double th0 = par->theta0;
571 | double thCg = par->thetaCoreGlobal;
572 | int spread = par->spread;
573 |
574 | double R = x[0];
575 | double u = x[1];
576 | double th = x[2];
577 |
578 | double g = sqrt(1+u*u);
579 | double be = u/g;
580 | double bes = 4*u*g/(4*u*u+3);
581 |
582 | double sinth = sin(0.5*th);
583 | double costh = cos(0.5*th);
584 | double om = 2*sinth*sinth;
585 |
586 | double dRdt = 4*u*g/(4*u*u+3) * v_light;
587 |
588 | double rho = envDensityPar(R, par);
589 | double M = envMassPar(R, par);
590 | double env3mk = 4*M_PI*rho*R*R*R / M;
591 |
592 | double dThdt = 0.0;
593 | //TODO: verify spreading procedure 190401
594 | //if(spread && th < 0.5*M_PI && u < 1)
595 | //if(spread && th < 0.5*M_PI && u*3.0*th < 1)
596 | if(spread)
597 | {
598 | if(spread == 1)
599 | {
600 | double Q0 = 2.0;
601 | double Q = sqrt(2.0)*3.0;
602 |
603 | if(th < 0.5*M_PI && Q0*u*thC < 1)
604 | {
605 | double e = u*u/(g+1); // specific internal energy == gamma-1
606 |
607 | //Sound speed from trans-relativistic EoS.
608 | double cs = v_light * sqrt(e*(2+e)*(5+8*e+4*e*e)
609 | / (3*(1+e)*(1+e)*(1+2*e)*(3+2*e)));
610 | double fac = u*thC*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thC) / (Q-Q0);
611 | /*
612 | if(fac < 1.0)
613 | {
614 | double sharp = 3.0;
615 | double f0 = exp(-sharp);
616 | fac = (exp(sharp*(fac-1.0))-f0) / (1.0-f0);
617 | }
618 | */
619 | //fac = 0.0;
620 | dThdt = fac * cs / (R*g);
621 | }
622 | }
623 | else if(spread == 2)
624 | {
625 | double Q0 = 2.0;
626 | double Q = sqrt(2)*3.0;
627 |
628 | if(th < 0.5*M_PI && Q0*u*th0 < 1)
629 | {
630 | double e = u*u/(g+1); // specific internal energy == gamma-1
631 |
632 | //Sound speed from trans-relativistic EoS.
633 | double cs = v_light * sqrt(e*(2+e)*(5+8*e+4*e*e)
634 | / (3*(1+e)*(1+e)*(1+2*e)*(3+2*e)));
635 | double fac = u*th0*Q < 1.0 ? 1.0 : Q*(1-Q0*u*th0) / (Q-Q0);
636 | /*
637 | if(fac < 1.0)
638 | {
639 | double sharp = 3.0;
640 | double f0 = exp(-sharp);
641 | fac = (exp(sharp*(fac-1.0))-f0) / (1.0-f0);
642 | }
643 | */
644 | //fac = 0.0;
645 | dThdt = fac * cs / (R*g);
646 | }
647 | }
648 | else if(spread == 3)
649 | {
650 | double Q0 = 2.0;
651 | double Q = sqrt(2)*3.0;
652 |
653 | if(th < 0.5*M_PI && Q0*u*thCg < 1)
654 | {
655 | double e = u*u/(g+1); // specific internal energy == gamma-1
656 |
657 | //Sound speed from trans-relativistic EoS.
658 | double cs = v_light * sqrt(e*(2+e)*(5+8*e+4*e*e)
659 | / (3*(1+e)*(1+e)*(1+2*e)*(3+2*e)));
660 | double fac = u*thCg*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thCg) / (Q-Q0);
661 | /*
662 | if(fac < 1.0)
663 | {
664 | double sharp = 3.0;
665 | double f0 = exp(-sharp);
666 | fac = (exp(sharp*(fac-1.0))-f0) / (1.0-f0);
667 | }
668 | */
669 | //fac = 0.0;
670 | dThdt = fac * cs / (R*g);
671 | }
672 | }
673 | else if(spread == 4)
674 | {
675 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
676 | dThdt = bew * v_light / R;
677 | }
678 | else if(spread == 5)
679 | {
680 | double Q0 = 2.0;
681 | double Q = sqrt(2.0)*3.0;
682 |
683 | if(th < 0.5*M_PI && Q0*u*thC < 1)
684 | {
685 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
686 | double fac = u*thC*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thC) / (Q-Q0);
687 | dThdt = fac * bew * v_light / R;
688 | }
689 | }
690 | else if(spread == 6)
691 | {
692 | double Q0 = 2.0;
693 | double Q = sqrt(2.0)*3.0;
694 |
695 | if(th < 0.5*M_PI && Q0*u*thCg < 1)
696 | {
697 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
698 | double fac = u*thCg*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thCg) / (Q-Q0);
699 | dThdt = fac * bew * v_light / R;
700 | }
701 | }
702 | else if(spread == 7)
703 | {
704 | double envk = 3 - env3mk;
705 | double Q = sqrt(2.0)*env3mk;
706 | double Q0;
707 | if(envk < 2.0)
708 | Q0 = 0.5 * (2.0 * (2.0 - envk) + 1.4 * envk);
709 | else
710 | Q0 = 1.4 * (3.0 - envk);
711 |
712 | if(th < 0.5*M_PI && Q0*u*thC < 1)
713 | {
714 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
715 | double fac = u*thC*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thC) / (Q-Q0);
716 | if (th0 < thC)
717 | fac *= tan(0.5*th0)/tan(0.5*thC); //th0/thC;
718 | dThdt = fac * bew * v_light / R;
719 | }
720 | }
721 | else if(spread == 8)
722 | {
723 | double envk = 3 - env3mk;
724 | double Q = sqrt(2.0)*env3mk;
725 | double Q0;
726 | if(envk < 2.0)
727 | Q0 = 0.5 * (2.0 * (2.0 - envk) + 1.4 * envk);
728 | else
729 | Q0 = 1.4 * (3.0 - envk);
730 |
731 | if(th < 0.5*M_PI && Q0*u*thCg < 1)
732 | {
733 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
734 | double fac = u*thCg*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thCg) / (Q-Q0);
735 | if (th0 < thCg)
736 | fac *= tan(0.5*th0)/tan(0.5*thCg); //th0/thC;
737 | dThdt = fac * bew * v_light / R;
738 | }
739 | }
740 | else if(spread == 9)
741 | {
742 | //Fernandez & Lamb 2021 spreading.
743 | if(th < 0.5*M_PI && u*thC < 100)
744 | {
745 | double gs = 1 / sqrt((1-bes)*(1+bes));
746 | double fac = 1.0;
747 | if (th0 < thC)
748 | fac *= tan(0.5*th0)/tan(0.5*thC); //th0/thC;
749 | dThdt = fac / (gs*gs * th) * dRdt / R;
750 | }
751 | }
752 | else if(spread == 11)
753 | {
754 | double envk = 3 - env3mk;
755 | double Q = sqrt(2.0)*env3mk;
756 | double Q0;
757 | if(envk < 2.0)
758 | Q0 = 0.5 * (2.0 * (2.0 - envk) + 1.4 * envk);
759 | else
760 | Q0 = 1.4 * (3.0 - envk);
761 |
762 | if(th < 0.5*M_PI && Q0*u*thC < 1)
763 | {
764 | double bew = 0.5*sqrt((2*u*u+3)/(4*u*u+3))*bes/g;
765 | double fac = u*thC*Q < 1.0 ? 1.0 : Q*(1-Q0*u*thC) / (Q-Q0);
766 | if (th0 < thC)
767 | fac *= tan(0.5*th0)/tan(0.5*thC); //th0/thC;
768 | double gs = 1.0 / sqrt((1-bes)*(1+bes));
769 | double dThdt_lum = fac * v_light / (R * g);
770 | dThdt = 4.0 * fac * bew * v_light / R;
771 | if(dThdt > dThdt_lum)
772 | dThdt = dThdt_lum;
773 | }
774 | }
775 | }
776 |
777 | double dEdu = 0.0;
778 | if(Einj > 0.0 && u>umin)
779 | dEdu = -k*Einj*pow(u,-k-1);
780 |
781 | double dEdt = 0.0;
782 | double te = t - R/v_light;
783 | if(L0 > 0.0 && te < ts)
784 | {
785 | double gs2 = (4*u*u+3)*(4*u*u+3) / (8*u*u+9);
786 | dEdt = L_inj(te, L0, q, ts) / (gs2*(1+bes)); // Doppler factor (1-bes)
787 | }
788 |
789 | double c2 = v_light*v_light;
790 |
791 | double dR_Esh = 4*M_PI/3.0 * (4*u*u+3)*be*be * rho * R*R * om * c2;
792 | double du_Esh = 2.0*(2*u*u+1)*(2*u*u+3)*u * M * om * c2 / (3.0 * g*g*g*g);
793 | double dTh_Esh = (4*u*u+3)*be*be * M * 2*sinth*costh * c2 / 3.0;
794 | double du_Eej = be * Mej * c2;
795 | double dudt = (-dR_Esh * dRdt - dTh_Esh * dThdt + dEdt)
796 | / (du_Eej + du_Esh - dEdu);
797 |
798 | /*
799 | double num = -16*M_PI/3.0 * om*rho0*R*R * be*u*u * v_light
800 | -8*M_PI/9.0*rho0*R*R*R*(4*u*u+3)*be*be*sinth*costh*dThdt
801 | + om*dEdt/(v_light*v_light);
802 | double denom = be*Mej
803 | + 8*M_PI*om*rho0*R*R*R*u*(2*u*u+1)*(2*u*u+3)/(9*g*g*g*g)
804 | - dEdu/(v_light*v_light);
805 | double dudt = num/denom;
806 | */
807 |
808 | xdot[0] = dRdt;
809 | xdot[1] = dudt;
810 | xdot[2] = dThdt;
811 | }
812 |
813 | void shockEvolveRK4(double *t, double *R, double *u, int N, double R0,
814 | double u0, void *args)
815 | {
816 | int i,j;
817 |
818 | double dt, x[2], x0[2], k1[2], k2[2], k3[2], k4[2];
819 |
820 | R[0] = R0;
821 | u[0] = u0;
822 |
823 | for(i=0; i 0.5*M_PI)
886 | th[i+1] = 0.5*M_PI;
887 | else
888 | th[i+1] = x[2];
889 | }
890 | }
891 |
892 | void setup_shockParams(struct shockParams *pars, int spread,
893 | double E0, double Mej,
894 | int envType, double rho0_env, double R0_env,
895 | double k_env, double rho1_env,
896 | double L0_inj, double q_inj, double t0_inj,
897 | double ts_inj,
898 | double E0_refresh, double k_refresh, double umin_refresh,
899 | double thetaCore, double theta0,
900 | double thetaCoreGlobal)
901 | {
902 | pars->spread = spread;
903 | pars->E0 = E0;
904 | pars->Mej = Mej;
905 | pars->envType = envType;
906 | pars->rho0_env = rho0_env;
907 | pars->R0_env = R0_env;
908 | pars->k_env = k_env;
909 | pars->rho1_env = rho1_env;
910 | pars->L0_inj = L0_inj;
911 | pars->q_inj = q_inj;
912 | pars->t0_inj = t0_inj;
913 | pars->ts_inj = ts_inj;
914 | pars->E0_refresh = E0_refresh;
915 | pars->k_refresh = k_refresh;
916 | pars->umin_refresh = umin_refresh;
917 | pars->thetaCore = thetaCore;
918 | pars->theta0 = theta0;
919 | pars->thetaCoreGlobal = thetaCoreGlobal;
920 | }
921 |
--------------------------------------------------------------------------------
/afterglowpy/shockEvolution.h:
--------------------------------------------------------------------------------
1 | #ifndef AFTERGLOWPY_SHOCK
2 | #define AFTERGLOWPY_SHOCK
3 |
4 | enum{ENV_ISM, ENV_WIND, ENV_PL, ENV_STEP};
5 |
6 | static const double T0_inj = 1.0e3;
7 |
8 | struct shockParams
9 | {
10 | int spread;
11 | double E0;
12 | double Mej;
13 | double L0_inj;
14 | double q_inj;
15 | double t0_inj;
16 | double ts_inj;
17 | double E0_refresh;
18 | double k_refresh;
19 | double umin_refresh;
20 | double thetaCore;
21 | double theta0;
22 | double thetaCoreGlobal;
23 | int envType;
24 | double rho0_env;
25 | double R0_env;
26 | double k_env;
27 | double rho1_env;
28 | };
29 |
30 | void setup_shockParams(struct shockParams *pars, int spread,
31 | double E0, double Mej,
32 | int envType, double env_rho0, double R0_env,
33 | double k_env, double rho1_env,
34 | double L0_inj, double q_inj, double t0_inj,
35 | double ts_inj,
36 | double E0_refresh, double k_refresh, double umin_refresh,
37 | double thetaCore, double theta0,
38 | double thetaCoreGlobal);
39 |
40 | double shockVel(double u);
41 | double E_inj(double te, double L0, double q, double ts);
42 | double L_inj(double te, double L0, double q, double ts);
43 | double envDensityPar(double R, struct shockParams *pars);
44 | double envMassPar(double R, struct shockParams *pars);
45 | double envRadiusPar(double M, struct shockParams *pars);
46 | double envDensity(double R, int envType, double rho0, double R0, double k,
47 | double rho1);
48 | double envMass(double R, int envType, double rho0, double R0, double k,
49 | double rho1);
50 | double envRadius(double M, int envType, double rho0, double R0, double k,
51 | double rho1);
52 | void shockInitDecel(double t0, double *R0, double *u0, void *argv);
53 | void shockInitFind(double t0, double *R0, double *u0, double tRes, void *argv);
54 | void shockInitFindISM(double t0, double *R0, double *u0, double tRes,
55 | void *argv);
56 | void Rudot2D(double t, double *x, void *argv, double *xdot);
57 | void RuThdot3D(double t, double *x, void *argv, double *xdot);
58 | void shockEvolveRK4(double *t, double *R, double *u, int N, double R0,
59 | double u0, void *args);
60 | void shockEvolveSpreadRK4(double *t, double *R, double *u, double *th, int N,
61 | double R0, double u0, double th0, void *args);
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/afterglowpy/shockmodule.c:
--------------------------------------------------------------------------------
1 | #include
2 | #define NPY_NO_DEPRECATED_API NPY_1_11_API_VERSION
3 | #include
4 | #include "shockEvolution.h"
5 |
6 | static char shock_docstring[] =
7 | "This module calculates evolution of relativistic shocks.";
8 | static char shockEvolRK4_docstring[] =
9 | "Evolve a spherical shock with RK4";
10 | static char shockEvolSpreadRK4_docstring[] =
11 | "Evolve a conical shock with RK4";
12 |
13 | static PyObject *error_out(PyObject *m);
14 | static PyObject *shock_shockEvolRK4(PyObject *self, PyObject *args);
15 | static PyObject *shock_shockEvolSpreadRK4(PyObject *self, PyObject *args);
16 |
17 | struct module_state
18 | {
19 | PyObject *error;
20 | };
21 | #if PY_MAJOR_VERSION >= 3
22 | #define GETSTATE(m) ((struct module_state *) PyModule_GetState(m))
23 | #else
24 | #define GETSTATE(m) (&_state)
25 | static struct module_state _state;
26 | #endif
27 |
28 | static PyMethodDef shockMethods[] = {
29 | {"shockEvolRK4", shock_shockEvolRK4, METH_VARARGS, shockEvolRK4_docstring},
30 | {"shockEvolSpreadRK4", shock_shockEvolSpreadRK4, METH_VARARGS,
31 | shockEvolSpreadRK4_docstring},
32 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
33 | {NULL, NULL, 0, NULL}};
34 |
35 | #if PY_MAJOR_VERSION >= 3
36 |
37 | static int shock_traverse(PyObject *m, visitproc visit, void *arg)
38 | {
39 | Py_VISIT(GETSTATE(m)->error);
40 | return 0;
41 | }
42 |
43 | static int shock_clear(PyObject *m)
44 | {
45 | Py_CLEAR(GETSTATE(m)->error);
46 | return 0;
47 | }
48 |
49 | static struct PyModuleDef shockModule = {
50 | PyModuleDef_HEAD_INIT,
51 | "shock", /* Module Name */
52 | shock_docstring,
53 | sizeof(struct module_state),
54 | shockMethods,
55 | NULL,
56 | shock_traverse,
57 | shock_clear,
58 | NULL
59 | };
60 | #define INITERROR return NULL
61 |
62 | PyMODINIT_FUNC PyInit_shock(void)
63 | #else
64 | #define INITERROR return
65 |
66 | void initshock(void)
67 | #endif
68 | {
69 | #if PY_MAJOR_VERSION >= 3
70 | PyObject *module = PyModule_Create(&shockModule);
71 | #else
72 | PyObject *module = Py_InitModule3("shock", shockMethods, shock_docstring);
73 | #endif
74 | if(module == NULL)
75 | INITERROR;
76 | struct module_state *st = GETSTATE(module);
77 | st->error = PyErr_NewException("shock.Error", NULL, NULL);
78 | if(st->error == NULL)
79 | {
80 | Py_DECREF(module);
81 | INITERROR;
82 | }
83 |
84 | //Load numpy stuff!
85 | import_array();
86 | #if PY_MAJOR_VERSION >= 3
87 | return module;
88 | #endif
89 | }
90 |
91 | static PyObject *error_out(PyObject *m)
92 | {
93 | struct module_state *st = GETSTATE(m);
94 | PyErr_SetString(st->error, "something bad happened");
95 | return NULL;
96 | }
97 |
98 | static PyObject *shock_shockEvolRK4(PyObject *self, PyObject *args)
99 | {
100 | PyObject *t_obj = NULL;
101 |
102 | double R0, u0;
103 | double Mej, rho0_env, R0_env, k_env, rho1_env, Einj, k, umin, L0, q, ts;
104 | int envType;
105 |
106 | //Parse Arguments
107 | if(!PyArg_ParseTuple(args, "Odd""d""idddd""ddd""ddd",
108 | &t_obj, &R0, &u0,
109 | &Mej,
110 | &envType, &rho0_env, &R0_env, &k_env, &rho1_env,
111 | &Einj, &k, &umin,
112 | &L0, &q, &ts))
113 | {
114 | PyErr_SetString(PyExc_RuntimeError, "Could not parse arguments.");
115 | return NULL;
116 | }
117 |
118 | //Grab NUMPY arrays
119 | PyArrayObject *t_arr;
120 | t_arr = (PyArrayObject *) PyArray_FROM_OTF(t_obj, NPY_DOUBLE,
121 | NPY_ARRAY_IN_ARRAY);
122 | if(t_arr == NULL)
123 | {
124 | PyErr_SetString(PyExc_RuntimeError, "Could not read input arrays.");
125 | Py_XDECREF(t_arr);
126 | return NULL;
127 | }
128 |
129 | int t_ndim = (int) PyArray_NDIM(t_arr);
130 | if(t_ndim != 1)
131 | {
132 | PyErr_SetString(PyExc_RuntimeError, "Array must be 1-D.");
133 | Py_DECREF(t_arr);
134 | return NULL;
135 | }
136 |
137 | int N = (int)PyArray_DIM(t_arr, 0);
138 |
139 | double *t = (double *)PyArray_DATA(t_arr);
140 |
141 | //Allocate output array
142 |
143 | npy_intp dims[1] = {N};
144 | PyObject *R_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
145 | PyObject *u_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
146 |
147 | if(R_obj == NULL || u_obj == NULL)
148 | {
149 | PyErr_SetString(PyExc_RuntimeError, "Could not make output arrays.");
150 | Py_DECREF(t_arr);
151 | Py_XDECREF(R_obj);
152 | Py_XDECREF(u_obj);
153 | return NULL;
154 | }
155 | double *R = PyArray_DATA((PyArrayObject *) R_obj);
156 | double *u = PyArray_DATA((PyArrayObject *) u_obj);
157 |
158 | // Evolve the shock!
159 | struct shockParams shock_pars;
160 | setup_shockParams(&shock_pars, 0, 0.0, Mej,
161 | envType, rho0_env, R0_env, k_env, rho1_env,
162 | L0, q, 1.0e3, ts,
163 | Einj, k, umin,
164 | 0.0, 0.0, 0.0);
165 | //double shockArgs[9] = {0.0, Mej, rho0, Einj, k, umin, L0, q, ts};
166 | shockEvolveRK4(t, R, u, N, R0, u0, &shock_pars);
167 |
168 | // Clean up!
169 | Py_DECREF(t_arr);
170 |
171 | //Build output
172 | PyObject *ret = Py_BuildValue("NN", R_obj, u_obj);
173 |
174 | return ret;
175 | }
176 |
177 | static PyObject *shock_shockEvolSpreadRK4(PyObject *self, PyObject *args)
178 | {
179 | PyObject *t_obj = NULL;
180 |
181 | double R0, u0, th0;
182 | double Mej, rho0_env, R0_env, k_env, rho1_env;
183 | double Einj, k, umin, L0, q, ts, thC;
184 | int envType;
185 | int spread;
186 |
187 | //Parse Arguments
188 | if(!PyArg_ParseTuple(args, "Oddd""d""idddd""ddd""ddd""di",
189 | &t_obj, &R0, &u0, &th0,
190 | &Mej,
191 | &envType, &rho0_env, &R0_env, &k_env,
192 | &rho1_env,
193 | &Einj, &k, &umin,
194 | &L0, &q, &ts,
195 | &thC, &spread))
196 | {
197 | PyErr_SetString(PyExc_RuntimeError, "Could not parse arguments.");
198 | return NULL;
199 | }
200 |
201 | //Grab NUMPY arrays
202 | PyArrayObject *t_arr;
203 | t_arr = (PyArrayObject *) PyArray_FROM_OTF(t_obj, NPY_DOUBLE,
204 | NPY_ARRAY_IN_ARRAY);
205 | if(t_arr == NULL)
206 | {
207 | PyErr_SetString(PyExc_RuntimeError, "Could not read input arrays.");
208 | Py_XDECREF(t_arr);
209 | return NULL;
210 | }
211 |
212 | int t_ndim = (int) PyArray_NDIM(t_arr);
213 | if(t_ndim != 1)
214 | {
215 | PyErr_SetString(PyExc_RuntimeError, "Array must be 1-D.");
216 | Py_DECREF(t_arr);
217 | return NULL;
218 | }
219 |
220 | int N = (int)PyArray_DIM(t_arr, 0);
221 |
222 | double *t = (double *)PyArray_DATA(t_arr);
223 |
224 | //Allocate output arrays
225 |
226 | npy_intp dims[1] = {N};
227 | PyObject *R_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
228 | PyObject *u_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
229 | PyObject *th_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
230 |
231 | if(R_obj == NULL || u_obj == NULL || th_obj == NULL)
232 | {
233 | PyErr_SetString(PyExc_RuntimeError, "Could not make output arrays.");
234 | Py_DECREF(t_arr);
235 | Py_XDECREF(R_obj);
236 | Py_XDECREF(u_obj);
237 | Py_XDECREF(th_obj);
238 | return NULL;
239 | }
240 | double *R = PyArray_DATA((PyArrayObject *) R_obj);
241 | double *u = PyArray_DATA((PyArrayObject *) u_obj);
242 | double *th = PyArray_DATA((PyArrayObject *) th_obj);
243 |
244 | // Evolve the shock!
245 | //double shockArgs[12] = {0.0, Mej, rho0, Einj, k, umin, L0, q, ts, thC, th0,
246 | // thC};
247 |
248 | struct shockParams shock_pars;
249 | setup_shockParams(&shock_pars, spread, 0.0, Mej,
250 | envType, rho0_env, R0_env, k_env, rho1_env,
251 | L0, q, 1.0e3, ts,
252 | Einj, k, umin,
253 | thC, th0, thC);
254 |
255 | shockEvolveSpreadRK4(t, R, u, th, N, R0, u0, th0, &shock_pars);
256 |
257 | // Clean up!
258 | Py_DECREF(t_arr);
259 |
260 | //Build output
261 | PyObject *ret = Py_BuildValue("NNN", R_obj, u_obj, th_obj);
262 |
263 | return ret;
264 | }
265 |
--------------------------------------------------------------------------------
/afterglowpy/version.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | r"""Version info"""
3 |
4 | __short_version__ = '0.8'
5 | __version__ = '0.8.1'
6 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx>=2.2
2 | numpy>=2.0
3 | scipy>=1.0
4 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 |
14 |
15 | import os
16 | import sys
17 | sys.path.insert(0, os.path.abspath('../..'))
18 | # from afterglowpy.version import __version__
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = 'afterglowpy'
23 | copyright = '2019, Geoffrey Ryan'
24 | author = 'Geoffrey Ryan'
25 |
26 | # The full version, including alpha/beta/rc tags
27 |
28 | with open("../../afterglowpy/version.py", "r") as f:
29 | exec(f.read())
30 | release = __version__
31 | version = __version__
32 |
33 |
34 | # -- General configuration ---------------------------------------------------
35 |
36 | # Add any Sphinx extension module names here, as strings. They can be
37 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
38 | # ones.
39 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
40 |
41 | # Add any paths that contain templates here, relative to this directory.
42 | templates_path = ['_templates']
43 |
44 | # List of patterns, relative to source directory, that match files and
45 | # directories to ignore when looking for source files.
46 | # This pattern also affects html_static_path and html_extra_path.
47 | exclude_patterns = []
48 |
49 | master_doc = 'index'
50 | autodoc_mock_imports = ['afterglowpy.jet', 'afterglowpy.shock']
51 |
52 |
53 | # -- Options for HTML output -------------------------------------------------
54 |
55 | # The theme to use for HTML and HTML Help pages. See the documentation for
56 | # a list of builtin themes.
57 | #
58 | html_theme = 'alabaster'
59 |
60 | # Add any paths that contain custom static files (such as style sheets) here,
61 | # relative to this directory. They are copied after the builtin static files,
62 | # so a file named "default.css" will overwrite the builtin "default.css".
63 | html_static_path = ['_static']
64 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | afterglowpy
2 | ===========
3 |
4 | *afterglowpy* is a Python package for modelling Gamma-ray burst afterglows. It computes synchrotron radiation from an external shock and is capable of handling both structured jets and off-axis observers.
5 |
6 | For details of the physics, approximations, and numerical implementation please see `Ryan et al. 2020 `_.
7 |
8 | You can install *afterglowpy* with pip::
9 |
10 | $ pip install afterglowpy
11 |
12 | This documentation is still a little thin. Please read the Quickstart, check out the examples on Github: ``_, and contact the author if you have any questions or issues.
13 |
14 | Attribution
15 | +++++++++++
16 |
17 | If you use *afterglowpy* in your work, please refer to the package by name and cite the paper `Ryan et al. 2020 `_.
18 |
19 | Contents
20 | ^^^^^^^^
21 |
22 | .. toctree::
23 | :maxdepth: 4
24 |
25 | quickstart.rst
26 | modules.rst
27 |
28 |
29 | Indices and tables
30 | ------------------
31 |
32 | * :ref:`genindex`
33 | * :ref:`modindex`
34 | * :ref:`search`
35 |
--------------------------------------------------------------------------------
/docs/source/modules.rst:
--------------------------------------------------------------------------------
1 | API Documentation
2 | =================
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | modules/afterglowpy
8 |
--------------------------------------------------------------------------------
/docs/source/modules/afterglowpy.cocoon.rst:
--------------------------------------------------------------------------------
1 | afterglowpy.cocoon module
2 | =========================
3 |
4 | .. automodule:: afterglowpy.cocoon
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/modules/afterglowpy.flux.rst:
--------------------------------------------------------------------------------
1 | afterglowpy.flux module
2 | =========================
3 |
4 | .. automodule:: afterglowpy.flux
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/modules/afterglowpy.jet.rst:
--------------------------------------------------------------------------------
1 | afterglowpy.jet module
2 | =========================
3 |
4 | .. automodule:: afterglowpy.jet
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/modules/afterglowpy.rst:
--------------------------------------------------------------------------------
1 | afterglowpy package
2 | ===================
3 |
4 | .. automodule:: afterglowpy
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 | Submodules
10 | ----------
11 |
12 | .. toctree::
13 |
14 | afterglowpy.shock
15 | afterglowpy.jet
16 | afterglowpy.cocoon
17 | afterglowpy.flux
18 |
--------------------------------------------------------------------------------
/docs/source/modules/afterglowpy.shock.rst:
--------------------------------------------------------------------------------
1 | afterglowpy.shock module
2 | =========================
3 |
4 | .. automodule:: afterglowpy.shock
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/source/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quickstart
2 | ==========
3 |
4 | *afterglowpy* exposes a single function ``fluxDensity()`` for the purposes of calculating light curves and spectra from GRB afterglows. It takes many arguments, which are grouped as follows:
5 |
6 | 1. ``t``: a 1-D array of observer-frame times, in seconds (s)
7 | 2. ``nu``: a 1-D array, the same shape as ``t``, of observer-frame frequencies, in Hertz (Hz)
8 | 3. ``jetType``: an integer specifying which jet model to use (top-hat: -1, Gaussian: 0, power law: 4).
9 | 4. ``specType``: an integer specifying which spectral model to use.
10 | 5. Model Parameters (keywords): A model-dependent set of parameters, such as E\ :sub:`iso`, n\ :sub:`0`, etc.
11 | 6. Numerical Parameters (keywords): A model-dependent set of keyword arguments specifying numerical settings, such as `tRes` (resolution of time integration), and `spread` (whether or not to include jet spreading).
12 |
13 | A light curve from a top-hat jet may be created with::
14 |
15 | import numpy as np
16 | import afterglowpy as grb
17 |
18 | # For convenience, place arguments into a dict.
19 | Z = {'jetType': grb.jet.TopHat, # Top-Hat jet
20 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Spectrum
21 |
22 | 'thetaObs': 0.05, # Viewing angle in radians
23 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
24 | 'thetaCore': 0.1, # Half-opening angle in radians
25 | 'n0': 1.0, # circumburst density in cm^{-3}
26 | 'p': 2.2, # electron energy distribution index
27 | 'epsilon_e': 0.1, # epsilon_e
28 | 'epsilon_B': 0.01, # epsilon_B
29 | 'xi_N': 1.0, # Fraction of electrons accelerated
30 | 'd_L': 1.0e28, # Luminosity distance in cm
31 | 'z': 0.55} # redshift
32 |
33 | # Space time points geometrically, from 10^3 s to 10^7 s
34 | t = np.geomspace(1.0e3, 1.0e7, 300)
35 |
36 | # Calculate flux in a single X-ray band (all times have same frequency)
37 | nu = np.empty(t.shape)
38 | nu[:] = 1.0e18
39 |
40 | # Calculate!
41 |
42 | Fnu = grb.fluxDensity(t, nu, **Z)
43 |
44 | ``Fnu`` is now an array, same size as ``t`` and ``nu``, containing the observed flux in mJy at each time and frequency.
45 |
46 | Let's calculate the light curve for a Gaussian jet with the same parameters. A Gaussian jet has an isotropic-energy profile E(theta) = E0 * exp(-0.5*theta^2/thetaCore^2). Now that ``thetaCore`` sets the Gaussian width of the jet core, and we need to provide a sensible value for ``thetaWing`` to truncate the outer regions of the jet::
47 |
48 | Z['jetType'] = grb.jet.Gaussian # Gaussian jet
49 |
50 | # We'll re-use the Z dict, just add a thetaWing entry
51 |
52 | Z['thetaObs'] = 0.3 # Larger viewing angle, just for fun
53 | Z['thetaWing'] = 4 * Z['thetaCore'] # Setting thetaW
54 |
55 | # Nothing else to change, so calculate!
56 |
57 | Fnu_G = grb.fluxDensity(t, nu, **Z)
58 |
59 | A power law structured jet has E(theta) ~ theta^(-b) for theta >> thetaC. We need to give ``b`` a reasonable value::
60 |
61 | Z['jetType'] = grb.jet.PowerLaw # power law
62 |
63 | # Set b
64 | Z['b'] = 6.0 # The rough ballpark from RHD simulations
65 |
66 | # Calculate!
67 |
68 | Fnu_PL = grb.fluxDensity(t, nu, **Z)
69 |
70 | There you go! Simple X-ray light curves for three different jet models.
71 |
--------------------------------------------------------------------------------
/examples/plotCentroidAndSize.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | def computeMoments(t, nu, Z):
6 |
7 | # Make array to store moment options
8 | moment = np.empty(t.shape, dtype=int)
9 |
10 | # compute moment 0 (flux)
11 | moment[:] = grb.jet.MOM_0
12 | Fnu = grb.fluxDensity(t, nu, **Z, moment=moment)
13 |
14 | # compute moment 1 (integral of x_obs * intensity)
15 | moment[:] = grb.jet.MOM_X
16 | FnuX = grb.fluxDensity(t, nu, **Z, moment=moment)
17 |
18 | # compute moment 2 (integral of x_obs^2 * intensity)
19 | moment[:] = grb.jet.MOM_XX
20 | FnuXX = grb.fluxDensity(t, nu, **Z, moment=moment)
21 |
22 | # compute moment 2 (integral of y_obs^2 * intensity)
23 | moment[:] = grb.jet.MOM_YY
24 | FnuYY = grb.fluxDensity(t, nu, **Z, moment=moment)
25 |
26 | # The jet propagates in the x-direction and is axisymmetric, so MOM_Y
27 | # and MOM_XY will always be identically 0
28 |
29 | # Get the intensity-weighted distance measures
30 | X_cm = FnuX / Fnu # in cm
31 | X2_cm = FnuXX / Fnu # in cm^2
32 | Y2_cm = FnuYY / Fnu # in cm^2
33 |
34 | # Convert proper distances to angular distances
35 | dA = Z['d_L'] / (1 + Z['z'])**2
36 |
37 | X_rad = X_cm / dA
38 | X2_rad = X2_cm / dA**2
39 | Y2_rad = Y2_cm / dA**2
40 |
41 | # Convert radians to mas
42 | rad2mas = 1000 * 3600 * 180 / np.pi
43 | x = X_rad * rad2mas
44 | x2 = X2_rad * rad2mas**2
45 | y2 = Y2_rad * rad2mas**2
46 |
47 | # Compute sizes
48 | sig_x = np.sqrt(x2 - x**2)
49 | sig_y = np.sqrt(y2)
50 |
51 | return Fnu, x, sig_x, sig_y
52 |
53 | if __name__ == "__main__":
54 |
55 |
56 | # For convenience, place arguments into a dict.
57 | Z = {'jetType': grb.jet.Gaussian, # Top-Hat jet
58 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Spectrum
59 |
60 | 'thetaObs': 0.00, # Viewing angle in radians
61 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
62 | 'thetaCore': 0.05, # Half-opening angle in radians
63 | 'thetaWing': 0.4, # Half-opening angle in radians
64 | 'n0': 1.0e-3, # circumburst density in cm^{-3}
65 | 'p': 2.2, # electron energy distribution index
66 | 'epsilon_e': 0.1, # epsilon_e
67 | 'epsilon_B': 0.01, # epsilon_B
68 | 'xi_N': 1.0, # Fraction of electrons accelerated
69 | 'd_L': 1.0e26, # Luminosity distance in cm
70 | 'z': 0.01} # redshift
71 |
72 | # Space time points geometrically, from 10^3 s to 10^7 s
73 | t = np.geomspace(1.0e3, 1.0e7, 100)
74 |
75 | # Calculate flux in a single X-ray band (all times have same frequency)
76 | nu = np.empty(t.shape)
77 | nu[:] = 3.0e9
78 |
79 |
80 | # Calculate!
81 | Fnu, x, sig_x, sig_y = computeMoments(t, nu, Z)
82 |
83 | # Try an off-axis one too
84 | Z['thetaObs'] = 6 * Z['thetaCore']
85 | Fnu_off, x_off, sig_x_off, sig_y_off = computeMoments(t, nu, Z)
86 |
87 | # Plot!
88 |
89 | print("Plotting")
90 | fig, ax = plt.subplots(4, 1, figsize=(6, 12))
91 |
92 | ax[0].plot(t, Fnu, label='on-axis')
93 | ax[0].plot(t, Fnu_off, label='off-axis')
94 |
95 | ax[1].plot(t, x)
96 | ax[1].plot(t, x_off)
97 |
98 | ax[2].plot(t, sig_x)
99 | ax[2].plot(t, sig_x_off)
100 |
101 | ax[3].plot(t, sig_y)
102 | ax[3].plot(t, sig_y_off)
103 |
104 | ax[0].legend()
105 |
106 | ax[0].set(xscale='log', xlabel=r'$t$ (s)',
107 | yscale='log', ylabel=r'$F_\nu$[$3\times 10^{9}$ Hz] (mJy)')
108 | ax[1].set(xscale='log', xlabel=r'$t$ (s)',
109 | yscale='linear', ylabel=r'centroid offset (mas)')
110 | ax[2].set(xscale='log', xlabel=r'$t$ (s)',
111 | yscale='log', ylabel=r'$\sigma_x$ (mas)')
112 | ax[3].set(xscale='log', xlabel=r'$t$ (s)',
113 | yscale='log', ylabel=r'$\sigma_y$ (mas)')
114 |
115 | fig.tight_layout()
116 | print("Saving figure lc_centroid_and_size.png")
117 | fig.savefig("lc_centroid_and_size.png")
118 | plt.close(fig)
119 |
--------------------------------------------------------------------------------
/examples/plotLateTime.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | # For convenience, place arguments into a dict.
6 | Z = {'jetType': grb.jet.TopHat, # Top-Hat jet
7 | 'specType': grb.jet.DeepNewtonian, # Deep Newtonian Emission
8 |
9 | 'thetaObs': 0.05, # Viewing angle in radians
10 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
11 | 'thetaCore': 0.1, # Half-opening angle in radians
12 | 'n0': 1.0, # circumburst density in cm^{-3}
13 | 'p': 2.2, # electron energy distribution index
14 | 'epsilon_e': 0.001, # epsilon_e (low eps_e turns DN on sooner)
15 | 'epsilon_B': 0.001, # epsilon_B
16 | 'xi_N': 1.0, # Fraction of electrons accelerated
17 | 'd_L': 1.0e28, # Luminosity distance in cm
18 | 'z': 0.55, # redshift
19 | 'counterjet': True # Turn on the counter jet
20 | }
21 |
22 | # Space time points geometrically, from 10^4 s to 10^9 s
23 | t = np.geomspace(1.0e4, 1.0e9, 300)
24 |
25 | # Calculate flux in a single radio band (all times have same frequency)
26 | nu = np.empty(t.shape)
27 | nu[:] = 3.0e9
28 |
29 | # Calculate!
30 |
31 | Fnu = grb.fluxDensity(t, nu, **Z)
32 |
33 | # Compute a light curve with counterjet but no deep newtonian
34 |
35 | Z_cj_only = Z.copy()
36 | Z_cj_only['specType'] = grb.jet.SimpleSpec
37 | Fnu_cj_only = grb.fluxDensity(t, nu, **Z_cj_only)
38 |
39 | # Compute a light curve with no counterjet and no deep newtonian
40 | Z_default = Z.copy()
41 | Z_default['specType'] = grb.jet.SimpleSpec
42 | Z_default['counterjet'] = False
43 | Fnu_default = grb.fluxDensity(t, nu, **Z_default)
44 |
45 |
46 | # Plot!
47 |
48 | print("Plotting")
49 | fig, ax = plt.subplots(1, 1)
50 |
51 | ax.plot(t, Fnu, label='Deep Newtonian and counterjet')
52 | ax.plot(t, Fnu_cj_only, label='counterjet')
53 | ax.plot(t, Fnu_default, label='Default (no Deep Newtonian or counterjet)')
54 | ax.legend()
55 |
56 | ax.set(xscale='log', xlabel=r'$t$ (s)',
57 | yscale='log', ylabel=r'$F_\nu$[$3\times 10^{9}$ Hz] (mJy)')
58 |
59 | fig.tight_layout()
60 | print("Saving figure lc_late_time.png")
61 | fig.savefig("lc_late_time.png")
62 | plt.close(fig)
63 |
--------------------------------------------------------------------------------
/examples/plotLightCurve.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | # For convenience, place arguments into a dict.
6 | Z = {'jetType': grb.jet.TopHat, # Top-Hat jet
7 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Emission Spectrum
8 |
9 | 'thetaObs': 0.05, # Viewing angle in radians
10 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
11 | 'thetaCore': 0.1, # Half-opening angle in radians
12 | 'n0': 1.0, # circumburst density in cm^{-3}
13 | 'p': 2.2, # electron energy distribution index
14 | 'epsilon_e': 0.1, # epsilon_e
15 | 'epsilon_B': 0.01, # epsilon_B
16 | 'xi_N': 1.0, # Fraction of electrons accelerated
17 | 'd_L': 1.0e28, # Luminosity distance in cm
18 | 'z': 0.55} # redshift
19 |
20 | # Space time points geometrically, from 10^3 s to 10^7 s
21 | t = np.geomspace(1.0e3, 1.0e7, 300)
22 |
23 | # Calculate flux in a single X-ray band (all times have same frequency)
24 | nu = np.empty(t.shape)
25 | nu[:] = 1.0e18
26 |
27 | # Calculate!
28 |
29 | Fnu = grb.fluxDensity(t, nu, **Z)
30 |
31 | # Write to a file
32 |
33 | print("Writing lc.txt")
34 | with open("lc.txt", 'w') as f:
35 | f.write("# nu " + str(nu) + '\n')
36 | f.write("# t(s) Fnu(mJy)\n")
37 | for i in range(len(t)):
38 | f.write("{0:.6e} {1:.6e}\n".format(t[i], Fnu[i]))
39 |
40 | # Plot!
41 |
42 | print("Plotting")
43 | fig, ax = plt.subplots(1, 1)
44 |
45 | ax.plot(t, Fnu)
46 |
47 | ax.set(xscale='log', xlabel=r'$t$ (s)',
48 | yscale='log', ylabel=r'$F_\nu$[$10^{18}$ Hz] (mJy)')
49 |
50 | fig.tight_layout()
51 | print("Saving figure lc.png")
52 | fig.savefig("lc.png")
53 | plt.close(fig)
54 |
--------------------------------------------------------------------------------
/examples/plotMultibandLightCurve.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | # Jet Parameters
6 | Z = {'jetType': grb.jet.Gaussian, # Gaussian jet
7 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Emission Spectrum
8 |
9 | 'thetaObs': 0.3, # Viewing angle in radians
10 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
11 | 'thetaCore': 0.05, # Half-opening angle in radians
12 | 'thetaWing': 0.4, # Outer truncation angle
13 | 'n0': 1.0e-3, # circumburst density in cm^{-3}
14 | 'p': 2.2, # electron energy distribution index
15 | 'epsilon_e': 0.1, # epsilon_e
16 | 'epsilon_B': 0.0001, # epsilon_B
17 | 'xi_N': 1.0, # Fraction of electrons accelerated
18 | 'd_L': 1.36e26, # Luminosity distance in cm
19 | 'z': 0.01} # redshift
20 |
21 | # Time and Frequencies
22 | ta = 1.0e-1 * grb.day2sec
23 | tb = 1.0e3 * grb.day2sec
24 | t = np.geomspace(ta, tb, num=100)
25 |
26 | nuR = 6.0e9
27 | nuO = 1.0e14
28 | nuX = 1.0e18
29 |
30 | # Calculate!
31 | print("Calc Radio")
32 | FnuR = grb.fluxDensity(t, nuR, **Z)
33 | print("Calc Optical")
34 | FnuO = grb.fluxDensity(t, nuO, **Z)
35 | print("Calc X-ray")
36 | FnuX = grb.fluxDensity(t, nuX, **Z)
37 |
38 | # Plot!
39 | print("Plot")
40 |
41 | tday = t * grb.sec2day
42 |
43 | fig, ax = plt.subplots(1, 1)
44 | ax.plot(tday, FnuR, ls='-', label=r'$\nu=6$ GHz')
45 | ax.plot(tday, FnuO, ls='--', label=r'$\nu=10^{14}$ Hz')
46 | ax.plot(tday, FnuX, ls='-.', label=r'$\nu=10^{18}$ Hz')
47 | ax.set_xscale('log')
48 | ax.set_yscale('log')
49 | ax.set_xlabel(r'$t$ (d)')
50 | ax.set_ylabel(r'$F_\nu$ (mJy)')
51 | ax.legend()
52 | fig.tight_layout()
53 |
54 | print("Saving lc_multi.png")
55 | fig.savefig("lc_multi.png")
56 | plt.close(fig)
57 |
--------------------------------------------------------------------------------
/examples/plotMultibandLightCurveBatch.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | # Jet Parameters
6 | Z = {'jetType': grb.jet.Gaussian, # Gaussian jet
7 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Emission Spectrum
8 |
9 | 'thetaObs': 0.3, # Viewing angle in radians
10 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
11 | 'thetaCore': 0.05, # Half-opening angle in radians
12 | 'thetaWing': 0.4, # Outer truncation angle
13 | 'n0': 1.0e-3, # circumburst density in cm^{-3}
14 | 'p': 2.2, # electron energy distribution index
15 | 'epsilon_e': 0.1, # epsilon_e
16 | 'epsilon_B': 0.0001, # epsilon_B
17 | 'xi_N': 1.0, # Fraction of electrons accelerated
18 | 'd_L': 1.36e26, # Luminosity distance in cm
19 | 'z': 0.01} # redshift
20 |
21 | # Time and Frequencies
22 | Nt = 100
23 | Nnu = 3
24 | t = np.empty((Nt, Nnu))
25 | nu = np.empty((Nt, Nnu))
26 |
27 | ta = 1.0e-1 * grb.day2sec
28 | tb = 1.0e3 * grb.day2sec
29 | nuR = 6.0e9
30 | nuO = 1.0e14
31 | nuX = 1.0e18
32 |
33 | t[:, :] = np.geomspace(ta, tb, num=100)[:, None]
34 | nu[:, 0] = nuR
35 | nu[:, 1] = nuO
36 | nu[:, 2] = nuX
37 |
38 | # Calculate!
39 | print("Calculate!")
40 | Fnu = grb.fluxDensity(t, nu, **Z)
41 |
42 | # Plot!
43 | print("Plot")
44 |
45 | tday = t * grb.sec2day
46 |
47 | fig, ax = plt.subplots(1, 1)
48 | ax.plot(tday[:, 0], Fnu[:, 0], ls='-', label=r'$\nu=6$ GHz')
49 | ax.plot(tday[:, 1], Fnu[:, 1], ls='--', label=r'$\nu=10^{14}$ Hz')
50 | ax.plot(tday[:, 2], Fnu[:, 2], ls='-.', label=r'$\nu=10^{18}$ Hz')
51 | ax.set_xscale('log')
52 | ax.set_yscale('log')
53 | ax.set_xlabel(r'$t$ (d)')
54 | ax.set_ylabel(r'$F_\nu$ (mJy)')
55 | ax.legend()
56 | fig.tight_layout()
57 |
58 | print("Saving lc_multi_batch.png")
59 | fig.savefig("lc_multi_batch.png")
60 | plt.close(fig)
61 |
--------------------------------------------------------------------------------
/examples/plotSpecType.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | # Set up a basic afterglow
6 | Z = dict(jetType=grb.jet.TopHat,
7 | specType=grb.jet.SimpleSpec,
8 | thetaObs=0.0,
9 | E0=1.0e53,
10 | thetaCore=0.1,
11 | n0=1.0,
12 | p=2.5,
13 | epsilon_e=0.1,
14 | epsilon_B=0.01,
15 | xi_N=1.0,
16 | d_L=2.0e28,
17 | z=1.0)
18 |
19 | # Make a second argument dict, with epsilon_e bar activated
20 | # The "epsilon_e" parameter is now treated as "epsilon_e_bar"
21 | #
22 | # Join specTypes with " | "
23 | Z_ebar = Z.copy()
24 | Z_ebar['specType'] = grb.jet.SimpleSpec | grb.jet.EpsEBar
25 |
26 | # Make an SED at 1 day
27 | t = 1.0 * grb.day2sec
28 | nu = np.geomspace(1.0e6, 1.0e20, 200)
29 |
30 | # Compute the flux.
31 | Fnu_p25 = grb.fluxDensity(t, nu, **Z) # epsilon_e = 0.1
32 | Fnu_ebar1_p25 = grb.fluxDensity(t, nu, **Z_ebar) # epsilon_e_bar = 0.1
33 |
34 | # Compute the epsilon_e_bar value that corresponds to the actual epsilon_e
35 | # at p=2.5
36 | eps_e = Z['epsilon_e']
37 | p = Z['p']
38 | eps_e_bar = eps_e * (p-2) / (p-1)
39 |
40 | # Update the args and compute the flux
41 | Z_ebar['epsilon_e'] = eps_e_bar
42 | Fnu_ebar_p25 = grb.fluxDensity(t, nu, **Z_ebar)
43 |
44 | # Now compute both models with p = 2.01 (not updating epsilon_e
45 | # or epsilon_e_bar)
46 | Z['p'] = 2.01
47 | Z_ebar['p'] = 2.01
48 | Fnu_p201 = grb.fluxDensity(t, nu, **Z)
49 | Fnu_ebar_p201 = grb.fluxDensity(t, nu, **Z_ebar)
50 |
51 | # Now just for fun, compute flux for p < 2.0. The standard model will
52 | # return an error, so only "ebar" here
53 |
54 | Z_ebar['p'] = 1.5
55 | Fnu_ebar_p15 = grb.fluxDensity(t, nu, **Z_ebar)
56 |
57 | fig, ax = plt.subplots(1, 1)
58 |
59 | ax.plot(nu, Fnu_p25, ls='--', color='C0', lw=2,
60 | label=r'Vanilla $\epsilon_e=0.1$ $p=2.5$')
61 | ax.plot(nu, Fnu_ebar1_p25, ls='-.', color='C0', lw=1,
62 | label=r'Ebar $\bar{\epsilon}_e=0.1$ $p=2.5$')
63 | ax.plot(nu, Fnu_ebar_p25, ls='-', color='C0', lw=1,
64 | label=r'Ebar $\epsilon_e=0.1$ $p=2.5$')
65 |
66 | ax.plot(nu, Fnu_p201, ls='--', color='C1', lw=2,
67 | label=r'Vanilla $\epsilon_e=0.1$ $p=2.01$')
68 | ax.plot(nu, Fnu_ebar_p201, ls='-', color='C1', lw=1,
69 | label=r'Ebar $\epsilon_e=0.1$ $p=2.01$')
70 |
71 | ax.plot(nu, Fnu_ebar_p15, ls='-', color='C2', lw=1,
72 | label=r'Ebar $\epsilon_e=0.1$ $p=1.5$')
73 |
74 | ax.set(xlabel=r'$\nu$ (Hz)', xscale='log',
75 | ylabel=r'$F_\nu$ (mJy)', yscale='log')
76 |
77 | ax.legend()
78 |
79 | figname = "example_ebar.png"
80 | print("Saving", figname)
81 | fig.savefig(figname)
82 |
83 | plt.show()
84 |
--------------------------------------------------------------------------------
/examples/plotSpectrum.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import afterglowpy as grb
4 |
5 | Z = {'jetType': grb.jet.TopHat, # Top-Hat jet
6 | 'specType': grb.jet.SimpleSpec, # Basic Synchrotron Emission Spectrum
7 |
8 | 'thetaObs': 0.05, # Viewing angle in radians
9 | 'E0': 1.0e53, # Isotropic-equivalent energy in erg
10 | 'thetaCore': 0.1, # Half-opening angle in radians
11 | 'n0': 1.0, # circumburst density in cm^{-3}
12 | 'p': 2.2, # electron energy distribution index
13 | 'epsilon_e': 0.1, # epsilon_e
14 | 'epsilon_B': 0.01, # epsilon_B
15 | 'xi_N': 1.0, # Fraction of electrons accelerated
16 | 'd_L': 1.0e28, # Luminosity distance in cm
17 | 'z': 0.55} # redshift
18 |
19 |
20 | nua = 1.0e0 # Low Frequencies in Hz
21 | nub = 1.0e20 # High Frequencies in Hz
22 |
23 | t = 1.0 * grb.day2sec # spectrum at 1 day
24 | nu = np.geomspace(nua, nub, num=100)
25 |
26 | print("Calculating")
27 | Fnu = grb.fluxDensity(t, nu, **Z)
28 |
29 | print("Writing spec.txt")
30 | with open("spec.txt", 'w') as f:
31 | f.write("# t " + str(t) + ' (s)\n')
32 | f.write("# nu(Hz) Fnu(mJy)\n")
33 | for i in range(len(nu)):
34 | f.write("{0:.6e} {1:.6e}\n".format(nu[i], Fnu[i]))
35 |
36 | print("Plotting")
37 | fig, ax = plt.subplots(1, 1)
38 |
39 | ax.plot(nu, Fnu)
40 |
41 | ax.set_xscale('log')
42 | ax.set_yscale('log')
43 | ax.set_xlabel(r'$\nu$ (Hz)')
44 | ax.set_ylabel(r'$F_\nu$[1 day] (mJy)')
45 |
46 | fig.tight_layout()
47 | print("Saving figure spec.png")
48 | fig.savefig("spec.png")
49 | plt.close(fig)
50 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 |
3 | requires = ["setuptools",
4 | "wheel",
5 | "numpy<2.0,<3; python_version<'3.9'",
6 | "numpy>=2.0,<3; python_version>='3.9' and python_version<'3.13'",
7 | "numpy>=2.1,<3; python_version>='3.13'"]
8 |
9 | build-backend = 'setuptools.build_meta'
10 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import site
3 | from setuptools import setup, Extension
4 | import numpy as np
5 | # import imp
6 |
7 | # PEP 517 Workaround for edittable user installs
8 | site.ENABLE_USER_SITE = True
9 |
10 | # version = imp.load_source('afterglowpy.version', 'afterglowpy/version.py')
11 |
12 | version = {}
13 | with open("afterglowpy/version.py", "r") as f:
14 | exec(f.read(), version)
15 |
16 | with open("README.md", "r") as f:
17 | long_description = f.read()
18 |
19 | inc = [np.get_include()]
20 | libs = []
21 | libdirs = []
22 |
23 | jetsources = ["afterglowpy/jetmodule.c", "afterglowpy/offaxis_struct_funcs.c",
24 | "afterglowpy/integrate.c", "afterglowpy/shockEvolution.c",
25 | "afterglowpy/interval.c"]
26 | jetdepends = ["afterglowpy/offaxis_struct.h",
27 | "afterglowpy/shockEvolution.h", "afterglowpy/interval.h"]
28 |
29 | shocksources = ["afterglowpy/shockmodule.c", "afterglowpy/shockEvolution.c"]
30 | shockdepends = ["afterglowpy/shockEvolution.h",
31 | "afterglowpy/offaxis_struct.h"]
32 |
33 | jetmodule = Extension('afterglowpy.jet', sources=jetsources, include_dirs=inc,
34 | depends=jetdepends, extra_compile_args=['-Wall'])
35 | shockmodule = Extension('afterglowpy.shock', sources=shocksources,
36 | include_dirs=inc, depends=shockdepends,
37 | extra_compile_args=['-Wall'])
38 |
39 | setup(
40 | name='afterglowpy',
41 | version=version['__version__'],
42 | author="Geoffrey Ryan",
43 | author_email="gryan@perimeterinstitute.ca",
44 | description='GRB Afterglow Models',
45 | long_description=long_description,
46 | long_description_content_type='text/markdown',
47 | license='MIT',
48 | url='https://github.com/geoffryan/afterglowpy',
49 | packages=['afterglowpy'],
50 | ext_modules=[jetmodule, shockmodule],
51 | classifiers=[
52 | "Programming Language :: Python :: 3",
53 | "Programming Language :: C",
54 | "License :: OSI Approved :: MIT License",
55 | "Operating System :: OS Independent",
56 | "Development Status :: 4 - Beta",
57 | "Intended Audience :: Science/Research",
58 | "Topic :: Scientific/Engineering :: Astronomy"],
59 | install_requires=['numpy>=1.13', 'scipy>=0.14'],
60 | extras_require={
61 | 'docs': ['numpydoc']
62 | },
63 | project_urls={
64 | "Source Code": "https://github.com/geoffryan/afterglowpy",
65 | "Documentation": "https://afterglowpy.readthedocs.io"}
66 | )
67 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoffryan/afterglowpy/9251502434075a96d7086f38605de530350b478b/test/__init__.py
--------------------------------------------------------------------------------
/test/test_flux.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import numpy as np
3 | import afterglowpy.flux as flux
4 |
5 |
6 | class TestFlux(unittest.TestCase):
7 |
8 | def compareArrayEqualSingle(a1, a2):
9 | return (a1 == a2).all()
10 |
11 | def compareArrayTuple(self, func, argin, out):
12 | res = func(*argin)
13 | self.assertEqual(len(res), len(out))
14 |
15 | for i, a in enumerate(res):
16 | self.assertTrue((a == out[i]).all())
17 |
18 | def test_checkTNu(self):
19 | a1_5 = np.arange(5)
20 | a1_4 = np.arange(4)
21 | a2_45 = np.random.rand(4, 5)
22 | a2_46 = np.random.rand(4, 6)
23 | b2_45 = np.random.rand(4, 5)
24 | s1_1 = np.array([3.0])
25 | s1_4 = np.empty(4)
26 | s1_4[:] = 3.0
27 | s2_45 = np.empty((4, 5))
28 | s2_45[:] = 3.0
29 |
30 | # Check singleton args
31 | self.compareArrayTuple(flux.checkTNu, (s1_1, s1_1), (s1_1, s1_1))
32 | self.compareArrayTuple(flux.checkTNu, (3.0, s1_1), (s1_1, s1_1))
33 | self.compareArrayTuple(flux.checkTNu, (s1_1, 3.0), (s1_1, s1_1))
34 | self.compareArrayTuple(flux.checkTNu, (3.0, 3.0), (s1_1, s1_1))
35 |
36 | # Check correct 1d array args
37 | self.compareArrayTuple(flux.checkTNu, (a1_4, a1_4), (a1_4, a1_4))
38 | self.compareArrayTuple(flux.checkTNu, (s1_1, a1_4), (s1_4, a1_4))
39 | self.compareArrayTuple(flux.checkTNu, (a1_4, s1_1), (a1_4, s1_4))
40 | self.compareArrayTuple(flux.checkTNu, (3.0, a1_4), (s1_4, a1_4))
41 | self.compareArrayTuple(flux.checkTNu, (a1_4, 3.0), (a1_4, s1_4))
42 |
43 | # Check correct 2d array args
44 | self.compareArrayTuple(flux.checkTNu, (a2_45, b2_45), (a2_45, b2_45))
45 | self.compareArrayTuple(flux.checkTNu, (s1_1, a2_45), (s2_45, a2_45))
46 | self.compareArrayTuple(flux.checkTNu, (a2_45, s1_1), (a2_45, s2_45))
47 | self.compareArrayTuple(flux.checkTNu, (3.0, a2_45), (s2_45, a2_45))
48 | self.compareArrayTuple(flux.checkTNu, (a2_45, 3.0), (a2_45, s2_45))
49 |
50 | # Check incorrect args
51 | self.assertRaises(ValueError, flux.checkTNu, a1_4, a1_5)
52 | self.assertRaises(ValueError, flux.checkTNu, a1_4, a2_45)
53 | self.assertRaises(ValueError, flux.checkTNu, a2_45, a2_46)
54 |
55 | def test_checkThetaPhiTNu(self):
56 | a1_4 = np.arange(4)
57 | a1_5 = np.arange(5)
58 | a1_6 = np.arange(6)
59 | a2_45 = np.random.rand(4, 5)
60 | a2_46 = np.random.rand(4, 6)
61 | s1_1 = np.array([3.0])
62 | s1_4 = np.empty(4)
63 | s1_4[:] = 3.0
64 | s1_5 = np.empty(5)
65 | s1_5[:] = 3.0
66 | s1_6 = np.empty(6)
67 | s1_6[:] = 3.0
68 | s1_7 = np.empty(7)
69 | s1_7[:] = 3.0
70 | s2_45 = np.empty((4, 5))
71 | s2_45[:] = 3.0
72 |
73 | # Check singleton args
74 | self.compareArrayTuple(flux.checkThetaPhiTNu,
75 | (s1_1, s1_1, s1_1, s1_1),
76 | (s1_1, s1_1, s1_1, s1_1))
77 | self.compareArrayTuple(flux.checkThetaPhiTNu,
78 | (3.0, s1_1, s1_1, s1_1),
79 | (s1_1, s1_1, s1_1, s1_1))
80 | self.compareArrayTuple(flux.checkThetaPhiTNu,
81 | (s1_1, 3.0, s1_1, s1_1),
82 | (s1_1, s1_1, s1_1, s1_1))
83 | self.compareArrayTuple(flux.checkThetaPhiTNu,
84 | (s1_1, s1_1, 3.0, s1_1),
85 | (s1_1, s1_1, s1_1, s1_1))
86 | self.compareArrayTuple(flux.checkThetaPhiTNu,
87 | (s1_1, s1_1, s1_1, 3.0),
88 | (s1_1, s1_1, s1_1, s1_1))
89 | self.compareArrayTuple(flux.checkThetaPhiTNu,
90 | (3.0, 3.0, s1_1, s1_1),
91 | (s1_1, s1_1, s1_1, s1_1))
92 | self.compareArrayTuple(flux.checkThetaPhiTNu,
93 | (3.0, s1_1, 3.0, s1_1),
94 | (s1_1, s1_1, s1_1, s1_1))
95 | self.compareArrayTuple(flux.checkThetaPhiTNu,
96 | (3.0, s1_1, s1_1, 3.0),
97 | (s1_1, s1_1, s1_1, s1_1))
98 | self.compareArrayTuple(flux.checkThetaPhiTNu,
99 | (s1_1, 3.0, 3.0, s1_1),
100 | (s1_1, s1_1, s1_1, s1_1))
101 | self.compareArrayTuple(flux.checkThetaPhiTNu,
102 | (s1_1, 3.0, s1_1, 3.0),
103 | (s1_1, s1_1, s1_1, s1_1))
104 | self.compareArrayTuple(flux.checkThetaPhiTNu,
105 | (s1_1, s1_1, 3.0, 3.0),
106 | (s1_1, s1_1, s1_1, s1_1))
107 | self.compareArrayTuple(flux.checkThetaPhiTNu,
108 | (3.0, 3.0, 3.0, s1_1),
109 | (s1_1, s1_1, s1_1, s1_1))
110 | self.compareArrayTuple(flux.checkThetaPhiTNu,
111 | (3.0, 3.0, s1_1, 3.0),
112 | (s1_1, s1_1, s1_1, s1_1))
113 | self.compareArrayTuple(flux.checkThetaPhiTNu,
114 | (3.0, s1_1, 3.0, 3.0),
115 | (s1_1, s1_1, s1_1, s1_1))
116 | self.compareArrayTuple(flux.checkThetaPhiTNu,
117 | (s1_1, 3.0, 3.0, 3.0),
118 | (s1_1, s1_1, s1_1, s1_1))
119 |
120 | # Check correct 1d array args
121 | self.compareArrayTuple(flux.checkThetaPhiTNu,
122 | (a1_4, a1_4, a1_4, a1_4),
123 | (a1_4, a1_4, a1_4, a1_4))
124 | self.compareArrayTuple(flux.checkThetaPhiTNu,
125 | (s1_1, a1_4, a1_4, a1_4),
126 | (s1_4, a1_4, a1_4, a1_4))
127 | self.compareArrayTuple(flux.checkThetaPhiTNu,
128 | (a1_4, s1_4, a1_4, a1_4),
129 | (a1_4, s1_4, a1_4, a1_4))
130 | self.compareArrayTuple(flux.checkThetaPhiTNu,
131 | (a1_4, a1_4, s1_4, a1_4),
132 | (a1_4, a1_4, s1_4, a1_4))
133 | self.compareArrayTuple(flux.checkThetaPhiTNu,
134 | (a1_4, a1_4, a1_4, s1_4),
135 | (a1_4, a1_4, a1_4, s1_4))
136 | self.compareArrayTuple(flux.checkThetaPhiTNu,
137 | (3.0, a1_4, a1_4, a1_4),
138 | (s1_4, a1_4, a1_4, a1_4))
139 | self.compareArrayTuple(flux.checkThetaPhiTNu,
140 | (a1_4, 3.0, a1_4, a1_4),
141 | (a1_4, s1_4, a1_4, a1_4))
142 | self.compareArrayTuple(flux.checkThetaPhiTNu,
143 | (a1_4, a1_4, 3.0, a1_4),
144 | (a1_4, a1_4, s1_4, a1_4))
145 | self.compareArrayTuple(flux.checkThetaPhiTNu,
146 | (a1_4, a1_4, a1_4, 3.0),
147 | (a1_4, a1_4, a1_4, s1_4))
148 | self.compareArrayTuple(flux.checkThetaPhiTNu,
149 | (a1_4, 3.0, 3.0, 3.0),
150 | (a1_4, s1_4, s1_4, s1_4))
151 | self.compareArrayTuple(flux.checkThetaPhiTNu,
152 | (3.0, a1_4, 3.0, 3.0),
153 | (s1_4, a1_4, s1_4, s1_4))
154 | self.compareArrayTuple(flux.checkThetaPhiTNu,
155 | (3.0, 3.0, a1_4, 3.0),
156 | (s1_4, s1_4, a1_4, s1_4))
157 | self.compareArrayTuple(flux.checkThetaPhiTNu,
158 | (3.0, 3.0, 3.0, a1_4),
159 | (s1_4, s1_4, s1_4, a1_4))
160 |
161 | # Check correct 2d array args
162 | self.compareArrayTuple(flux.checkThetaPhiTNu,
163 | (a2_45, a2_45, a2_45, a2_45),
164 | (a2_45, a2_45, a2_45, a2_45))
165 | self.compareArrayTuple(flux.checkThetaPhiTNu,
166 | (s1_1, a2_45, a2_45, a2_45),
167 | (s2_45, a2_45, a2_45, a2_45))
168 | self.compareArrayTuple(flux.checkThetaPhiTNu,
169 | (a2_45, s2_45, a2_45, a2_45),
170 | (a2_45, s2_45, a2_45, a2_45))
171 | self.compareArrayTuple(flux.checkThetaPhiTNu,
172 | (a2_45, a2_45, s2_45, a2_45),
173 | (a2_45, a2_45, s2_45, a2_45))
174 | self.compareArrayTuple(flux.checkThetaPhiTNu,
175 | (a2_45, a2_45, a2_45, s2_45),
176 | (a2_45, a2_45, a2_45, s2_45))
177 | self.compareArrayTuple(flux.checkThetaPhiTNu,
178 | (3.0, 3.0, a2_45, a2_45),
179 | (s2_45, s2_45, a2_45, a2_45))
180 | self.compareArrayTuple(flux.checkThetaPhiTNu,
181 | (a2_45, a2_45, 3.0, 3.0),
182 | (a2_45, a2_45, s2_45, s2_45))
183 |
184 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
185 | a1_4, a1_4, a1_4, a1_5)
186 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
187 | a1_4, a1_4, a1_5, a1_4)
188 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
189 | a1_4, a1_5, a1_4, a1_4)
190 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
191 | a1_5, a1_4, a1_4, a1_4)
192 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
193 | a1_6, a1_4, a1_4, a1_5)
194 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
195 | a1_4, a1_6, a1_5, a1_4)
196 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
197 | a1_4, a1_5, a1_6, a1_4)
198 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
199 | a1_5, a1_4, a1_4, a1_6)
200 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
201 | 3.0, a1_4, a1_4, a1_5)
202 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
203 | a1_4, 3.0, a1_5, a1_4)
204 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
205 | a1_4, a1_5, 3.0, a1_4)
206 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
207 | a1_5, a1_4, a1_4, 3.0)
208 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
209 | a2_45, a2_45, a2_45, a2_46)
210 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
211 | a2_45, a2_45, a2_46, a2_45)
212 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
213 | a2_45, a2_46, a2_45, a2_45)
214 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
215 | a2_46, a2_45, a2_45, a2_45)
216 | self.assertRaises(ValueError, flux.checkThetaPhiTNu,
217 | 3.0, a2_45, a2_45, a1_4)
218 |
219 | def test_checkJetArgs(self):
220 |
221 | EPS = 1.0e-8
222 | Y0 = np.array([0.05, 1.0e53, 0.1, 0.4, 4, 0, 0, 0, 1.0, 2.2, 0.1,
223 | 0.01, 0.99, 1.0e28])
224 | Z0 = {'z': 0.5}
225 |
226 | Y = Y0.copy()
227 | Z = {}
228 | for k in Z0:
229 | Z[k] = Z0[k]
230 |
231 | models = [-2, -1, 0, 1, 2, 4]
232 | s = 0
233 |
234 | for m in models:
235 | self.assertIsNone(flux.checkJetArgs(m, s, *Y0, **Z))
236 |
237 | # theta_obs
238 | Y[0] = 0.0
239 | self.assertIsNone(flux.checkJetArgs(m, s, *Y, **Z))
240 | Y[0] = -0.1
241 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
242 | Y[0] = 0.5*np.pi + EPS
243 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
244 | Y[0] = Y0[0]
245 |
246 | # E0
247 | Y[1] = 0.0
248 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
249 | Y[1] = -1.0
250 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
251 | Y[1] = Y0[1]
252 |
253 | # theta_C
254 | Y[2] = 0.0
255 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
256 | Y[2] = -0.1
257 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
258 | Y[2] = 0.5*np.pi + EPS
259 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
260 | Y[2] = Y0[2]
261 |
262 | # theta_W
263 | if m != -1:
264 | Y[3] = 0.0
265 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
266 | Y[3] = -0.1
267 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
268 | Y[3] = 0.5*np.pi + EPS
269 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
270 | Y[3] = Y0[3]
271 |
272 | # b
273 | if m == 4:
274 | Y[4] = 0.0
275 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
276 | Y[4] = -1.0
277 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
278 | Y[4] = Y0[4]
279 |
280 | # L0
281 | Y[5] = -1.0
282 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
283 | Y[5] = Y0[5]
284 |
285 | # t_s
286 | Y[7] = -1.0
287 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
288 | Y[7] = Y0[7]
289 |
290 | # n0
291 | Y[8] = 0.0
292 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
293 | Y[8] = -1.0
294 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
295 | Y[8] = Y0[8]
296 |
297 | # p
298 | Y[9] = 0.0
299 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
300 | Y[9] = 2.0
301 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
302 | Y[9] = 1.0
303 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
304 | Y[9] = -1.0
305 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
306 | Y[9] = Y0[9]
307 |
308 | s = 2
309 | Y[9] = 2.0
310 | self.assertIsNone(flux.checkJetArgs(m, s, *Y, **Z))
311 | Y[9] = 1.0
312 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
313 | Y[9] = 0.0
314 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
315 | Y[9] = -1.0
316 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
317 | Y[9] = Y0[9]
318 | s = 0
319 |
320 | # eps_e
321 | Y[10] = 1.0
322 | self.assertIsNone(flux.checkJetArgs(m, s, *Y, **Z))
323 | Y[10] = 1.0 + EPS
324 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
325 | Y[10] = 0.0
326 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
327 | Y[10] = -1.0
328 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
329 | Y[10] = Y0[10]
330 |
331 | # eps_B
332 | Y[11] = 1.0
333 | self.assertIsNone(flux.checkJetArgs(m, s, *Y, **Z))
334 | Y[11] = 1.0 + EPS
335 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
336 | Y[11] = 0.0
337 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
338 | Y[11] = -1.0
339 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
340 | Y[11] = Y0[11]
341 |
342 | # xi_N
343 | Y[12] = 1.0
344 | self.assertIsNone(flux.checkJetArgs(m, s, *Y, **Z))
345 | Y[12] = 1.0 + EPS
346 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
347 | Y[12] = 0.0
348 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
349 | Y[12] = -1.0
350 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
351 | Y[12] = Y0[12]
352 |
353 | # dL
354 | Y[13] = 0.0
355 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
356 | Y[13] = -1.0
357 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
358 | Y[13] = Y0[13]
359 |
360 | # z
361 | Z['z'] = -1.0
362 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
363 | Z['z'] = Z0['z']
364 |
365 | # specific for cone
366 | m = -2
367 | Y[3] = 0.5*Y[2]
368 | self.assertRaises(ValueError, flux.checkJetArgs, m, s, *Y, **Z)
369 |
370 | def test_checkCocoonArgs(self):
371 |
372 | EPS = 1.0e-8
373 | Y0 = np.array([10.0, 1.0, 1.0e53, 5, 1.0e-5, 0, 0, 0, 1.0, 2.2, 0.1,
374 | 0.01, 0.99, 1.0e28])
375 | Z0 = {'z': 0.5}
376 |
377 | Y = Y0.copy()
378 | Z = {}
379 | for k in Z0:
380 | Z[k] = Z0[k]
381 |
382 | models = [3]
383 | s = 0
384 |
385 | for m in models:
386 | self.assertIsNone(flux.checkCocoonArgs(m, s, *Y0, **Z))
387 |
388 | # u_max
389 | Y[0] = 0.0
390 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
391 | Y[0] = -1.0
392 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
393 | Y[0] = Y0[0]
394 |
395 | # u_min
396 | Y[1] = 0.0
397 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
398 | Y[1] = -1.0
399 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
400 | Y[1] = Y0[1]
401 |
402 | # Ei
403 | Y[2] = 0.0
404 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
405 | Y[2] = -0.1
406 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
407 | Y[2] = Y0[2]
408 |
409 | # Mej_solar
410 | Y[4] = 0.0
411 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s,
412 | *Y, **Z)
413 | Y[4] = -1.0
414 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s,
415 | *Y, **Z)
416 | Y[4] = Y0[4]
417 |
418 | # L0
419 | Y[5] = -1.0
420 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
421 | Y[5] = Y0[5]
422 |
423 | # t_s
424 | Y[7] = -1.0
425 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
426 | Y[7] = Y0[7]
427 |
428 | # n0
429 | Y[8] = 0.0
430 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
431 | Y[8] = -1.0
432 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
433 | Y[8] = Y0[8]
434 |
435 | # p
436 | Y[9] = 0.0
437 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
438 | Y[9] = 2.0
439 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
440 | Y[9] = 1.0
441 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
442 | Y[9] = -1.0
443 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
444 | Y[9] = Y0[9]
445 |
446 | # eps_e
447 | Y[10] = 1.0
448 | self.assertIsNone(flux.checkCocoonArgs(m, s, *Y, **Z))
449 | Y[10] = 1.0 + EPS
450 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
451 | Y[10] = 0.0
452 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
453 | Y[10] = -1.0
454 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
455 | Y[10] = Y0[10]
456 |
457 | # eps_B
458 | Y[11] = 1.0
459 | self.assertIsNone(flux.checkCocoonArgs(m, s, *Y, **Z))
460 | Y[11] = 1.0 + EPS
461 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
462 | Y[11] = 0.0
463 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
464 | Y[11] = -1.0
465 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
466 | Y[11] = Y0[11]
467 |
468 | # xi_N
469 | Y[12] = 1.0
470 | self.assertIsNone(flux.checkCocoonArgs(m, s, *Y, **Z))
471 | Y[12] = 1.0 + EPS
472 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
473 | Y[12] = 0.0
474 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
475 | Y[12] = -1.0
476 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
477 | Y[12] = Y0[12]
478 |
479 | # dL
480 | Y[13] = 0.0
481 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
482 | Y[13] = -1.0
483 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
484 | Y[13] = Y0[13]
485 |
486 | # z
487 | Z['z'] = -1.0
488 | self.assertRaises(ValueError, flux.checkCocoonArgs, m, s, *Y, **Z)
489 | Z['z'] = Z0['z']
490 |
491 |
492 | if __name__ == "__main__":
493 | unittest.main()
494 |
--------------------------------------------------------------------------------