• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nikhil-sarin / redback / 17297018917

28 Aug 2025 01:16PM UTC coverage: 86.258% (-1.3%) from 87.575%
17297018917

Pull #287

github

web-flow
Merge 0fcd153f0 into d0b2cb1d3
Pull Request #287: Afterglow optimisation

80 of 338 new or added lines in 1 file covered. (23.67%)

1 existing line in 1 file now uncovered.

13609 of 15777 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.2
/redback/transient_models/afterglow_models.py
1
from astropy.cosmology import Planck18 as cosmo  # noqa
1✔
2
from inspect import isfunction
1✔
3
from redback.utils import logger, citation_wrapper, calc_ABmag_from_flux_density, lambda_to_nu, bands_to_frequency
1✔
4
from redback.constants import day_to_s, speed_of_light, solar_mass, proton_mass, electron_mass, sigma_T
1✔
5
from redback.sed import get_correct_output_format_from_spectra
1✔
6
import astropy.units as uu
1✔
7
import numpy as np
1✔
8
from collections import namedtuple
1✔
9
from scipy.special import erf
1✔
10
try:
1✔
11
    import afterglowpy as afterglow
1✔
12

13
    jettype_dict = {'tophat': afterglow.jet.TopHat, 'gaussian': afterglow.jet.Gaussian,
1✔
14
                    'powerlaw_w_core': afterglow.jet.PowerLawCore, 'gaussian_w_core': afterglow.jet.GaussianCore,
15
                    'cocoon': afterglow.Spherical, 'smooth_power_law': afterglow.jet.PowerLaw,
16
                    'cone': afterglow.jet.Cone}
17
    spectype_dict = {'no_inverse_compton': 0, 'inverse_compton': 1}
1✔
18
except ModuleNotFoundError as e:
×
19
    logger.warning(e)
×
20
    afterglow = None
×
21

22
jet_spreading_models = ['tophat', 'cocoon', 'gaussian',
1✔
23
                          'kn_afterglow', 'cone_afterglow',
24
                          'gaussiancore', 'gaussian',
25
                          'smoothpowerlaw', 'powerlawcore',
26
                          'tophat']
27

28
import numba as nb
1✔
29

30
# Physical constants (as module-level constants for Numba)
31
MP = 1.6726231e-24  # g, mass of proton
1✔
32
ME = 9.1093897e-28  # g, mass of electron
1✔
33
CC = 2.99792453e10  # cm s^-1, speed of light
1✔
34
QE = 4.8032068e-10  # esu, electron charge
1✔
35
C2 = CC * CC
1✔
36
SIGT = (QE * QE / (ME * C2)) ** 2 * (8 * np.pi / 3)  # Thomson cross-section
1✔
37
FOURPI = 4 * np.pi
1✔
38
DAY_TO_S = 86400.0
1✔
39

40

41
# Numba-compiled utility functions
42
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
43
def get_segments_numba(thj, res):
1✔
44
    """Calculate jet segments - matches Python exactly"""
NEW
45
    latstep = thj / res
×
NEW
46
    rotstep = 2.0 * np.pi / res
×
NEW
47
    Nlatstep = int(res)
×
NEW
48
    Nrotstep = int(2 * np.pi / rotstep)
×
49

NEW
50
    Omi = np.zeros(Nlatstep * Nrotstep)
×
NEW
51
    phi = np.linspace(rotstep, Nrotstep * rotstep, Nrotstep)
×
52

53
    # Match the Python loop exactly
NEW
54
    for i in range(Nlatstep):
×
NEW
55
        start_idx = i * Nrotstep
×
NEW
56
        end_idx = (i + 1) * Nrotstep
×
NEW
57
        for j in range(Nrotstep):
×
NEW
58
            Omi[start_idx + j] = (phi[j] - (phi[j] - rotstep)) * (np.cos(i * latstep) - np.cos((i + 1) * latstep))
×
59

NEW
60
    thi = np.linspace(latstep - latstep / 2., Nlatstep * latstep - latstep / 2., Nlatstep)
×
NEW
61
    phii = np.linspace(rotstep - rotstep / 2., Nrotstep * rotstep - rotstep / 2., Nrotstep)
×
62

NEW
63
    return Omi, thi, phii, rotstep, latstep
×
64

65

66
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
67
def erf_approximation_numba(x):
1✔
68
    """Numba-compatible erf approximation that matches scipy.special.erf closely"""
69
    # Use a high-precision approximation
NEW
70
    a1 = 0.254829592
×
NEW
71
    a2 = -0.284496736
×
NEW
72
    a3 = 1.421413741
×
NEW
73
    a4 = -1.453152027
×
NEW
74
    a5 = 1.061405429
×
NEW
75
    p = 0.3275911
×
76

NEW
77
    result = np.zeros_like(x)
×
NEW
78
    for i in range(len(x)):
×
NEW
79
        sign = 1.0 if x[i] >= 0 else -1.0
×
NEW
80
        x_abs = abs(x[i])
×
81

82
        # A&S formula 7.1.26
NEW
83
        t = 1.0 / (1.0 + p * x_abs)
×
NEW
84
        y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * np.exp(-x_abs * x_abs)
×
85

NEW
86
        result[i] = sign * y
×
87

NEW
88
    return result
×
89

90

91
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
92
def get_structure_numba(gamma, en, thi, thc, method_id, s, a, thj):
1✔
93
    """Calculate jet structure - matches Python exactly"""
NEW
94
    n = thi.shape[0]
×
NEW
95
    Gs = np.full(n, gamma)
×
NEW
96
    Ei = np.full(n, en)
×
97

NEW
98
    if method_id == 1:  # Top-hat - match Python implementation exactly
×
NEW
99
        thj_used = min(thc, thj)
×
100
        # Use the erf approximation that matches Python
NEW
101
        erf_arg = -(thi - thj_used) * 1000.0
×
NEW
102
        fac = (erf_approximation_numba(erf_arg) * 0.5) + 0.5
×
NEW
103
        Gs = (Gs - 1) * fac + 1.0000000000001
×
NEW
104
        Ei *= fac
×
105

NEW
106
    elif method_id == 2:  # Gaussian
×
NEW
107
        fac = np.exp(-0.5 * (thi / thc) ** 2)
×
NEW
108
        Gs = (Gs - 1.0) * fac + 1.0000000000001
×
NEW
109
        Ei *= fac
×
110

NEW
111
    elif method_id == 3:  # Power-law
×
NEW
112
        for i in range(n):
×
NEW
113
            if thi[i] >= thc:
×
NEW
114
                fac_s = (thc / thi[i]) ** s
×
NEW
115
                fac_a = (thc / thi[i]) ** a
×
NEW
116
                Ei[i] *= fac_s
×
NEW
117
                Gs[i] = (Gs[i] - 1.0) * fac_a + 1.000000000000001
×
118

NEW
119
    elif method_id == 4:  # Alternative powerlaw
×
NEW
120
        fac = (1 + (thi / thc) ** 2) ** 0.5
×
NEW
121
        Gs = 1.000000000001 + (Gs - 1.0) * fac ** (-a)
×
NEW
122
        Ei *= fac ** (-s)
×
123

NEW
124
    elif method_id == 5:  # Two-Component
×
NEW
125
        for i in range(n):
×
NEW
126
            if thi[i] > thc:
×
NEW
127
                Gs[i] = a
×
NEW
128
                Ei[i] *= s
×
129

NEW
130
    elif method_id == 6:  # Double Gaussian
×
NEW
131
        for i in range(n):
×
132
            # Energy structure
NEW
133
            Ei[i] = Ei[i] * ((1.0 - s) * np.exp(-0.5 * (thi[i] / thc) ** 2) +
×
134
                             s * np.exp(-0.5 * (thi[i] / thj) ** 2))
135

136
            # Lorentz factor structure
NEW
137
            temp = ((1.0 - s) * np.exp(-0.5 * (thi[i] / thc) ** 2) +
×
138
                    s * np.exp(-0.5 * (thi[i] / thj) ** 2)) / \
139
                   ((1.0 - s / a) * np.exp(-0.5 * (thi[i] / thc) ** 2) +
140
                    (s / a) * np.exp(-0.5 * (thi[i] / thj) ** 2))
141

NEW
142
            if not np.isfinite(temp):
×
NEW
143
                Gs[i] = a
×
144
            else:
NEW
145
                Gs[i] = (Gs[i] - 1.0) * temp + 1.0000000000001
×
146

NEW
147
    return Gs, Ei
×
148

149

150
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
151
def get_obsangle_numba(phii, thi, tho):
1✔
152
    """Calculate observer angles - matches Python exactly"""
NEW
153
    phi = 0.0  # rotational symmetry
×
NEW
154
    sin_thi = np.sin(thi)
×
NEW
155
    cos_thi = np.cos(thi)
×
156

157
    # Match the Python broadcasting exactly
NEW
158
    n_thi = thi.shape[0]
×
NEW
159
    n_phi = phii.shape[0]
×
NEW
160
    Obsa = np.zeros(n_thi * n_phi)
×
161

NEW
162
    idx = 0
×
NEW
163
    for i in range(n_thi):
×
NEW
164
        for j in range(n_phi):
×
NEW
165
            f1 = np.sin(phi) * np.sin(tho) * np.sin(phii[j]) * sin_thi[i]
×
NEW
166
            f2 = np.cos(phi) * np.sin(tho) * np.cos(phii[j]) * sin_thi[i]
×
NEW
167
            ang = np.cos(tho) * cos_thi[i] + f2 + f1
×
168

169
            # Ensure valid range for acos
NEW
170
            if ang > 1.0:
×
NEW
171
                ang = 1.0
×
NEW
172
            elif ang < -1.0:
×
NEW
173
                ang = -1.0
×
174

NEW
175
            Obsa[idx] = np.arccos(ang)
×
NEW
176
            idx += 1
×
177

NEW
178
    return Obsa
×
179

180

181
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
182
def RK4_step_numba(ghat, dm_rk4, G_rk4, M, fac, therm):
1✔
183
    """Single RK4 step - matches Python exactly"""
NEW
184
    ghatm1 = ghat - 1.0
×
NEW
185
    dm_base10 = 10.0 ** dm_rk4
×
NEW
186
    G_rk4_sq = G_rk4 * G_rk4
×
NEW
187
    _G_rk4 = 1.0 / G_rk4
×
188

NEW
189
    return fac * dm_base10 * (ghat * (G_rk4_sq - 1.0) - ghatm1 * (G_rk4 - _G_rk4)) / \
×
190
        (M + dm_base10 * (therm + (1.0 - therm) * (2.0 * ghat * G_rk4 -
191
                                                   ghatm1 * (1 + 1.0 / G_rk4_sq))))
192

193

194
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
195
def get_gamma_numba(G0, Eps, therm, steps, n0, k):
1✔
196
    """Calculate gamma evolution - matches Python exactly"""
NEW
197
    n_g0 = G0.shape[0]
×
NEW
198
    steps_int = int(steps)
×
199

200
    # Parameter setting - match Python exactly
NEW
201
    Rmin = 1e10
×
NEW
202
    Rmax = 1e24
×
NEW
203
    Nmin = (FOURPI / (3.0 - k)) * n0 * Rmin ** (3.0 - k)
×
NEW
204
    Nmax = (FOURPI / (3.0 - k)) * n0 * Rmax ** (3.0 - k)
×
NEW
205
    dlogm = np.log10(Nmin * MP)
×
NEW
206
    h = np.log10(Nmax / Nmin) / float(steps_int)
×
NEW
207
    fac = -h * np.log(10.0)
×
208

NEW
209
    M = Eps / (G0 * C2)
×
210

211
    # Arrays for state
NEW
212
    state_G = np.zeros((steps_int, n_g0), dtype=np.float64)
×
NEW
213
    state_dm = np.zeros((steps_int, n_g0), dtype=np.float64)
×
NEW
214
    state_gH = np.zeros((steps_int, n_g0), dtype=np.float64)
×
215

216
    # Initial values
NEW
217
    G = G0.astype(np.float64).copy()
×
NEW
218
    dm = np.full(n_g0, dlogm, dtype=np.float64)
×
219

220
    # Main loop - match Python exactly
NEW
221
    for i in range(steps_int):
×
222
        # Store current values
NEW
223
        state_G[i] = G
×
NEW
224
        state_dm[i] = dm
×
225

226
        # Calculate intermediate values
NEW
227
        G2m1 = G * G - 1.0
×
NEW
228
        G2m1_root = np.sqrt(G2m1)
×
229

230
        # Temperature and adiabatic index - match Python exactly
NEW
231
        theta = G2m1_root * (G2m1_root + 1.07 * G2m1) / (3.0 * (1 + G2m1_root + 1.07 * G2m1))
×
NEW
232
        z = theta / (0.24 + theta)
×
NEW
233
        ghat = ((((((1.07136 * z - 2.39332) * z + 2.32513) * z -
×
234
                   0.96583) * z + 0.18203) * z - 1.21937) * z + 5.0) / 3.0
235

NEW
236
        state_gH[i] = ghat
×
237

238
        # 4th order Runge-Kutta - match Python exactly
NEW
239
        F1 = RK4_step_numba(ghat, dm, G, M, fac, therm)
×
NEW
240
        F2 = RK4_step_numba(ghat, dm + 0.5 * h, G + 0.5 * F1, M, fac, therm)
×
NEW
241
        F3 = RK4_step_numba(ghat, dm + 0.5 * h, G + 0.5 * F2, M, fac, therm)
×
NEW
242
        F4 = RK4_step_numba(ghat, dm + h, G + F3, M, fac, therm)
×
243

244
        # Update state - match Python exactly
NEW
245
        G = G + (F1 + 2.0 * (F2 + F3) + F4) / 6.0 + 1e-15
×
NEW
246
        dm = dm + h
×
247

NEW
248
    return state_G.T, (10.0 ** state_dm).T, state_gH.T
×
249

250

251
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
252
def calc_afterglow_step1_numba(G, dm, p, xp, Fx, EB, Ee, n, k, thi, ghat, rotstep, latstep, xiN, is_expansion, a1, res):
1✔
253
    """Calculate synchrotron parameters - matches Python exactly"""
NEW
254
    size = G.shape[0]
×
255

256
    # Ensure all variables are properly typed arrays
NEW
257
    Gm1 = G - 1.0
×
NEW
258
    G2 = G * G
×
NEW
259
    beta = np.sqrt(1 - 1.0 / G2)
×
NEW
260
    Ne = dm / MP
×
261

262
    # Side-ways expansion - match Python exactly
NEW
263
    cs = np.sqrt(C2 * ghat * (ghat - 1) * Gm1 / (1 + ghat * Gm1))
×
NEW
264
    te = np.arcsin(cs / (CC * np.sqrt(G2 - 1.0)))
×
265

266
    # Initialize OmG as array from the start
NEW
267
    OmG = np.zeros(size, dtype=np.float64)
×
268

269
    # Expansion effects - match Python logic exactly
NEW
270
    if is_expansion:
×
NEW
271
        ex = te / G ** (a1 + 1)
×
NEW
272
        fac = 0.5 * latstep
×
NEW
273
        for i in range(size):
×
NEW
274
            OmG[i] = rotstep * (np.cos(thi - fac) - np.cos(ex[i] / res + thi + fac))
×
275
    else:
NEW
276
        ex = np.ones(size, dtype=np.float64)  # Make sure ex is an array
×
NEW
277
        fac = 0.5 * latstep
×
NEW
278
        omg_val = rotstep * (np.cos(thi - fac) - np.cos(thi + fac))
×
NEW
279
        for i in range(size):
×
NEW
280
            OmG[i] = omg_val
×
281

282
    # Calculate R - match Python exactly
NEW
283
    exponent = np.ones(size, dtype=np.float64)
×
NEW
284
    for i in range(1, size):
×
NEW
285
        exponent[i] = ((1 - np.cos(latstep + ex[0])) / (1 - np.cos(latstep + ex[i]))) ** (1.0 / 2.0)
×
286

NEW
287
    R = ((3.0 - k) * Ne[:size] / (FOURPI * n)) ** (1.0 / (3.0 - k))
×
288

289
    # Match Python: R[1:] = np.diff(R) * exponent[1:size] ** (1. / (3. - k))
NEW
290
    R_orig = R.copy()
×
NEW
291
    for i in range(1, size):
×
NEW
292
        R[i] = (R_orig[i] - R_orig[i - 1]) * exponent[i] ** (1.0 / (3.0 - k))
×
293

NEW
294
    R = np.cumsum(R)  # Match Python: R = np.cumsum(R)
×
295

NEW
296
    n0 = n * R ** (-k)
×
297

298
    # Forward shock parameters - ensure all are arrays
NEW
299
    B = np.sqrt(2 * FOURPI * EB * n0 * MP * C2 * ((ghat * G + 1.0) / (ghat - 1.0)) * Gm1)
×
NEW
300
    gmm = np.sqrt(1.5 * FOURPI * QE / (SIGT * B))
×
301

302
    # Calculate gm - ensure it's always an array
NEW
303
    gm = np.zeros(size, dtype=np.float64)
×
304

NEW
305
    if p > 2:
×
NEW
306
        gp = (p - 2) / (p - 1)
×
NEW
307
        gm[:] = gp * (Ee / xiN) * Gm1 * (MP / ME)
×
NEW
308
    elif p == 2:
×
NEW
309
        for i in range(size):
×
NEW
310
            gp_i = 1 / np.log(gmm[i] / ((Ee / xiN) * Gm1[i] * (MP / ME)))
×
NEW
311
            gm[i] = gp_i * (Ee / xiN) * Gm1[i] * (MP / ME)
×
312
    else:  # p < 2
NEW
313
        for i in range(size):
×
NEW
314
            gm[i] = ((2 - p) / (p - 1) * (MP / ME) * (Ee / xiN) * Gm1[i] * gmm[i] ** (p - 2)) ** (1 / (p - 1))
×
315

316
    # Ensure all output arrays are properly formed
NEW
317
    nump = 3.0 * xp * gm * gm * QE * B / (FOURPI * ME * CC)
×
NEW
318
    Pp = xiN * Fx * ME * C2 * SIGT * B / (3.0 * QE)
×
NEW
319
    KT = gm * ME * C2
×
320

NEW
321
    return beta, Ne, OmG, R, B, gm, nump, Pp, KT
×
322

323

324
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
325
def calc_afterglow_step2_numba(Dl, Om0, rotstep, latstep, Obsa, beta, Ne, OmG, R, B, gm, nump, Pp, KT, G, is_expansion):
1✔
326
    """Calculate emission properties - matches Python exactly"""
NEW
327
    Dl2 = Dl * Dl
×
NEW
328
    NO = Om0 * Ne / FOURPI  # Match Python: NO = Om0 * Ne / self.fourpi
×
NEW
329
    cos_Obsa = np.cos(Obsa)
×
330

NEW
331
    size = G.shape[0]
×
332

333
    # Match Python expansion logic exactly
NEW
334
    Om = np.zeros(size, dtype=np.float64)
×
NEW
335
    thii = np.zeros(size, dtype=np.float64)
×
336

NEW
337
    if is_expansion:
×
NEW
338
        for i in range(size):
×
NEW
339
            Om[i] = max(Om0, OmG[i])  # Match Python: Om = np.maximum(Om0, OmG)
×
NEW
340
            thii[i] = np.arccos(1.0 - Om[i] / rotstep)
×
341
    else:
NEW
342
        for i in range(size):
×
NEW
343
            Om[i] = OmG[i]  # Match Python: Om = OmG
×
NEW
344
            thii[i] = np.arccos(1.0 - Om[i] / rotstep)
×
345

346
    # Match Python: R_diff = np.diff(R,prepend=0)
NEW
347
    R_diff = np.zeros(size, dtype=np.float64)
×
NEW
348
    R_diff[0] = R[0]
×
NEW
349
    for i in range(1, size):
×
NEW
350
        R_diff[i] = R[i] - R[i - 1]
×
351

NEW
352
    dt = R_diff * (1.0 / beta[:size] - cos_Obsa) / CC
×
NEW
353
    dto = R_diff * (1.0 / beta[:size] - 1.0) / CC
×
354

NEW
355
    tobs = np.cumsum(dt)
×
NEW
356
    tobso = np.cumsum(dto)
×
357

358
    # Forward shock - match Python exactly
NEW
359
    dop = 1.0 / (G * (1.0 - beta * cos_Obsa))
×
NEW
360
    gc = 6.0 * np.pi * ME * CC / (G * SIGT * B * B * tobso)
×
NEW
361
    nucp = 0.286 * 3.0 * gc * gc * QE * B / (FOURPI * ME * CC)
×
NEW
362
    num = dop * nump
×
NEW
363
    nuc = dop * nucp
×
NEW
364
    Fmax = NO * Pp * dop * dop * dop / (FOURPI * Dl2)
×
365

366
    # Self-absorption - match Python exactly
NEW
367
    FBB = 2 * Om * np.cos(thii) * dop * KT * R * R / (C2 * Dl2)
×
368

NEW
369
    return FBB, Fmax, nuc, num, tobs
×
370

371

372
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
373
def get_ag_numba(FBB, nuc, num, nu1, Fmax, p):
1✔
374
    """Calculate flux at frequency - matches Python exactly"""
NEW
375
    Fluxt = np.zeros(num.shape[0], dtype=np.float64)
×
376

377
    # Match Python boolean logic exactly
NEW
378
    for i in range(num.shape[0]):
×
379
        # Fast cooling regime
NEW
380
        if (nuc[i] < num[i]) and (nu1 < nuc[i]):
×
NEW
381
            Fluxt[i] = Fmax[i] * (nu1 / nuc[i]) ** (1.0 / 3.0)
×
NEW
382
        elif (nuc[i] < nu1) and (nuc[i] < num[i]):
×
NEW
383
            Fluxt[i] = Fmax[i] * (nu1 / nuc[i]) ** (-1.0 / 2.0)
×
NEW
384
        elif (num[i] < nu1) and (nuc[i] < num[i]):
×
NEW
385
            Fluxt[i] = Fmax[i] * (num[i] / nuc[i]) ** (-1.0 / 2.0) * (nu1 / num[i]) ** (-p / 2.0)
×
386
        # Slow cooling regime
NEW
387
        elif (num[i] < nuc[i]) and (nu1 < num[i]):
×
NEW
388
            Fluxt[i] = Fmax[i] * (nu1 / num[i]) ** (1.0 / 3.0)
×
NEW
389
        elif (num[i] < nu1) and (num[i] < nuc[i]):
×
NEW
390
            Fluxt[i] = Fmax[i] * (nu1 / num[i]) ** (-(p - 1.0) / 2.0)
×
NEW
391
        elif (nuc[i] < nu1) and (num[i] < nuc[i]):
×
NEW
392
            Fluxt[i] = Fmax[i] * (nuc[i] / num[i]) ** (-(p - 1.0) / 2.0) * (nu1 / nuc[i]) ** (-p / 2.0)
×
393

394
    # Self-absorption - match Python exactly
NEW
395
    FBB_adj = FBB * nu1 ** 2.0 * np.maximum(1.0, (nu1 / num) ** 0.5)
×
NEW
396
    for i in range(len(Fluxt)):
×
NEW
397
        Fluxt[i] = min(FBB_adj[i], Fluxt[i])
×
398

NEW
399
    return Fluxt
×
400

401
@nb.jit(nopython=True, fastmath=True, cache=True)
1✔
402
def get_gamma_refreshed_numba(G0, G1, Eps, Eps2, s1, therm, steps, n0, k):
1✔
403
    """Calculate gamma evolution with energy injection - matches Python exactly"""
NEW
404
    Eps0 = Eps
×
NEW
405
    n = n0
×
406

407
    # Parameter setting - match Python exactly
NEW
408
    Rmin = 1e10
×
NEW
409
    Rmax = 1e24
×
NEW
410
    Nmin = (FOURPI / 3.0) * n * Rmin ** (3.0 - k)
×
NEW
411
    Nmax = (FOURPI / 3.0) * n * Rmax ** (3.0 - k)
×
412

NEW
413
    dlogm = np.log10(Nmin * MP)
×
NEW
414
    h = (np.log10(Nmax) - np.log10(Nmin)) / steps
×
415

416
    # Arrays for state
NEW
417
    steps_int = int(steps)
×
NEW
418
    G = np.ones(steps_int + 1, dtype=np.float64)
×
NEW
419
    dm = np.zeros(steps_int, dtype=np.float64)
×
NEW
420
    gH = np.zeros(steps_int, dtype=np.float64)
×
421

422
    # Initial values
NEW
423
    G[0] = G0
×
NEW
424
    dm[0] = dlogm
×
NEW
425
    M = Eps / (G0 * C2)
×
426

427
    # Main loop - match Python exactly
NEW
428
    for i in range(steps_int):
×
429
        # Calculate intermediate values
NEW
430
        G2m1 = G[i] * G[i] - 1.0
×
NEW
431
        G2m1_root = np.sqrt(G2m1)
×
432

433
        # Temperature and adiabatic index - match Python exactly
NEW
434
        theta = G2m1_root / 3.0 * ((G2m1_root + 1.07 * G2m1) / (1 + G2m1_root + 1.07 * G2m1))
×
NEW
435
        z = theta / (0.24 + theta)
×
NEW
436
        ghat = (5.0 - 1.21937 * z + 0.18203 * z ** 2 - 0.96583 * z ** 3 +
×
437
                2.32513 * z ** 4 - 2.39332 * z ** 5 + 1.07136 * z ** 6) / 3.0
438

NEW
439
        dm[i] = dlogm + i * h
×
NEW
440
        gH[i] = ghat
×
441

442
        # Helper values for RK4
NEW
443
        dm_10 = 10.0 ** dm[i]
×
NEW
444
        h_log10 = h * np.log(10.0)
×
445

446
        # 4th order Runge-Kutta - match Python exactly
NEW
447
        F1 = -h_log10 * dm_10 * (ghat * G2m1 - (ghat - 1.0) * G[i] * (1.0 - 1.0 / G[i] ** 2)) / \
×
448
             (M + therm * dm_10 + (1.0 - therm) * dm_10 * (2.0 * ghat * G[i] - (ghat - 1.0) * (1.0 + 1.0 / G[i] ** 2)))
449

NEW
450
        G_F1_2 = G[i] + F1 / 2.0
×
NEW
451
        dm_h2_10 = 10.0 ** (dm[i] + h / 2.0)
×
NEW
452
        F2 = -h_log10 * dm_h2_10 * (ghat * (G_F1_2 ** 2 - 1.0) - (ghat - 1.0) * G_F1_2 * (1.0 - 1.0 / G_F1_2 ** 2)) / \
×
453
             (M + therm * dm_h2_10 + (1.0 - therm) * dm_h2_10 * (
454
                         2.0 * ghat * G_F1_2 - (ghat - 1.0) * (1.0 + 1.0 / G_F1_2 ** 2)))
455

NEW
456
        G_F2_2 = G[i] + F2 / 2.0
×
NEW
457
        F3 = -h_log10 * dm_h2_10 * (ghat * (G_F2_2 ** 2 - 1.0) - (ghat - 1.0) * G_F2_2 * (1.0 - 1.0 / G_F2_2 ** 2)) / \
×
458
             (M + therm * dm_h2_10 + (1.0 - therm) * dm_h2_10 * (
459
                         2.0 * ghat * G_F2_2 - (ghat - 1.0) * (1.0 + 1.0 / G_F2_2 ** 2)))
460

NEW
461
        G_F3 = G[i] + F3
×
NEW
462
        dm_h_10 = 10.0 ** (dm[i] + h)
×
NEW
463
        F4 = -h_log10 * dm_h_10 * (ghat * (G_F3 ** 2 - 1.0) - (ghat - 1.0) * G_F3 * (1.0 - 1.0 / G_F3 ** 2)) / \
×
464
             (M + therm * dm_h_10 + (1.0 - therm) * dm_h_10 * (
465
                         2.0 * ghat * G_F3 - (ghat - 1.0) * (1.0 + 1.0 / G_F3 ** 2)))
466

467
        # Update state
NEW
468
        G[i + 1] = G[i] + (F1 + 2.0 * F2 + 2.0 * F3 + F4) / 6.0
×
469

470
        # Energy injection - match Python exactly
NEW
471
        if G[i + 1] <= G1:
×
NEW
472
            Eps1 = Eps
×
NEW
473
            beta_ratio = np.sqrt(G[i + 1] ** 2 - 1.0) / np.sqrt(G1 ** 2 - 1.0)
×
NEW
474
            Eps = min(Eps0 * (beta_ratio ** (-s1)), Eps2)
×
NEW
475
            M += (Eps - Eps1) / (G[i] * C2)
×
476

477
    # Return arrays matching the Python version
NEW
478
    G_out = G[:-1]  # Remove the last element to match Python size
×
NEW
479
    dm_out = 10.0 ** dm
×
NEW
480
    gH_out = gH
×
481

NEW
482
    return G_out, dm_out, gH_out
×
483

484
# Main class with Numba acceleration
485
class RedbackAfterglows:
1✔
486
    def __init__(self, k, n, epsb, epse, g0, ek, thc, thj, tho, p, exp, time, freq, redshift, Dl,
1✔
487
                 extra_structure_parameter_1, extra_structure_parameter_2, method='TH', res=100, steps=int(500), xiN=1,
488
                 a1=1):
489
        """
490
        A general class for afterglow models implemented directly in redback.
491
        This class is not meant to be used directly but instead via the interface for each specific model.
492
        The afterglows are based on the method shown in Lamb, Mandel & Resmi 2018 and other papers.
493
        Script was originally written by En-Tzu Lin <entzulin@gapp.nthu.edu.tw> and Gavin Lamb <g.p.lamb@ljmu.ac.uk>
494
        and modified and implemented into redback by Nikhil Sarin <nsarin.astro@gmail.com>.
495
        Includes wind-like mediums, expansion and multiple jet structures.
496
        Includes SSA and uses Numba for acceleration.
497

498
        :param k: 0 or 2 for constant or wind density
499
        :param n: ISM, ambient number density
500
        :param epsb: magnetic fraction
501
        :param epse: electron fraction
502
        :param g0: initial Lorentz factor
503
        :param ek: kinetic energy
504
        :param thc: core angle
505
        :param thj: jet outer angle. For tophat jets thc=thj
506
        :param tho: observers viewing angle
507
        :param p: electron power-law index
508
        :param exp: Boolean for whether to include sound speed expansion
509
        :param time: lightcurve time steps
510
        :param freq: lightcurve frequencies
511
        :param redshift: source redshift
512
        :param Dl: luminosity distance
513
        :param extra_structure_parameter_1: Extra structure specific parameter #1.
514
            Specifically, this parameter sets;
515
            The index on energy for power-law jets.
516
            The fractional energy contribution for the Double Gaussian (must be less than 1).
517
            The energy fraction  for the outer sheath for two-component jets (must be less than 1).
518
            Unused for tophat or Gaussian jets.
519
        :param extra_structure_parameter_2: Extra structure specific parameter #2.
520
            Specifically, this parameter sets;
521
            The index on lorentz factor for power-law jets.
522
            The lorentz factor for second Gaussian (must be less than 1).
523
            The lorentz factor  for the outer sheath for two-component jets (must be less than 1).
524
            Unused for tophat or Gaussian jets.
525
        :param method: Type of jet structure to use. Defaults to 'TH' for tophat jet.
526
            Other options are '2C', 'GJ', 'PL', 'PL2', 'DG'. Corresponding to two component, gaussian jet, powerlaw,
527
            alternative powerlaw and double Gaussian.
528
        :param res: resolution
529
        :param steps: number of steps used to resolve Gamma and dm
530
        :param XiN: fraction of electrons that get accelerated
531
        :param a1: the expansion description, a1 = 0 sound speed, a1 = 1 Granot & Piran 2012
532
        """
533
        self.k = float(k)
1✔
534
        if self.k == 0:
1✔
535
            self.n = float(n)
1✔
536
        elif self.k == 2:
×
NEW
537
            self.n = float(n * 3e35)
×
538
        else:
NEW
539
            self.n = float(n)
×
540

541
        self.epsB = float(epsb)
1✔
542
        self.epse = float(epse)
1✔
543
        self.g0 = float(g0)
1✔
544
        self.ek = float(ek)
1✔
545
        self.thc = float(thc)
1✔
546
        self.thj = float(thj)
1✔
547
        self.tho = float(tho)
1✔
548
        self.p = float(p)
1✔
549
        self.exp = bool(exp)
1✔
550
        self.t = time
1✔
551
        self.freq = freq
1✔
552
        self.z = float(redshift)
1✔
553
        self.Dl = float(Dl)
1✔
554
        self.method = method
1✔
555
        self.s = float(extra_structure_parameter_1)
1✔
556
        self.a = float(extra_structure_parameter_2)
1✔
557
        self.res = float(res)
1✔
558
        self.steps = int(steps)
1✔
559
        self.xiN = float(xiN)
1✔
560
        self.a1 = float(a1)
1✔
561
        self.is_expansion = self.exp
1✔
562

563
        # Method ID for Numba
564
        self.method_id = self._method_to_id(method)
1✔
565

566
    def _method_to_id(self, method):
1✔
567
        """Convert method string to integer for Numba"""
568
        method_map = {"TH": 1, "Gaussian": 2, "GJ": 2, "PL": 3, "PL-alt": 4, "PL2": 4,
1✔
569
                      "Two-Component": 5, "2C": 5, "Double-Gaussian": 6, "DG": 6}
570
        return method_map.get(method, 1)
1✔
571

572
    def get_lightcurve(self):
1✔
573
        if (self.k != 0) and (self.k != 2):
1✔
574
            raise ValueError("k must either be 0 or 2")
×
575
        if (self.p < 1.2) or (self.p > 3.4):
1✔
576
            raise ValueError("p is out of range, 1.2 < p < 3.4")
×
577

578
        # Parameters from Wijers & Galama 1999
579
        pxf = np.array([[1.0, 3.0, 0.41], [1.2, 1.4, 0.44], [1.4, 1.1, 0.48], [1.6, 0.86, 0.53], [1.8, 0.725, 0.56],
1✔
580
                        [2.0, 0.637, 0.59], [2.2, 0.579, 0.612], [2.5, 0.520, 0.630], [2.7, 0.487, 0.641],
581
                        [3.0, 0.451, 0.659], [3.2, 0.434, 0.660], [3.4, 0.420, 0.675]])
582
        xp = np.interp(self.p, pxf[:, 0], pxf[:, 1])  # dimensionless spectral peak
1✔
583
        Fx = np.interp(self.p, pxf[:, 0], pxf[:, 2])  # dimensionless peak flux
1✔
584
        nu0 = np.unique(self.freq)
1✔
585
        nu = nu0 * (1 + self.z)
1✔
586
        nu = np.array(nu)
1✔
587

588
        # Use Numba-accelerated functions
589
        Omi, thi, phii, rotstep, latstep = get_segments_numba(self.thj, self.res)
1✔
590
        Gs, Ei = get_structure_numba(self.g0, self.ek, thi, self.thc, self.method_id, self.s, self.a, self.thj)
1✔
591
        G, SM, ghat = get_gamma_numba(Gs, Ei, 0.0, self.steps, self.n, self.k)
1✔
592
        Obsa = get_obsangle_numba(phii, thi, self.tho)
1✔
593

594
        # Calculate afterglow flux using Numba
595
        Flux, tobs = self.calc_afterglow_numba(G, SM, self.Dl, self.p, xp, Fx,
1✔
596
                                               self.epsB, self.epse, Gs, Omi, Ei,
597
                                               self.n, self.k, self.tho, thi, phii,
598
                                               self.thj, ghat, rotstep, latstep, Obsa, nu,
599
                                               self.steps, self.xiN)
600

601
        # Calculate final lightcurve
602
        LC = self.calc_lightcurve(self.t, tobs, Flux, nu.size, thi.size, phii.size, self.freq, nu0)
1✔
603
        return LC
1✔
604

605
    def calc_afterglow_numba(self, G, SM, Dl, p, xp, Fx, EB, Ee, Gs, Omi, Ei, n, k, tho, thi, phii, thj, ghat, rotstep,
1✔
606
                             latstep, Obsa, nu, steps, XiN):
607
        """Numba-accelerated afterglow calculation"""
608
        Flux = np.empty((nu.size, steps, thi.size * phii.size))
1✔
609
        tobs = np.empty((steps, thi.size * phii.size))
1✔
610

611
        kk = 0
1✔
612
        for i in range(thi.size):
1✔
613
            beta, Ne, OmG, R, B, gm, nump, Pp, KT = calc_afterglow_step1_numba(
1✔
614
                G[i, :], SM[i, :], p, xp, Fx, EB, Ee, n, k, thi[i], ghat[i, :],
615
                rotstep, latstep, XiN, self.is_expansion, self.a1, self.res)
616

617
            for j in range(phii.size):
1✔
618
                FBB, Fmax, nuc, num, tobs[:, kk] = calc_afterglow_step2_numba(
1✔
619
                    Dl, Omi[kk], rotstep, latstep, Obsa[kk], beta, Ne, OmG, R, B, gm, nump, Pp, KT, G[i, :],
620
                    self.is_expansion)
621

622
                if nu.size > 1:
1✔
NEW
623
                    for h in range(nu.size):
×
NEW
624
                        Flux[h, :, kk] = get_ag_numba(FBB, nuc, num, nu[h], Fmax, p)
×
625
                elif nu.size == 1:
1✔
626
                    Flux[0, :, kk] = get_ag_numba(FBB, nuc, num, nu[0], Fmax, p)
1✔
627
                kk += 1
1✔
628

629
        return Flux, tobs
1✔
630

631
    def calc_lightcurve(self, time, tobs, Flux, nu_size, thi_size, phii_size, freq, nu0):
1✔
632
        LC = np.zeros(freq.size)
1✔
633
        # forward shock lightcurve at each observation time
634
        for h in range(nu_size):
1✔
635
            FF = np.zeros(len(time[(freq == nu0[h])]))
1✔
636
            for i in range(thi_size * phii_size):
1✔
637
                FF += np.interp(time[(freq == nu0[h])] / (1 + self.z), tobs[:, i], Flux[h, :, i])
1✔
638
            LC[(freq == nu0[h])] = FF
1✔
639
        return LC * (1 + self.z)
1✔
640

641
class RedbackAfterglowsRefreshed(RedbackAfterglows):
1✔
642
    def __init__(self, k, n, epsb, epse, g0, g1, ek, et, s1, thc, thj, tho, p, exp, time, freq, redshift, Dl,
1✔
643
                 extra_structure_parameter_1, extra_structure_parameter_2,
644
                 method='TH', res=100, steps=int(500), xiN=1, a1=1):
645

646
        """
647
        A general class for refreshed afterglow models implemented directly in redback.
648
        This class is not meant to be used directly but instead via the interface for each specific model.
649
        The afterglows are based on the method shown in Lamb, Mandel & Resmi 2018 and other papers.
650
        Script was originally written by En-Tzu Lin <entzulin@gapp.nthu.edu.tw> and Gavin Lamb <g.p.lamb@ljmu.ac.uk>
651
        and modified and implemented into redback by Nikhil Sarin <nsarin.astro@gmail.com>.
652
        Includes wind-like mediums, expansion and multiple jet structures.
653
        Includes SSA and uses Numba for acceleration.
654

655
        :param k: 0 or 2 for constant or wind density
656
        :param n: ISM, ambient number density
657
        :param epsb: magnetic fraction
658
        :param epse: electron fraction
659
        :param g0: initial Lorentz factor
660
        :param g1: Lorentz factor of shell at start of energy injection
661
        :param ek: kinetic energy
662
        :param et: factor by which total kinetic energy is larger
663
        :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
664
            Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
665
        :param thc: core angle
666
        :param thj: jet outer angle. For tophat jets thc=thj
667
        :param tho: observers viewing angle
668
        :param p: electron power-law index
669
        :param exp: Boolean for whether to include sound speed expansion
670
        :param time: lightcurve time steps
671
        :param freq: lightcurve frequencies
672
        :param redshift: source redshift
673
        :param Dl: luminosity distance
674
        :param extra_structure_parameter_1: Extra structure specific parameter #1.
675
            Specifically, this parameter sets;
676
            The index on energy for power-law jets.
677
            The fractional energy contribution for the Double Gaussian (must be less than 1).
678
            The energy fraction  for the outer sheath for two-component jets (must be less than 1).
679
            Unused for tophat or Gaussian jets.
680
        :param extra_structure_parameter_2: Extra structure specific parameter #2.
681
            Specifically, this parameter sets;
682
            The index on lorentz factor for power-law jets.
683
            The lorentz factor for second Gaussian (must be less than 1).
684
            The lorentz factor  for the outer sheath for two-component jets (must be less than 1).
685
            Unused for tophat or Gaussian jets.
686
        :param method: Type of jet structure to use. Defaults to 'TH' for tophat jet.
687
            Other options are '2C', 'GJ', 'PL', 'PL2', 'DG'. Corresponding to two component, gaussian jet, powerlaw,
688
            alternative powerlaw and double Gaussian.
689
        :param res: resolution
690
        :param steps: number of steps used to resolve Gamma and dm
691
        :param XiN: fraction of electrons that get accelerated
692
        :param a1: the expansion description, a1 = 0 sound speed, a1 = 1 Granot & Piran 2012
693
        """
694

695
        super().__init__(k=k, n=n, epsb=epsb, epse=epse, g0=g0, ek=ek, thc=thc, thj=thj,
1✔
696
                         tho=tho, p=p, exp=exp, time=time, freq=freq, redshift=redshift,
697
                         Dl=Dl, extra_structure_parameter_1=extra_structure_parameter_1,
698
                         extra_structure_parameter_2=extra_structure_parameter_2, method=method,
699
                         res=res, steps=steps, xiN=xiN, a1=a1)
700
        self.G1 = float(g1)
1✔
701
        self.Et = float(et)
1✔
702
        self.s1 = float(s1)
1✔
703

704
    def get_lightcurve(self):
1✔
705
        if (self.k != 0) and (self.k != 2):
1✔
706
            raise ValueError("k must either be 0 or 2")
×
707
        if (self.p < 1.2) or (self.p > 3.4):
1✔
708
            raise ValueError("p is out of range, 1.2 < p < 3.4")
×
709
        # parameters p, x_p, and phi_p from Wijers & Galama 1999
710
        pxf = np.array([[1.0, 3.0, 0.41], [1.2, 1.4, 0.44], [1.4, 1.1, 0.48], [1.6, 0.86, 0.53], [1.8, 0.725, 0.56],
1✔
711
                        [2.0, 0.637, 0.59], [2.2, 0.579, 0.612], [2.5, 0.520, 0.630], [2.7, 0.487, 0.641],
712
                        [3.0, 0.451, 0.659],
713
                        [3.2, 0.434, 0.660], [3.4, 0.420, 0.675]])
714
        xp = np.interp(self.p, pxf[:, 0], pxf[:, 1])  # dimensionless spectral peak
1✔
715
        Fx = np.interp(self.p, pxf[:, 0], pxf[:, 2])  # dimensionless peak flux
1✔
716
        nu0 = np.unique(self.freq)  # unique frequencies in the sample, if loading a data array for frequencies
1✔
717
        nu = nu0 * (1 + self.z)  # rest frame frequency
1✔
718
        nu = np.array(nu)
1✔
719

720
        # Use Numba-accelerated functions from parent class
721
        Omi, thi, phii, rotstep, latstep = get_segments_numba(self.thj, self.res)
1✔
722
        Gs, Ei = get_structure_numba(self.g0, self.ek, thi, self.thc, self.method_id, self.s, self.a, self.thj)
1✔
723

724
        # Use Numba-accelerated refreshed gamma calculation
725
        G = np.empty((thi.size, self.steps))
1✔
726
        SM = np.empty((thi.size, self.steps))
1✔
727
        ghat = np.empty((thi.size, self.steps))
1✔
728

729
        for i in range(thi.size):
1✔
730
            E2 = self.Et * Ei[i]
1✔
731
            Gg, dM, gh = get_gamma_refreshed_numba(Gs[i], self.G1, Ei[i], E2 * Ei[i] / self.ek,
1✔
732
                                                   self.s1, 0.0, self.steps, self.n, self.k)
733
            G[i, :] = Gg
1✔
734
            SM[i, :] = dM
1✔
735
            ghat[i, :] = gh
1✔
736

737
        Obsa = get_obsangle_numba(phii, thi, self.tho)
1✔
738

739
        # Calculate afterglow flux using Numba from parent class
740
        Flux, tobs = self.calc_afterglow_numba(G, SM, self.Dl, self.p, xp, Fx,
1✔
741
                                               self.epsB, self.epse, Gs, Omi, Ei,
742
                                               self.n, self.k, self.tho, thi, phii,
743
                                               self.thj, ghat, rotstep, latstep, Obsa, nu,
744
                                               self.steps, self.xiN)
745

746
        # Calculate final lightcurve
747
        LC = self.calc_lightcurve(time=self.t, tobs=tobs, Flux=Flux,
1✔
748
                                  nu_size=nu.size, thi_size=thi.size, phii_size=phii.size,
749
                                  freq=self.freq, nu0=nu0)
750
        return LC
1✔
751

752
def _pnu_synchrotron(nu, B, gamma_m, gamma_c, Ne, p):
1✔
753
    """
754

755
    :param nu: frequency in Hz
756
    :param B: magnetic field in G
757
    :param gamma_m: minimum Lorentz factor of electrons
758
    :param gamma_c: electron Lorentz factor at which the cooling is important
759
    :param Ne: Number of emitting electrons
760
    :param p: power law index of the electron energy distribution
761
    :return: Pnu
762
    """
763
    qe = 4.80320425e-10  # electron charge in CGS
1✔
764
    Pnu_max = Ne * (electron_mass * speed_of_light ** 2 * sigma_T / (3.0 * qe)) * B
1✔
765
    nu_m = qe * B * gamma_m ** 2 / (2.0 * np.pi * electron_mass * speed_of_light)
1✔
766
    nu_c = qe * B * gamma_c ** 2 / (2.0 * np.pi * electron_mass * speed_of_light)
1✔
767

768
    Pnu = np.zeros_like(gamma_m)
1✔
769

770
    # slow cooling
771
    cooling_msk = (nu_m <= nu_c)
1✔
772

773
    msk = (nu < nu_m) * cooling_msk
1✔
774
    Pnu[msk] = Pnu_max[msk] * (nu[msk] / nu_m[msk]) ** (1.0 / 3.0)
1✔
775
    msk = (nu_m <= nu) * (nu <= nu_c) * cooling_msk
1✔
776
    Pnu[msk] = Pnu_max[msk] * (nu[msk] / nu_m[msk]) ** (-0.5 * (p - 1.0))
1✔
777
    msk = (nu_c < nu) * cooling_msk
1✔
778
    Pnu[msk] = Pnu_max[msk] * (nu_c[msk] / nu_m[msk]) ** (-0.5 * (p - 1.0)) * (nu[msk] / nu_c[msk]) ** (-0.5 * p)
1✔
779

780
    # fast cooling
781
    cooling_msk = (nu_c < nu_m)
1✔
782

783
    msk = (nu < nu_c) * cooling_msk
1✔
784
    Pnu[msk] = Pnu_max[msk] * (nu[msk] / nu_c[msk]) ** (1.0 / 3.0)
1✔
785
    msk = (nu_c <= nu) * (nu <= nu_m) * cooling_msk
1✔
786
    Pnu[msk] = Pnu_max[msk] * (nu[msk] / nu_c[msk]) ** (-0.5)
1✔
787
    msk = (nu_m < nu) * cooling_msk
1✔
788
    Pnu[msk] = Pnu_max[msk] * (nu_m[msk] / nu_c[msk]) ** (-0.5) * (nu[msk] / nu_m[msk]) ** (-0.5 * p)
1✔
789

790
    return Pnu
1✔
791

792
def _get_kn_dynamics(n0, Eej, Mej):
1✔
793
    """
794
    Calculates blast-wave hydrodynamics. Based on Pe'er (2012) with a numerical correction
795
    factor to ensure asymptotic convergence to Sedov-Taylor solution (see also Nava et al. 2013; Huang et al. 1999)
796

797
    :param n0: ISM density in cm^-3
798
    :param Eej: Ejecta energy in erg
799
    :param Mej: ejecta mass in g
800
    :return: Dynamical outputs - t, R, beta, Gamma, eden, tobs, beta_sh, Gamma_sh.
801
    """
802
    from scipy import integrate
1✔
803

804
    # calculate initial Lorentz factor & velocity from mass and energy
805
    Gamma0 = 1.0 + Eej / (Mej * speed_of_light ** 2)
1✔
806
    v0 = speed_of_light * (1.0 - Gamma0 ** (-2)) ** 0.5
1✔
807

808
    # characteristic, maximum, and starting times
809
    tdec = (3 * Eej / (4 * np.pi * proton_mass * speed_of_light ** 2 * n0 * Gamma0 * (Gamma0 - 1.0) * v0 ** 3)) ** (1.0 / 3.0)
1✔
810
    tmax = 1e3 * tdec
1✔
811
    t0 = 1e-4 * tdec
1✔
812

813
    # maximum numer of timesteps before abort
814
    N = 100000
1✔
815
    # maximal fractional difference in variables between timesteps
816
    s = 0.002
1✔
817

818
    # initiate variables
819
    t = np.zeros(N)
1✔
820
    R = np.zeros_like(t)
1✔
821
    Gamma = np.zeros_like(t)
1✔
822
    beta = np.zeros_like(t)
1✔
823
    m = np.zeros_like(t)
1✔
824
    Gamma_sh = np.zeros_like(t)
1✔
825
    beta_sh = np.zeros_like(t)
1✔
826

827
    t[0] = t0
1✔
828
    Gamma[0] = Gamma0
1✔
829
    beta[0] = v0 / speed_of_light
1✔
830
    R[0] = v0 * t[0]
1✔
831
    m[0] = (4.0 * np.pi / 3.0) * n0 * proton_mass * R[0] ** 3
1✔
832

833
    g = (4.0 + Gamma[0] ** (-1)) / 3.0
1✔
834
    Gamma_sh[0] = ((Gamma[0] + 1.0) * (g * (Gamma[0] - 1.0) + 1.0) ** 2 / (
1✔
835
                g * (2.0 - g) * (Gamma[0] - 1.0) + 2.0)) ** 0.5
836
    beta_sh[0] = (1.0 - Gamma_sh[0] ** (-2)) ** 0.5
1✔
837

838
    # integrate equations
839
    i = 0
1✔
840
    while t[i] < tmax and i < N - 1:
1✔
841
        # time derivative of variables (time is measured in lab frame)
842
        Rdot = beta_sh[i] * speed_of_light
1✔
843
        mdot = 4.0 * np.pi * n0 * proton_mass * R[i] ** 2 * Rdot
1✔
844
        Gammadot = -mdot * (g * (Gamma[i] ** 2 - 1.0) - (g - 1.0) * Gamma[i] * beta[i] ** 2) / (
1✔
845
                    Mej + m[i] * (2.0 * g * Gamma[i] - (g - 1.0) * (Gamma[i] ** (-2) + 1.0)))
846

847
        # calculate next timestep based on allowed tollerance "s" (or exit condition)
848
        dt = min(s * R[i] / Rdot, s * np.abs((Gamma[i] - 1.0) / Gammadot), s * m[i] / mdot, tmax - t[i])
1✔
849

850
        # update variables
851
        t[i + 1] = t[i] + dt
1✔
852
        R[i + 1] = R[i] + dt * Rdot
1✔
853
        Gamma[i + 1] = Gamma[i] + dt * Gammadot
1✔
854
        m[i + 1] = m[i] + dt * mdot
1✔
855
        beta[i + 1] = (1.0 - Gamma[i + 1] ** (-2)) ** 0.5
1✔
856
        # effectiv adiabatic index, smoothly interpolating between 4/3 in the ultra-relativistic limit and 5/3 in the Newtonian regime
857
        g = (4.0 + Gamma[i + 1] ** (-1)) / 3.0
1✔
858
        Gamma_sh[i + 1] = ((Gamma[i + 1] + 1.0) * (g * (Gamma[i + 1] - 1.0) + 1.0) ** 2 / (
1✔
859
                    g * (2.0 - g) * (Gamma[i + 1] - 1.0) + 2.0)) ** 0.5
860
        beta_sh[i + 1] = (1.0 - Gamma_sh[i + 1] ** (-2)) ** 0.5
1✔
861
        i += 1
1✔
862

863
    # trim arrays to end at last point of iteration
864
    i += 1
1✔
865
    t = t[:i]
1✔
866
    R = R[:i]
1✔
867
    beta = beta[:i]
1✔
868
    Gamma = Gamma[:i]
1✔
869
    beta_sh = beta_sh[:i]
1✔
870
    Gamma_sh = Gamma_sh[:i]
1✔
871

872
    g = (4.0 + Gamma ** (-1)) / 3.0
1✔
873
    # calculate post-shock thermal energy density (Blandford & McKee 1976).
874
    # Assumes cold upstream matter: enthalpy = mass density * c^2
875
    eden = (Gamma - 1.0) * ((g * Gamma + 1.0) / (g - 1.0)) * n0 * proton_mass * speed_of_light ** 2
1✔
876

877
    # convert from lab frame to observer time. Approximate expression acounting for radial+azimuthal time-of-flight effect
878
    # (eq. 26 from Nava et al. 2013; see also Waxman 1997)
879
    tobs = R / (Gamma ** 2 * (1.0 + beta) * speed_of_light) + np.insert(
1✔
880
        integrate.cumulative_trapezoid((1.0 - beta_sh) / beta_sh, x=R) / speed_of_light, 0, 0.0)
881

882
    return t, R, beta, Gamma, eden, tobs, beta_sh, Gamma_sh
1✔
883

884
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
885
def tophat_redback(time, redshift, thv, loge0, thc, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
886
    """
887
    A tophat model implemented directly in redback. Based on Lamb, Mandel & Resmi 2018 and other work.
888
    Look at the RedbackAfterglow class for more details/implementation.
889

890
    :param time: time in days
891
    :param redshift: source redshift
892
    :param thv: observer viewing angle in radians
893
    :param loge0: jet energy in \log_{10} ergs
894
    :param thc: jet opening angle in radians
895
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
896
    :param p: electron power law index
897
    :param logepse: partition fraction in electrons
898
    :param logepsb: partition fraction in magnetic field
899
    :param g0: initial lorentz factor
900
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
901
    :param kwargs: additional keyword arguments
902
    :param res: resolution - set dynamically based on afterglow properties by default,
903
            but can be set manually to a specific number.
904
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
905
    :param k: power law index of density profile. Defaults to 0 for constant density.
906
        Can be set to 2 for wind-like density profile.
907
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
908
    :param output_format: Whether to output flux density or AB mag
909
    :param frequency: frequency in Hz for the flux density calculation
910
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
911
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
912
        For a proper calculation of the magntitude use the sed variant models.
913
    """
914
    frequency = kwargs['frequency']
1✔
915
    if isinstance(frequency, float):
1✔
916
        frequency = np.ones(len(time)) * frequency
1✔
917
    k = kwargs.get('k', 0)
1✔
918
    a1 = kwargs.get('a1', 1)
1✔
919
    exp = kwargs.get('expansion', 1)
1✔
920
    epse = 10 ** logepse
1✔
921
    epsb = 10 ** logepsb
1✔
922
    nism = 10 ** logn0
1✔
923
    e0 = 10 ** loge0
1✔
924
    time = time * day_to_s
1✔
925
    cosmology = kwargs.get('cosmology', cosmo)
1✔
926
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
927
    method = 'TH'
1✔
928
    s, a = 0.01, 0.5
1✔
929

930
    # Set resolution dynamically
931
    sep = max(thv - thc, 0)
1✔
932
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
933
    default_res = max(10, order)
1✔
934
    res = kwargs.get('res', default_res)
1✔
935
    steps = kwargs.get('steps', 250)
1✔
936
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thc, tho=thv, p=p, exp=exp,
1✔
937
                                time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
938
                                 extra_structure_parameter_1=s, extra_structure_parameter_2=a,
939
                                 res=res, xiN=xiN, steps=steps, a1=a1)
940
    flux_density = ag_class.get_lightcurve()
1✔
941
    fmjy = flux_density / 1e-26
1✔
942
    if kwargs['output_format'] == 'flux_density':
1✔
943
        return fmjy
1✔
944
    elif kwargs['output_format'] == 'magnitude':
1✔
945
        return calc_ABmag_from_flux_density(fmjy).value
1✔
946

947
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
948
def gaussian_redback(time, redshift, thv, loge0, thc, thj, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
949
    """
950
    A Gaussian structure afterglow model implemented directly in redback. Based on Lamb, Mandel & Resmi 2018 and other work.
951
    Look at the RedbackAfterglow class for more details/implementation.
952

953
    :param time: time in days
954
    :param redshift: source redshift
955
    :param thv: observer viewing angle in radians
956
    :param loge0: jet energy in \log_{10} ergs
957
    :param thc: jet core size in radians
958
    :param thj: jet edge in radians (thc < thj < pi/2)
959
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
960
    :param p: electron power law index
961
    :param logepse: partition fraction in electrons
962
    :param logepsb: partition fraction in magnetic field
963
    :param g0: initial lorentz factor
964
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
965
    :param kwargs: additional keyword arguments
966
    :param res: resolution - set dynamically based on afterglow properties by default,
967
            but can be set manually to a specific number.
968
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
969
    :param k: power law index of density profile. Defaults to 0 for constant density.
970
        Can be set to 2 for wind-like density profile.
971
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
972
    :param output_format: Whether to output flux density or AB mag
973
    :param frequency: frequency in Hz for the flux density calculation
974
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
975
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
976
        For a proper calculation of the magntitude use the sed variant models.
977
    """
978
    frequency = kwargs['frequency']
1✔
979
    if isinstance(frequency, float):
1✔
980
        frequency = np.ones(len(time)) * frequency
1✔
981
    k = kwargs.get('k', 0)
1✔
982
    a1 = kwargs.get('a1', 1)
1✔
983
    exp = kwargs.get('expansion', 1)
1✔
984
    epse = 10 ** logepse
1✔
985
    epsb = 10 ** logepsb
1✔
986
    nism = 10 ** logn0
1✔
987
    e0 = 10 ** loge0
1✔
988
    time = time * day_to_s
1✔
989
    cosmology = kwargs.get('cosmology', cosmo)
1✔
990
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
991
    method = 'GJ'
1✔
992
    s, a = 0.01, 0.5
1✔
993

994
    # Set resolution dynamically
995
    sep = max(thv - thc, 0)
1✔
996
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
997
    default_res = max(10, order)
1✔
998
    res = kwargs.get('res', default_res)
1✔
999
    steps = kwargs.get('steps', 250)
1✔
1000
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1✔
1001
                                time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1002
                                 extra_structure_parameter_1=s, extra_structure_parameter_2=a,
1003
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1004
    flux_density = ag_class.get_lightcurve()
1✔
1005
    fmjy = flux_density / 1e-26
1✔
1006
    if kwargs['output_format'] == 'flux_density':
1✔
1007
        return fmjy
1✔
1008
    elif kwargs['output_format'] == 'magnitude':
1✔
1009
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1010

1011
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
1012
def twocomponent_redback(time, redshift, thv, loge0, thc, thj, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
1013
    """
1014
    A two component model implemented directly in redback. Tophat till thc and then second component till thj.
1015
    Based on Lamb, Mandel & Resmi 2018 and other work.
1016
    Look at the RedbackAfterglow class for more details/implementation.
1017

1018
    :param time: time in days
1019
    :param redshift: source redshift
1020
    :param thv: observer viewing angle in radians
1021
    :param loge0: jet energy in \log_{10} ergs
1022
    :param thc: jet core size in radians
1023
    :param thj: jet edge in radians (thc < thj < pi/2)
1024
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1025
    :param p: electron power law index
1026
    :param logepse: partition fraction in electrons
1027
    :param logepsb: partition fraction in magnetic field
1028
    :param g0: initial lorentz factor
1029
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1030
    :param kwargs: additional keyword arguments
1031
    :param res: resolution - set dynamically based on afterglow properties by default,
1032
            but can be set manually to a specific number.
1033
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1034
    :param k: power law index of density profile. Defaults to 0 for constant density.
1035
        Can be set to 2 for wind-like density profile.
1036
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1037
    :param ss: Fraction of energy in the outer sheath of the jet. Defaults to 0.01
1038
    :param aa: Lorentz factor outside the core. Defaults to 4.
1039
    :param output_format: Whether to output flux density or AB mag
1040
    :param frequency: frequency in Hz for the flux density calculation
1041
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1042
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1043
        For a proper calculation of the magntitude use the sed variant models.
1044
    """
1045
    frequency = kwargs['frequency']
1✔
1046
    if isinstance(frequency, float):
1✔
1047
        frequency = np.ones(len(time)) * frequency
1✔
1048
    k = kwargs.get('k', 0)
1✔
1049
    a1 = kwargs.get('a1', 1)
1✔
1050
    exp = kwargs.get('expansion', 1)
1✔
1051
    epse = 10 ** logepse
1✔
1052
    epsb = 10 ** logepsb
1✔
1053
    nism = 10 ** logn0
1✔
1054
    e0 = 10 ** loge0
1✔
1055
    time = time * day_to_s
1✔
1056
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1057
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1058
    method = '2C'
1✔
1059
    ss = kwargs.get('ss', 0.01)
1✔
1060
    aa = kwargs.get('aa', 4)
1✔
1061

1062
    # Set resolution dynamically
1063
    sep = max(thv - thc, 0)
1✔
1064
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1065
    default_res = max(10, order)
1✔
1066
    res = kwargs.get('res', default_res)
1✔
1067
    steps = kwargs.get('steps', 250)
1✔
1068
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1✔
1069
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1070
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1071
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1072
    flux_density = ag_class.get_lightcurve()
1✔
1073
    fmjy = flux_density / 1e-26
1✔
1074
    if kwargs['output_format'] == 'flux_density':
1✔
1075
        return fmjy
1✔
1076
    elif kwargs['output_format'] == 'magnitude':
1✔
1077
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1078

1079
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
1080
def powerlaw_redback(time, redshift, thv, loge0, thc, thj, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
1081
    """
1082
    A Classic powerlaw structured jet implemented directly in redback.
1083
    Tophat with powerlaw energy proportional to theta^ss and lorentz factor proportional to theta^aa outside core.
1084
    Based on Lamb, Mandel & Resmi 2018 and other work.
1085
    Look at the RedbackAfterglow class for more details/implementation.
1086

1087
    :param time: time in days
1088
    :param redshift: source redshift
1089
    :param thv: observer viewing angle in radians
1090
    :param loge0: jet energy in \log_{10} ergs
1091
    :param thc: jet core size in radians
1092
    :param thj: jet edge in radians (thc < thj < pi/2)
1093
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1094
    :param p: electron power law index
1095
    :param logepse: partition fraction in electrons
1096
    :param logepsb: partition fraction in magnetic field
1097
    :param g0: initial lorentz factor
1098
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1099
    :param kwargs: additional keyword arguments
1100
    :param res: resolution - set dynamically based on afterglow properties by default,
1101
            but can be set manually to a specific number.
1102
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1103
    :param k: power law index of density profile. Defaults to 0 for constant density.
1104
        Can be set to 2 for wind-like density profile.
1105
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1106
    :param ss: Index of energy outside core. Defaults to -3
1107
    :param aa: Index of Lorentz factor outside the core. Defaults to -3
1108
    :param output_format: Whether to output flux density or AB mag
1109
    :param frequency: frequency in Hz for the flux density calculation
1110
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1111
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1112
        For a proper calculation of the magntitude use the sed variant models.
1113
    """
1114
    frequency = kwargs['frequency']
1✔
1115
    if isinstance(frequency, float):
1✔
1116
        frequency = np.ones(len(time)) * frequency
1✔
1117
    k = kwargs.get('k', 0)
1✔
1118
    a1 = kwargs.get('a1', 1)
1✔
1119
    exp = kwargs.get('expansion', 1)
1✔
1120
    epse = 10 ** logepse
1✔
1121
    epsb = 10 ** logepsb
1✔
1122
    nism = 10 ** logn0
1✔
1123
    e0 = 10 ** loge0
1✔
1124
    time = time * day_to_s
1✔
1125
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1126
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1127
    method = 'PL'
1✔
1128
    ss = kwargs.get('ss', 3)
1✔
1129
    aa = kwargs.get('aa', -3)
1✔
1130

1131
    # Set resolution dynamically
1132
    sep = max(thv - thc, 0)
1✔
1133
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1134
    default_res = max(10, order)
1✔
1135
    res = kwargs.get('res', default_res)
1✔
1136
    steps = kwargs.get('steps', 250)
1✔
1137
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1✔
1138
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1139
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1140
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1141
    flux_density = ag_class.get_lightcurve()
1✔
1142
    fmjy = flux_density / 1e-26
1✔
1143
    if kwargs['output_format'] == 'flux_density':
1✔
1144
        return fmjy
1✔
1145
    elif kwargs['output_format'] == 'magnitude':
1✔
1146
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1147

1148
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
1149
def alternativepowerlaw_redback(time, redshift, thv, loge0, thc, thj, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
1150
    """
1151
    An alternative powerlaw structured jet implemented directly in redback. Profile follows (theta/thc^2)^0.5^(-s or -a).
1152
    Based on Lamb, Mandel & Resmi 2018 and other work.
1153
    Look at the RedbackAfterglow class for more details/implementation.
1154

1155
    :param time: time in days
1156
    :param redshift: source redshift
1157
    :param thv: observer viewing angle in radians
1158
    :param loge0: jet energy in \log_{10} ergs
1159
    :param thc: jet core size in radians
1160
    :param thj: jet edge in radians (thc < thj < pi/2)
1161
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1162
    :param p: electron power law index
1163
    :param logepse: partition fraction in electrons
1164
    :param logepsb: partition fraction in magnetic field
1165
    :param g0: initial lorentz factor
1166
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1167
    :param kwargs: additional keyword arguments
1168
    :param res: resolution - set dynamically based on afterglow properties by default,
1169
            but can be set manually to a specific number.
1170
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1171
    :param k: power law index of density profile. Defaults to 0 for constant density.
1172
        Can be set to 2 for wind-like density profile.
1173
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1174
    :param ss: Index of energy outside core. Defaults to 3
1175
    :param aa: Index of Lorentz factor outside the core. Defaults to 3
1176
    :param output_format: Whether to output flux density or AB mag
1177
    :param frequency: frequency in Hz for the flux density calculation
1178
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1179
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1180
        For a proper calculation of the magntitude use the sed variant models.
1181
    """
1182
    frequency = kwargs['frequency']
1✔
1183
    if isinstance(frequency, float):
1✔
1184
        frequency = np.ones(len(time)) * frequency
1✔
1185
    k = kwargs.get('k', 0)
1✔
1186
    a1 = kwargs.get('a1', 1)
1✔
1187
    exp = kwargs.get('expansion', 1)
1✔
1188
    epse = 10 ** logepse
1✔
1189
    epsb = 10 ** logepsb
1✔
1190
    nism = 10 ** logn0
1✔
1191
    e0 = 10 ** loge0
1✔
1192
    time = time * day_to_s
1✔
1193
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1194
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1195
    method = 'PL2'
1✔
1196
    ss = kwargs.get('ss', 3)
1✔
1197
    aa = kwargs.get('aa', 3)
1✔
1198

1199
    # Set resolution dynamically
1200
    sep = max(thv - thc, 0)
1✔
1201
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1202
    default_res = max(10, order)
1✔
1203
    res = kwargs.get('res', default_res)
1✔
1204
    steps = kwargs.get('steps', 250)
1✔
1205
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1✔
1206
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1207
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1208
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1209
    flux_density = ag_class.get_lightcurve()
1✔
1210
    fmjy = flux_density / 1e-26
1✔
1211
    if kwargs['output_format'] == 'flux_density':
1✔
1212
        return fmjy
1✔
1213
    elif kwargs['output_format'] == 'magnitude':
1✔
1214
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1215

1216
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2018MNRAS.481.2581L/abstract')
1✔
1217
def doublegaussian_redback(time, redshift, thv, loge0, thc, thj, logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1✔
1218
    """
1219
    Double Gaussian structured jet implemented directly in redback.
1220
    Based on Lamb, Mandel & Resmi 2018 and other work.
1221
    Look at the RedbackAfterglow class for more details/implementation.
1222

1223
    :param time: time in days
1224
    :param redshift: source redshift
1225
    :param thv: observer viewing angle in radians
1226
    :param loge0: jet energy in \log_{10} ergs
1227
    :param thc: jet core size in radians
1228
    :param thj: jet edge in radians (thc < thj < pi/2)
1229
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1230
    :param p: electron power law index
1231
    :param logepse: partition fraction in electrons
1232
    :param logepsb: partition fraction in magnetic field
1233
    :param g0: initial lorentz factor
1234
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1235
    :param kwargs: additional keyword arguments
1236
    :param res: resolution - set dynamically based on afterglow properties by default,
1237
            but can be set manually to a specific number.
1238
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1239
    :param k: power law index of density profile. Defaults to 0 for constant density.
1240
        Can be set to 2 for wind-like density profile.
1241
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1242
    :param ss: Fractional contribution of energy to second Gaussian. Defaults to 0.1, must be less than 1.
1243
    :param aa: Lorentz factor for second Gaussian, must be less than 1.
1244
    :param output_format: Whether to output flux density or AB mag
1245
    :param frequency: frequency in Hz for the flux density calculation
1246
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1247
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1248
        For a proper calculation of the magntitude use the sed variant models.
1249
    """
1250
    frequency = kwargs['frequency']
1✔
1251
    if isinstance(frequency, float):
1✔
1252
        frequency = np.ones(len(time)) * frequency
1✔
1253
    k = kwargs.get('k', 0)
1✔
1254
    a1 = kwargs.get('a1', 1)
1✔
1255
    exp = kwargs.get('expansion', 1)
1✔
1256
    epse = 10 ** logepse
1✔
1257
    epsb = 10 ** logepsb
1✔
1258
    nism = 10 ** logn0
1✔
1259
    e0 = 10 ** loge0
1✔
1260
    time = time * day_to_s
1✔
1261
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1262
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1263
    method = 'DG'
1✔
1264
    ss = kwargs.get('ss', 0.1)
1✔
1265
    aa = kwargs.get('aa', 0.5)
1✔
1266

1267
    # Set resolution dynamically
1268
    sep = max(thv - thc, 0)
1✔
1269
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1270
    default_res = max(10, order)
1✔
1271
    res = kwargs.get('res', default_res)
1✔
1272
    steps = kwargs.get('steps', 250)
1✔
1273
    ag_class = RedbackAfterglows(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1✔
1274
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1275
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1276
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1277
    flux_density = ag_class.get_lightcurve()
1✔
1278
    fmjy = flux_density / 1e-26
1✔
1279
    if kwargs['output_format'] == 'flux_density':
1✔
1280
        return fmjy
1✔
1281
    elif kwargs['output_format'] == 'magnitude':
1✔
1282
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1283

1284
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1285
def tophat_redback_refreshed(time, redshift, thv, loge0, thc, g1, et, s1,
1✔
1286
                             logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1287
    """
1288
    A Refreshed tophat model implemented directly in redback. Based on Lamb et al. 2019
1289
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1290

1291
    :param time: time in days
1292
    :param redshift: source redshift
1293
    :param thv: observer viewing angle in radians
1294
    :param loge0: jet energy in \log_{10} ergs
1295
    :param thc: jet opening angle in radians
1296
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1297
    :param et: factor by which total kinetic energy is larger
1298
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1299
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1300
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1301
    :param p: electron power law index
1302
    :param logepse: partition fraction in electrons
1303
    :param logepsb: partition fraction in magnetic field
1304
    :param g0: initial lorentz factor
1305
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1306
    :param kwargs: additional keyword arguments
1307
    :param res: resolution - set dynamically based on afterglow properties by default,
1308
            but can be set manually to a specific number.
1309
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1310
    :param k: power law index of density profile. Defaults to 0 for constant density.
1311
        Can be set to 2 for wind-like density profile.
1312
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1313
    :param output_format: Whether to output flux density or AB mag
1314
    :param frequency: frequency in Hz for the flux density calculation
1315
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1316
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1317
        For a proper calculation of the magntitude use the sed variant models.
1318
    """
1319
    frequency = kwargs['frequency']
1✔
1320
    if isinstance(frequency, float):
1✔
1321
        frequency = np.ones(len(time)) * frequency
1✔
1322
    k = kwargs.get('k', 0)
1✔
1323
    a1 = kwargs.get('a1', 1)
1✔
1324
    exp = kwargs.get('expansion', 1)
1✔
1325
    epse = 10 ** logepse
1✔
1326
    epsb = 10 ** logepsb
1✔
1327
    nism = 10 ** logn0
1✔
1328
    e0 = 10 ** loge0
1✔
1329
    time = time * day_to_s
1✔
1330
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1331
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1332
    method = 'TH'
1✔
1333
    s, a = 0.01, 0.5
1✔
1334

1335
    # Set resolution dynamically
1336
    sep = max(thv - thc, 0)
1✔
1337
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1338
    default_res = max(10, order)
1✔
1339
    res = kwargs.get('res', default_res)
1✔
1340
    steps = kwargs.get('steps', 250)
1✔
1341
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, g1=g1, et=et, s1=s1,
1✔
1342
                                          thc=thc, thj=thc, tho=thv, p=p, exp=exp,time=time, freq=frequency,
1343
                                          redshift=redshift, Dl=dl, method=method,
1344
                                 extra_structure_parameter_1=s, extra_structure_parameter_2=a,
1345
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1346
    flux_density = ag_class.get_lightcurve()
1✔
1347
    fmjy = flux_density / 1e-26
1✔
1348
    if kwargs['output_format'] == 'flux_density':
1✔
1349
        return fmjy
1✔
1350
    elif kwargs['output_format'] == 'magnitude':
1✔
1351
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1352

1353
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1354
def gaussian_redback_refreshed(time, redshift, thv, loge0, thc, thj, g1, et, s1,
1✔
1355
                               logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1356
    """
1357
    A Refreshed Gaussian structured jet model implemented directly in redback. Based on Lamb et al. 2019
1358
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1359

1360
    :param time: time in days
1361
    :param redshift: source redshift
1362
    :param thv: observer viewing angle in radians
1363
    :param loge0: jet energy in \log_{10} ergs
1364
    :param thc: jet core size in radians
1365
    :param thj: jet edge in radians (thc < thj < pi/2)
1366
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1367
    :param et: factor by which total kinetic energy is larger
1368
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1369
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1370
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1371
    :param p: electron power law index
1372
    :param logepse: partition fraction in electrons
1373
    :param logepsb: partition fraction in magnetic field
1374
    :param g0: initial lorentz factor
1375
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1376
    :param kwargs: additional keyword arguments
1377
    :param res: resolution - set dynamically based on afterglow properties by default,
1378
            but can be set manually to a specific number.
1379
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1380
    :param k: power law index of density profile. Defaults to 0 for constant density.
1381
        Can be set to 2 for wind-like density profile.
1382
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1383
    :param output_format: Whether to output flux density or AB mag
1384
    :param frequency: frequency in Hz for the flux density calculation
1385
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1386
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1387
        For a proper calculation of the magntitude use the sed variant models.
1388
    """
1389
    frequency = kwargs['frequency']
1✔
1390
    if isinstance(frequency, float):
1✔
1391
        frequency = np.ones(len(time)) * frequency
1✔
1392
    k = kwargs.get('k', 0)
1✔
1393
    a1 = kwargs.get('a1', 1)
1✔
1394
    exp = kwargs.get('expansion', 1)
1✔
1395
    epse = 10 ** logepse
1✔
1396
    epsb = 10 ** logepsb
1✔
1397
    nism = 10 ** logn0
1✔
1398
    e0 = 10 ** loge0
1✔
1399
    time = time * day_to_s
1✔
1400
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1401
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1402
    method = 'GJ'
1✔
1403
    s, a = 0.01, 0.5
1✔
1404

1405
    # Set resolution dynamically
1406
    sep = max(thv - thc, 0)
1✔
1407
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1408
    default_res = max(10, order)
1✔
1409
    res = kwargs.get('res', default_res)
1✔
1410
    steps = kwargs.get('steps', 250)
1✔
1411
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj,
1✔
1412
                                 tho=thv, p=p, exp=exp, g1=g1, et=et, s1=s1,
1413
                                time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1414
                                 extra_structure_parameter_1=s, extra_structure_parameter_2=a,
1415
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1416
    flux_density = ag_class.get_lightcurve()
1✔
1417
    fmjy = flux_density / 1e-26
1✔
1418
    if kwargs['output_format'] == 'flux_density':
1✔
1419
        return fmjy
1✔
1420
    elif kwargs['output_format'] == 'magnitude':
1✔
1421
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1422

1423
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1424
def twocomponent_redback_refreshed(time, redshift, thv, loge0, thc, thj, g1, et, s1,
1✔
1425
                                   logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1426
    """
1427
    A refreshed two component model implemented directly in redback. Tophat till thc and then second component till thj.
1428
    Based on Lamb et al. 2019 and other work.
1429
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1430

1431
    :param time: time in days
1432
    :param redshift: source redshift
1433
    :param thv: observer viewing angle in radians
1434
    :param loge0: jet energy in \log_{10} ergs
1435
    :param thc: jet core size in radians
1436
    :param thj: jet edge in radians (thc < thj < pi/2)
1437
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1438
    :param et: factor by which total kinetic energy is larger
1439
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1440
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1441
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1442
    :param p: electron power law index
1443
    :param logepse: partition fraction in electrons
1444
    :param logepsb: partition fraction in magnetic field
1445
    :param g0: initial lorentz factor
1446
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1447
    :param kwargs: additional keyword arguments
1448
    :param res: resolution - set dynamically based on afterglow properties by default,
1449
            but can be set manually to a specific number.
1450
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1451
    :param k: power law index of density profile. Defaults to 0 for constant density.
1452
        Can be set to 2 for wind-like density profile.
1453
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1454
    :param ss: Fraction of energy in the outer sheath of the jet. Defaults to 0.01
1455
    :param aa: Lorentz factor outside the core. Defaults to 4.
1456
    :param output_format: Whether to output flux density or AB mag
1457
    :param frequency: frequency in Hz for the flux density calculation
1458
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1459
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1460
        For a proper calculation of the magntitude use the sed variant models.
1461
    """
1462
    frequency = kwargs['frequency']
1✔
1463
    if isinstance(frequency, float):
1✔
1464
        frequency = np.ones(len(time)) * frequency
1✔
1465
    k = kwargs.get('k', 0)
1✔
1466
    a1 = kwargs.get('a1', 1)
1✔
1467
    exp = kwargs.get('expansion', 1)
1✔
1468
    epse = 10 ** logepse
1✔
1469
    epsb = 10 ** logepsb
1✔
1470
    nism = 10 ** logn0
1✔
1471
    e0 = 10 ** loge0
1✔
1472
    time = time * day_to_s
1✔
1473
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1474
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1475
    method = '2C'
1✔
1476
    ss = kwargs.get('ss', 0.01)
1✔
1477
    aa = kwargs.get('aa', 4.)
1✔
1478

1479
    # Set resolution dynamically
1480
    sep = max(thv - thc, 0)
1✔
1481
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1482
    default_res = max(10, order)
1✔
1483
    res = kwargs.get('res', default_res)
1✔
1484
    steps = kwargs.get('steps', 250)
1✔
1485
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0, thc=thc, thj=thj,
1✔
1486
                                          tho=thv, p=p, exp=exp, g1=g1, et=et, s1=s1, time=time, freq=frequency,
1487
                                          redshift=redshift, Dl=dl, method=method, extra_structure_parameter_1=ss,
1488
                                          extra_structure_parameter_2=aa, res=res, xiN=xiN, steps=steps, a1=a1)
1489
    flux_density = ag_class.get_lightcurve()
1✔
1490
    fmjy = flux_density / 1e-26
1✔
1491
    if kwargs['output_format'] == 'flux_density':
1✔
1492
        return fmjy
1✔
1493
    elif kwargs['output_format'] == 'magnitude':
1✔
1494
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1495

1496
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1497
def powerlaw_redback_refreshed(time, redshift, thv, loge0, thc, thj, g1, et, s1,
1✔
1498
                               logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1499
    """
1500
    A Classic refreshed powerlaw structured jet implemented directly in redback.
1501
    Tophat with powerlaw energy proportional to theta^ss and lorentz factor proportional to theta^aa outside core.
1502
    Based on Lamb et al. 2019 and other work.
1503
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1504

1505
    :param time: time in days
1506
    :param redshift: source redshift
1507
    :param thv: observer viewing angle in radians
1508
    :param loge0: jet energy in \log_{10} ergs
1509
    :param thc: jet core size in radians
1510
    :param thj: jet edge in radians (thc < thj < pi/2)
1511
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1512
    :param et: factor by which total kinetic energy is larger
1513
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1514
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1515
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1516
    :param p: electron power law index
1517
    :param logepse: partition fraction in electrons
1518
    :param logepsb: partition fraction in magnetic field
1519
    :param g0: initial lorentz factor
1520
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1521
    :param kwargs: additional keyword arguments
1522
    :param res: resolution - set dynamically based on afterglow properties by default,
1523
            but can be set manually to a specific number.
1524
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1525
    :param k: power law index of density profile. Defaults to 0 for constant density.
1526
        Can be set to 2 for wind-like density profile.
1527
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1528
    :param ss: Index of energy outside core. Defaults to -3
1529
    :param aa: Index of Lorentz factor outside the core. Defaults to -3
1530
    :param output_format: Whether to output flux density or AB mag
1531
    :param frequency: frequency in Hz for the flux density calculation
1532
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1533
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1534
        For a proper calculation of the magntitude use the sed variant models.
1535
    """
1536
    frequency = kwargs['frequency']
1✔
1537
    if isinstance(frequency, float):
1✔
1538
        frequency = np.ones(len(time)) * frequency
1✔
1539
    k = kwargs.get('k', 0)
1✔
1540
    a1 = kwargs.get('a1', 1)
1✔
1541
    exp = kwargs.get('expansion', 1)
1✔
1542
    epse = 10 ** logepse
1✔
1543
    epsb = 10 ** logepsb
1✔
1544
    nism = 10 ** logn0
1✔
1545
    e0 = 10 ** loge0
1✔
1546
    time = time * day_to_s
1✔
1547
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1548
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1549
    method = 'PL'
1✔
1550
    ss = kwargs.get('ss', 3)
1✔
1551
    aa = kwargs.get('aa', -3)
1✔
1552

1553
    # Set resolution dynamically
1554
    sep = max(thv - thc, 0)
1✔
1555
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1556
    default_res = max(10, order)
1✔
1557
    res = kwargs.get('res', default_res)
1✔
1558
    steps = kwargs.get('steps', 250)
1✔
1559
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, g1=g1, et=et, s1=s1,
1✔
1560
                                 ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1561
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1562
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1563
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1564
    flux_density = ag_class.get_lightcurve()
1✔
1565
    fmjy = flux_density / 1e-26
1✔
1566
    if kwargs['output_format'] == 'flux_density':
1✔
1567
        return fmjy
1✔
1568
    elif kwargs['output_format'] == 'magnitude':
1✔
1569
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1570

1571
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1572
def alternativepowerlaw_redback_refreshed(time, redshift, thv, loge0, thc, thj, g1, et, s1,
1✔
1573
                                          logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1574
    """
1575
    An alternative refreshed powerlaw structured jet implemented directly in redback. Profile follows (theta/thc^2)^0.5^(-s or -a).
1576
    Based on Lamb et al. 2019.
1577
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1578

1579
    :param time: time in days
1580
    :param redshift: source redshift
1581
    :param thv: observer viewing angle in radians
1582
    :param loge0: jet energy in \log_{10} ergs
1583
    :param thc: jet core size in radians
1584
    :param thj: jet edge in radians (thc < thj < pi/2)
1585
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1586
    :param et: factor by which total kinetic energy is larger
1587
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1588
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1589
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1590
    :param p: electron power law index
1591
    :param logepse: partition fraction in electrons
1592
    :param logepsb: partition fraction in magnetic field
1593
    :param g0: initial lorentz factor
1594
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1595
    :param kwargs: additional keyword arguments
1596
    :param res: resolution - set dynamically based on afterglow properties by default,
1597
            but can be set manually to a specific number.
1598
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1599
    :param k: power law index of density profile. Defaults to 0 for constant density.
1600
        Can be set to 2 for wind-like density profile.
1601
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1602
    :param ss: Index of energy outside core. Defaults to 3
1603
    :param aa: Index of Lorentz factor outside the core. Defaults to 3
1604
    :param output_format: Whether to output flux density or AB mag
1605
    :param frequency: frequency in Hz for the flux density calculation
1606
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1607
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1608
        For a proper calculation of the magntitude use the sed variant models.
1609
    """
1610
    frequency = kwargs['frequency']
1✔
1611
    if isinstance(frequency, float):
1✔
1612
        frequency = np.ones(len(time)) * frequency
1✔
1613
    k = kwargs.get('k', 0)
1✔
1614
    a1 = kwargs.get('a1', 1)
1✔
1615
    exp = kwargs.get('expansion', 1)
1✔
1616
    epse = 10 ** logepse
1✔
1617
    epsb = 10 ** logepsb
1✔
1618
    nism = 10 ** logn0
1✔
1619
    e0 = 10 ** loge0
1✔
1620
    time = time * day_to_s
1✔
1621
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1622
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1623
    method = 'PL2'
1✔
1624
    ss = kwargs.get('ss', 3)
1✔
1625
    aa = kwargs.get('aa', 3)
1✔
1626

1627
    # Set resolution dynamically
1628
    sep = max(thv - thc, 0)
1✔
1629
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1630
    default_res = max(10, order)
1✔
1631
    res = kwargs.get('res', default_res)
1✔
1632
    steps = kwargs.get('steps', 250)
1✔
1633
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, g1=g1, et=et, s1=s1,
1✔
1634
                                          ek=e0, thc=thc, thj=thj, tho=thv, p=p, exp=exp,
1635
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1636
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1637
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1638
    flux_density = ag_class.get_lightcurve()
1✔
1639
    fmjy = flux_density / 1e-26
1✔
1640
    if kwargs['output_format'] == 'flux_density':
1✔
1641
        return fmjy
1✔
1642
    elif kwargs['output_format'] == 'magnitude':
1✔
1643
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1644

1645
@citation_wrapper('redback, https://ui.adsabs.harvard.edu/abs/2019ApJ...883...48L/abstract')
1✔
1646
def doublegaussian_redback_refreshed(time, redshift, thv, loge0, thc, thj, g1, et, s1,
1✔
1647
                                     logn0, p, logepse, logepsb, g0, xiN, **kwargs):
1648
    """
1649
    Double Gaussian structured, refreshed jet implemented directly in redback.
1650
    Based on Lamb et al. 2019 and other work.
1651
    Look at the RedbackAfterglowRefreshed class for more details/implementation.
1652

1653
    :param time: time in days
1654
    :param redshift: source redshift
1655
    :param thv: observer viewing angle in radians
1656
    :param loge0: jet energy in \log_{10} ergs
1657
    :param thc: jet core size in radians
1658
    :param thj: jet edge in radians (thc < thj < pi/2)
1659
    :param g1: Lorentz factor of shell at start of energy injection. 2 <= g1 < g0
1660
    :param et: factor by which total kinetic energy is larger
1661
    :param s1: index for energy injection; typically between 0--10, some higher values, ~<30, are supported for some structures.
1662
        Values of ~10 are consistent with a discrete shock interaction, see Lamb, Levan & Tanvir 2020
1663
    :param logn0: ism number density in \log_{10} cm^-3 or \log_{10} A* for wind-like density profile
1664
    :param p: electron power law index
1665
    :param logepse: partition fraction in electrons
1666
    :param logepsb: partition fraction in magnetic field
1667
    :param g0: initial lorentz factor
1668
    :param xiN: fraction of electrons that get accelerated. Defaults to 1.
1669
    :param kwargs: additional keyword arguments
1670
    :param res: resolution - set dynamically based on afterglow properties by default,
1671
            but can be set manually to a specific number.
1672
    :param steps: number of steps used to resolve Gamma and dm. Defaults to 250 but can be set manually.
1673
    :param k: power law index of density profile. Defaults to 0 for constant density.
1674
        Can be set to 2 for wind-like density profile.
1675
    :param expansion: 0 or 1 to dictate whether to include expansion effects. Defaults to 1
1676
    :param ss: Fractional contribution of energy to second Gaussian. Defaults to 0.1, must be less than 1.
1677
    :param aa: Lorentz factor for second Gaussian, must be less than 1.
1678
    :param output_format: Whether to output flux density or AB mag
1679
    :param frequency: frequency in Hz for the flux density calculation
1680
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1681
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1682
        For a proper calculation of the magntitude use the sed variant models.
1683
    """
1684
    frequency = kwargs['frequency']
1✔
1685
    if isinstance(frequency, float):
1✔
1686
        frequency = np.ones(len(time)) * frequency
1✔
1687
    k = kwargs.get('k', 0)
1✔
1688
    a1 = kwargs.get('a1', 1)
1✔
1689
    exp = kwargs.get('expansion', 1)
1✔
1690
    epse = 10 ** logepse
1✔
1691
    epsb = 10 ** logepsb
1✔
1692
    nism = 10 ** logn0
1✔
1693
    e0 = 10 ** loge0
1✔
1694
    time = time * day_to_s
1✔
1695
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1696
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1697
    method = 'DG'
1✔
1698
    ss = kwargs.get('ss', 0.1)
1✔
1699
    aa = kwargs.get('aa', 0.5)
1✔
1700

1701
    # Set resolution dynamically
1702
    sep = max(thv - thc, 0)
1✔
1703
    order = min(int((2 - 10 * sep) * thc * g0), 100)
1✔
1704
    default_res = max(10, order)
1✔
1705
    res = kwargs.get('res', default_res)
1✔
1706
    steps = kwargs.get('steps', 250)
1✔
1707
    ag_class = RedbackAfterglowsRefreshed(k=k, n=nism, epse=epse, epsb=epsb, g0=g0, ek=e0,
1✔
1708
                                 thc=thc, thj=thj, tho=thv, p=p, exp=exp, g1=g1, et=et, s1=s1,
1709
                                 time=time, freq=frequency, redshift=redshift, Dl=dl, method=method,
1710
                                 extra_structure_parameter_1=ss, extra_structure_parameter_2=aa,
1711
                                 res=res, xiN=xiN, steps=steps, a1=a1)
1712
    flux_density = ag_class.get_lightcurve()
1✔
1713
    fmjy = flux_density / 1e-26
1✔
1714
    if kwargs['output_format'] == 'flux_density':
1✔
1715
        return fmjy
1✔
1716
    elif kwargs['output_format'] == 'magnitude':
1✔
1717
        return calc_ABmag_from_flux_density(fmjy).value
1✔
1718

1719
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1720
def cocoon(time, redshift, umax, umin, loge0, k, mej, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1721
    """
1722
    A cocoon afterglow model from afterglowpy
1723

1724
    :param time: time in days in observer frame
1725
    :param redshift: source redshift
1726
    :param umax: initial outflow 4 velocity maximum
1727
    :param umin: minimum outflow 4 velocity
1728
    :param loge0: log10 fidicial energy in velocity distribution E(>u) = E0u^-k in erg
1729
    :param k: power law index of energy velocity distribution
1730
    :param mej: mass of material at umax in solar masses
1731
    :param logn0: log10 number density of ISM in cm^-3
1732
    :param p: electron distribution power law index. Must be greater than 2.
1733
    :param logepse: log10 fraction of thermal energy in electrons
1734
    :param logepsb: log10 fraction of thermal energy in magnetic field
1735
    :param ksin: fraction of electrons that get accelerated
1736
    :param g0: initial lorentz factor
1737
    :param kwargs: Additional keyword arguments
1738
    :param spread: whether jet can spread, defaults to False
1739
    :param latres: latitudinal resolution for structured jets, defaults to 2
1740
    :param tres: time resolution of shock evolution, defaults to 100
1741
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
1742
        Change to 1 for including inverse compton emission.
1743
    :param l0, ts, q: energy injection parameters, defaults to 0
1744
    :param output_format: Whether to output flux density or AB mag
1745
    :param frequency: frequency in Hz for the flux density calculation
1746
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1747
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1748
        For a proper calculation of the magntitude use the sed variant models.
1749
    """
1750
    time = time * day_to_s
1✔
1751
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1752
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1753
    spread = kwargs.get('spread', False)
1✔
1754
    latres = kwargs.get('latres', 2)
1✔
1755
    tres = kwargs.get('tres', 100)
1✔
1756
    spectype = kwargs.get('spectype', 0)
1✔
1757
    l0 = kwargs.get('L0', 0)
1✔
1758
    q = kwargs.get('q', 0)
1✔
1759
    ts = kwargs.get('ts', 0)
1✔
1760
    jettype = jettype_dict['cocoon']
1✔
1761
    frequency = kwargs['frequency']
1✔
1762
    e0 = 10 ** loge0
1✔
1763
    n0 = 10 ** logn0
1✔
1764
    epse = 10 ** logepse
1✔
1765
    epsb = 10 ** logepsb
1✔
1766
    Z = {'jetType': jettype, 'specType': spectype, 'uMax': umax, 'Er': e0,
1✔
1767
         'uMin': umin, 'k': k, 'MFast_solar': mej, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
1768
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
1769
         'spread': spread, 'latRes': latres, 'tRes': tres}
1770
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
1771
    if kwargs['output_format'] == 'flux_density':
1✔
1772
        return flux_density
1✔
1773
    elif kwargs['output_format'] == 'magnitude':
1✔
1774
        return calc_ABmag_from_flux_density(flux_density).value
1✔
1775

1776
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1777
def kilonova_afterglow(time, redshift, umax, umin, loge0, k, mej, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1778
    """
1779
    A kilonova afterglow model from afterglowpy, similar to cocoon but with constraints.
1780

1781
    :param time: time in days in observer frame
1782
    :param redshift: source redshift
1783
    :param umax: initial outflow 4 velocity maximum
1784
    :param umin: minimum outflow 4 velocity
1785
    :param loge0: log10 fidicial energy in velocity distribution E(>u) = E0u^-k in erg
1786
    :param k: power law index of energy velocity distribution
1787
    :param mej: mass of material at umax in solar masses
1788
    :param logn0: log10 number density of ISM in cm^-3
1789
    :param p: electron distribution power law index. Must be greater than 2.
1790
    :param logepse: log10 fraction of thermal energy in electrons
1791
    :param logepsb: log10 fraction of thermal energy in magnetic field
1792
    :param ksin: fraction of electrons that get accelerated
1793
    :param g0: initial lorentz factor
1794
    :param kwargs: Additional keyword arguments
1795
    :param spread: whether jet can spread, defaults to False
1796
    :param latres: latitudinal resolution for structured jets, defaults to 2
1797
    :param tres: time resolution of shock evolution, defaults to 100
1798
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
1799
        Change to 1 for including inverse compton emission.
1800
    :param l0, ts, q: energy injection parameters, defaults to 0
1801
    :param output_format: Whether to output flux density or AB mag
1802
    :param frequency: frequency in Hz for the flux density calculation
1803
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1804
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1805
        For a proper calculation of the magntitude use the sed variant models.
1806
    """
1807
    output = cocoon(time=time, redshift=redshift, umax=umax, umin=umin, loge0=loge0,
1✔
1808
                    k=k, mej=mej, logn0=logn0,p=p,logepse=logepse,logepsb=logepsb,
1809
                    ksin=ksin, g0=g0, **kwargs)
1810
    return output
1✔
1811

1812
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1813
def cone_afterglow(time, redshift, thv, loge0, thw, thc, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1814
    """
1815
    A cone afterglow model from afterglowpy
1816

1817
    :param time: time in days in observer frame
1818
    :param redshift: source redshift
1819
    :param thv: viewing angle in radians
1820
    :param loge0: log10 on axis isotropic equivalent energy
1821
    :param thw: wing truncation angle of jet thw = thw*thc
1822
    :param thc: half width of jet core in radians
1823
    :param logn0: log10 number density of ISM in cm^-3
1824
    :param p: electron distribution power law index. Must be greater than 2.
1825
    :param logepse: log10 fraction of thermal energy in electrons
1826
    :param logepsb: log10 fraction of thermal energy in magnetic field
1827
    :param ksin: fraction of electrons that get accelerated
1828
    :param g0: initial lorentz factor
1829
    :param kwargs: Additional keyword arguments
1830
    :param spread: whether jet can spread, defaults to False
1831
    :param latres: latitudinal resolution for structured jets, defaults to 2
1832
    :param tres: time resolution of shock evolution, defaults to 100
1833
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
1834
        Change to 1 for including inverse compton emission.
1835
    :param l0, ts, q: energy injection parameters, defaults to 0
1836
    :param output_format: Whether to output flux density or AB mag
1837
    :param frequency: frequency in Hz for the flux density calculation
1838
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1839
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1840
        For a proper calculation of the magntitude use the sed variant models.
1841
    """
1842
    time = time * day_to_s
1✔
1843
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1844
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1845
    spread = kwargs.get('spread', False)
1✔
1846
    latres = kwargs.get('latres', 2)
1✔
1847
    tres = kwargs.get('tres', 100)
1✔
1848
    spectype = kwargs.get('spectype', 0)
1✔
1849
    l0 = kwargs.get('L0', 0)
1✔
1850
    q = kwargs.get('q', 0)
1✔
1851
    ts = kwargs.get('ts', 0)
1✔
1852
    jettype = jettype_dict['cone']
1✔
1853
    frequency = kwargs['frequency']
1✔
1854
    thw = thw * thc
1✔
1855
    e0 = 10 ** loge0
1✔
1856
    n0 = 10 ** logn0
1✔
1857
    epse = 10 ** logepse
1✔
1858
    epsb = 10 ** logepsb
1✔
1859
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
1860
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
1861
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
1862
         'spread': spread, 'latRes': latres, 'tRes': tres, 'thetaWing': thw}
1863
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
1864
    if kwargs['output_format'] == 'flux_density':
1✔
1865
        return flux_density
1✔
1866
    elif kwargs['output_format'] == 'magnitude':
1✔
1867
        return calc_ABmag_from_flux_density(flux_density).value
1✔
1868

1869
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1870
def gaussiancore(time, redshift, thv, loge0, thc, thw, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1871
    """
1872
    A gaussiancore model from afterglowpy
1873

1874
    :param time: time in days in observer frame
1875
    :param redshift: source redshift
1876
    :param thv: viewing angle in radians
1877
    :param loge0: log10 on axis isotropic equivalent energy
1878
    :param thw: wing truncation angle of jet thw = thw*thc
1879
    :param thc: half width of jet core in radians
1880
    :param logn0: log10 number density of ISM in cm^-3
1881
    :param p: electron distribution power law index. Must be greater than 2.
1882
    :param logepse: log10 fraction of thermal energy in electrons
1883
    :param logepsb: log10 fraction of thermal energy in magnetic field
1884
    :param ksin: fraction of electrons that get accelerated
1885
    :param g0: initial lorentz factor
1886
    :param kwargs: Additional keyword arguments
1887
    :param spread: whether jet can spread, defaults to False
1888
    :param latres: latitudinal resolution for structured jets, defaults to 2
1889
    :param tres: time resolution of shock evolution, defaults to 100
1890
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
1891
        Change to 1 for including inverse compton emission.
1892
    :param l0, ts, q: energy injection parameters, defaults to 0
1893
    :param output_format: Whether to output flux density or AB mag
1894
    :param frequency: frequency in Hz for the flux density calculation
1895
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1896
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1897
        For a proper calculation of the magntitude use the sed variant models.
1898
    """
1899
    time = time * day_to_s
1✔
1900
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1901
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1902
    spread = kwargs.get('spread', False)
1✔
1903
    latres = kwargs.get('latres', 2)
1✔
1904
    tres = kwargs.get('tres', 100)
1✔
1905
    spectype = kwargs.get('spectype', 0)
1✔
1906
    l0 = kwargs.get('L0', 0)
1✔
1907
    q = kwargs.get('q', 0)
1✔
1908
    ts = kwargs.get('ts', 0)
1✔
1909
    jettype = jettype_dict['gaussian_w_core']
1✔
1910
    frequency = kwargs['frequency']
1✔
1911

1912
    thw = thw * thc
1✔
1913
    e0 = 10 ** loge0
1✔
1914
    n0 = 10 ** logn0
1✔
1915
    epse = 10 ** logepse
1✔
1916
    epsb = 10 ** logepsb
1✔
1917
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
1918
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
1919
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
1920
         'spread': spread, 'latRes': latres, 'tRes': tres, 'thetaWing': thw}
1921
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
1922
    if kwargs['output_format'] == 'flux_density':
1✔
1923
        return flux_density
1✔
1924
    elif kwargs['output_format'] == 'magnitude':
1✔
1925
        return calc_ABmag_from_flux_density(flux_density).value
1✔
1926

1927
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1928
def gaussian(time, redshift, thv, loge0, thw, thc, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1929
    """
1930
    A gaussian structured jet model from afterglowpy
1931

1932
    :param time: time in days in observer frame
1933
    :param redshift: source redshift
1934
    :param thv: viewing angle in radians
1935
    :param loge0: log10 on axis isotropic equivalent energy
1936
    :param thw: wing truncation angle of jet thw = thw*thc
1937
    :param thc: half width of jet core in radians
1938
    :param logn0: log10 number density of ISM in cm^-3
1939
    :param p: electron distribution power law index. Must be greater than 2.
1940
    :param logepse: log10 fraction of thermal energy in electrons
1941
    :param logepsb: log10 fraction of thermal energy in magnetic field
1942
    :param ksin: fraction of electrons that get accelerated
1943
    :param g0: initial lorentz factor
1944
    :param kwargs: Additional keyword arguments
1945
    :param spread: whether jet can spread, defaults to False
1946
    :param latres: latitudinal resolution for structured jets, defaults to 2
1947
    :param tres: time resolution of shock evolution, defaults to 100
1948
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
1949
        Change to 1 for including inverse compton emission.
1950
    :param l0, ts, q: energy injection parameters, defaults to 0
1951
    :param output_format: Whether to output flux density or AB mag
1952
    :param frequency: frequency in Hz for the flux density calculation
1953
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
1954
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
1955
        For a proper calculation of the magntitude use the sed variant models.
1956
    """
1957
    time = time * day_to_s
1✔
1958
    cosmology = kwargs.get('cosmology', cosmo)
1✔
1959
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
1960
    spread = kwargs.get('spread', False)
1✔
1961
    latres = kwargs.get('latres', 2)
1✔
1962
    tres = kwargs.get('tres', 100)
1✔
1963
    spectype = kwargs.get('spectype', 0)
1✔
1964
    l0 = kwargs.get('L0', 0)
1✔
1965
    q = kwargs.get('q', 0)
1✔
1966
    ts = kwargs.get('ts', 0)
1✔
1967
    jettype = jettype_dict['gaussian']
1✔
1968
    frequency = kwargs['frequency']
1✔
1969
    thw = thw * thc
1✔
1970
    e0 = 10 ** loge0
1✔
1971
    n0 = 10 ** logn0
1✔
1972
    epse = 10 ** logepse
1✔
1973
    epsb = 10 ** logepsb
1✔
1974
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
1975
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
1976
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
1977
         'spread': spread, 'latRes': latres, 'tRes': tres, 'thetaWing': thw}
1978
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
1979
    if kwargs['output_format'] == 'flux_density':
1✔
1980
        return flux_density
1✔
1981
    elif kwargs['output_format'] == 'magnitude':
1✔
1982
        return calc_ABmag_from_flux_density(flux_density).value
1✔
1983

1984
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
1985
def smoothpowerlaw(time, redshift, thv, loge0, thw, thc, beta, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
1986
    """
1987
    A smoothpowerlaw structured jet model from afterglowpy
1988

1989
    :param time: time in days in observer frame
1990
    :param redshift: source redshift
1991
    :param thv: viewing angle in radians
1992
    :param loge0: log10 on axis isotropic equivalent energy
1993
    :param thw: wing truncation angle of jet thw = thw*thc
1994
    :param thc: half width of jet core in radians
1995
    :param beta: index for power-law structure, theta^-b
1996
    :param logn0: log10 number density of ISM in cm^-3
1997
    :param p: electron distribution power law index. Must be greater than 2.
1998
    :param logepse: log10 fraction of thermal energy in electrons
1999
    :param logepsb: log10 fraction of thermal energy in magnetic field
2000
    :param ksin: fraction of electrons that get accelerated
2001
    :param g0: initial lorentz factor
2002
    :param kwargs: Additional keyword arguments
2003
    :param spread: whether jet can spread, defaults to False
2004
    :param latres: latitudinal resolution for structured jets, defaults to 2
2005
    :param tres: time resolution of shock evolution, defaults to 100
2006
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2007
        Change to 1 for including inverse compton emission.
2008
    :param l0, ts, q: energy injection parameters, defaults to 0
2009
    :param output_format: Whether to output flux density or AB mag
2010
    :param frequency: frequency in Hz for the flux density calculation
2011
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2012
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2013
        For a proper calculation of the magntitude use the sed variant models.
2014
    """
2015
    time = time * day_to_s
1✔
2016
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2017
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2018
    spread = kwargs.get('spread', False)
1✔
2019
    latres = kwargs.get('latres', 2)
1✔
2020
    tres = kwargs.get('tres', 100)
1✔
2021
    spectype = kwargs.get('spectype', 0)
1✔
2022
    l0 = kwargs.get('L0', 0)
1✔
2023
    q = kwargs.get('q', 0)
1✔
2024
    ts = kwargs.get('ts', 0)
1✔
2025
    jettype = jettype_dict['smooth_power_law']
1✔
2026
    frequency = kwargs['frequency']
1✔
2027
    thw = thw * thc
1✔
2028
    e0 = 10 ** loge0
1✔
2029
    n0 = 10 ** logn0
1✔
2030
    epse = 10 ** logepse
1✔
2031
    epsb = 10 ** logepsb
1✔
2032
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
2033
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
2034
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
2035
         'spread': spread, 'latRes': latres, 'tRes': tres, 'thetaWing': thw, 'b': beta}
2036
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
2037
    if kwargs['output_format'] == 'flux_density':
1✔
2038
        return flux_density
1✔
2039
    elif kwargs['output_format'] == 'magnitude':
1✔
2040
        return calc_ABmag_from_flux_density(flux_density).value
1✔
2041

2042
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
2043
def powerlawcore(time, redshift, thv, loge0, thw, thc, beta, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
2044
    """
2045
    A power law with core structured jet model from afterglowpy
2046

2047
    :param time: time in days in observer frame
2048
    :param redshift: source redshift
2049
    :param thv: viewing angle in radians
2050
    :param loge0: log10 on axis isotropic equivalent energy
2051
    :param thw: wing truncation angle of jet thw = thw*thc
2052
    :param thc: half width of jet core in radians
2053
    :param beta: index for power-law structure, theta^-b
2054
    :param logn0: log10 number density of ISM in cm^-3
2055
    :param p: electron distribution power law index. Must be greater than 2.
2056
    :param logepse: log10 fraction of thermal energy in electrons
2057
    :param logepsb: log10 fraction of thermal energy in magnetic field
2058
    :param ksin: fraction of electrons that get accelerated
2059
    :param g0: initial lorentz factor
2060
    :param kwargs: Additional keyword arguments
2061
    :param spread: whether jet can spread, defaults to False
2062
    :param latres: latitudinal resolution for structured jets, defaults to 2
2063
    :param tres: time resolution of shock evolution, defaults to 100
2064
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2065
        Change to 1 for including inverse compton emission.
2066
    :param l0, ts, q: energy injection parameters, defaults to 0
2067
    :param output_format: Whether to output flux density or AB mag
2068
    :param frequency: frequency in Hz for the flux density calculation
2069
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2070
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2071
        For a proper calculation of the magntitude use the sed variant models.
2072
    """
2073
    time = time * day_to_s
1✔
2074
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2075
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2076
    spread = kwargs.get('spread', False)
1✔
2077
    latres = kwargs.get('latres', 2)
1✔
2078
    tres = kwargs.get('tres', 100)
1✔
2079
    spectype = kwargs.get('spectype', 0)
1✔
2080
    l0 = kwargs.get('L0', 0)
1✔
2081
    q = kwargs.get('q', 0)
1✔
2082
    ts = kwargs.get('ts', 0)
1✔
2083
    jettype = jettype_dict['powerlaw_w_core']
1✔
2084
    frequency = kwargs['frequency']
1✔
2085
    thw = thw * thc
1✔
2086
    e0 = 10 ** loge0
1✔
2087
    n0 = 10 ** logn0
1✔
2088
    epse = 10 ** logepse
1✔
2089
    epsb = 10 ** logepsb
1✔
2090

2091
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
2092
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
2093
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
2094
         'spread': spread, 'latRes': latres, 'tRes': tres, 'thetaWing': thw, 'b': beta}
2095
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
2096
    if kwargs['output_format'] == 'flux_density':
1✔
2097
        return flux_density
1✔
2098
    elif kwargs['output_format'] == 'magnitude':
1✔
2099
        return calc_ABmag_from_flux_density(flux_density).value
1✔
2100

2101
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
2102
def tophat(time, redshift, thv, loge0, thc, logn0, p, logepse, logepsb, ksin, g0, **kwargs):
1✔
2103
    """
2104
    A tophat jet model from afterglowpy
2105

2106
    :param time: time in days in observer frame
2107
    :param redshift: source redshift
2108
    :param thv: viewing angle in radians
2109
    :param loge0: log10 on axis isotropic equivalent energy
2110
    :param thc: half width of jet core/jet opening angle in radians
2111
    :param logn0: log10 number density of ISM in cm^-3
2112
    :param p: electron distribution power law index. Must be greater than 2.
2113
    :param logepse: log10 fraction of thermal energy in electrons
2114
    :param logepsb: log10 fraction of thermal energy in magnetic field
2115
    :param ksin: fraction of electrons that get accelerated
2116
    :param g0: initial lorentz factor
2117
    :param kwargs: Additional keyword arguments
2118
    :param spread: whether jet can spread, defaults to False
2119
    :param latres: latitudinal resolution for structured jets, defaults to 2
2120
    :param tres: time resolution of shock evolution, defaults to 100
2121
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2122
        Change to 1 for including inverse compton emission.
2123
    :param l0, ts, q: energy injection parameters, defaults to 0
2124
    :param output_format: Whether to output flux density or AB mag
2125
    :param frequency: frequency in Hz for the flux density calculation
2126
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2127
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2128
        For a proper calculation of the magntitude use the sed variant models. assuming a monochromatic
2129
    """
2130
    time = time * day_to_s
1✔
2131
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2132
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2133
    spread = kwargs.get('spread', False)
1✔
2134
    latres = kwargs.get('latres', 2)
1✔
2135
    tres = kwargs.get('tres', 100)
1✔
2136
    spectype = kwargs.get('spectype', 0)
1✔
2137
    l0 = kwargs.get('L0', 0)
1✔
2138
    q = kwargs.get('q', 0)
1✔
2139
    ts = kwargs.get('ts', 0)
1✔
2140
    jettype = jettype_dict['tophat']
1✔
2141
    frequency = kwargs['frequency']
1✔
2142
    e0 = 10 ** loge0
1✔
2143
    n0 = 10 ** logn0
1✔
2144
    epse = 10 ** logepse
1✔
2145
    epsb = 10 ** logepsb
1✔
2146
    Z = {'jetType': jettype, 'specType': spectype, 'thetaObs': thv, 'E0': e0,
1✔
2147
         'thetaCore': thc, 'n0': n0, 'p': p, 'epsilon_e': epse, 'epsilon_B': epsb,
2148
         'xi_N': ksin, 'd_L': dl, 'z': redshift, 'L0': l0, 'q': q, 'ts': ts, 'g0': g0,
2149
         'spread': spread, 'latRes': latres, 'tRes': tres}
2150
    flux_density = afterglow.fluxDensity(time, frequency, **Z)
1✔
2151
    if kwargs['output_format'] == 'flux_density':
1✔
2152
        return flux_density
1✔
2153
    elif kwargs['output_format'] == 'magnitude':
1✔
2154
        return calc_ABmag_from_flux_density(flux_density).value
1✔
2155

2156

2157
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2025MNRAS.539.3319W/abstract')
1✔
2158
def tophat_from_emulator(time, redshift, thv, loge0, thc, logn0, p, logepse, logepsb, g0, **kwargs):
1✔
2159
    """
2160
    Evaluate a tophat afterglow model using an mpl regressor. Note that this model predicts for a fixed redshift = 0.01 and fixed ksin = 1.
2161
    This tophat model does not include all of the ususal kwargs
2162

2163
    :param time: time in days in observer frame, should be in range 0.1 to 300
2164
    :param redshift: source redshift
2165
    :param thv: viewing angle in radians
2166
    :param loge0: log10 on axis isotropic equivalent energy
2167
    :param thc: half width of jet core/jet opening angle in radians
2168
    :param logn0: log10 number density of ISM in cm^-3
2169
    :param p: electron distribution power law index. Must be greater than 2.
2170
    :param logepse: log10 fraction of thermal energy in electrons
2171
    :param logepsb: log10 fraction of thermal energy in magnetic field
2172
    :param g0: initial lorentz factor
2173
    :param kwargs: Additional keyword arguments
2174
    :param frequency: frequency of the band to view in- single number or same length as time array
2175
    :param output_format: Whether to output flux density or AB mag, specified by 'flux_density' or 'magnitude'
2176
    :return: flux density or AB mag predicted by emulator. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2177
        For a proper calculation of the magntitude use the sed variant models
2178
    """
2179

2180
    from redback_surrogates.afterglowmodels import tophat_emulator
1✔
2181
    
2182
    z1=0.01
1✔
2183
    z2= redshift
1✔
2184
    frequency= np.log10(kwargs['frequency'])    
1✔
2185
    flux_density = tophat_emulator(new_time=time/(1+z2), thv=thv, loge0=loge0, thc=thc, logn0=logn0, p=p,
1✔
2186
                                            logepse=logepse, logepsb=logepsb, g0=g0,frequency=frequency)
2187
        
2188
    #scaling flux density with redshift
2189
    dl1 = cosmo.luminosity_distance(z1)
1✔
2190
    dl2 = cosmo.luminosity_distance(z2)
1✔
2191
    scale_factor = ((dl1**2)*(1+z1)) / (dl2**2)
1✔
2192
    flux_density=flux_density*scale_factor
1✔
2193
    
2194
    if kwargs['output_format'] == 'flux_density':
1✔
2195
        return flux_density
1✔
2196
    elif kwargs['output_format'] == 'magnitude':
1✔
2197
        return calc_ABmag_from_flux_density(flux_density).value
1✔
2198

2199
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
2200
def afterglow_models_with_energy_injection(time, **kwargs):
1✔
2201
    """
2202
    A base class for afterglowpy models with energy injection.
2203

2204
    :param time: time in days in observer frame
2205
    :param kwargs: Additional keyword arguments
2206
    :param spread: whether jet can spread, defaults to False
2207
    :param latres: latitudinal resolution for structured jets, defaults to 2
2208
    :param tres: time resolution of shock evolution, defaults to 100
2209
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2210
        Change to 1 for including inverse compton emission.
2211
    :param l0, ts, q: energy injection parameters, defaults to 0
2212
    :param output_format: Whether to output flux density or AB mag
2213
    :param frequency: frequency in Hz for the flux density calculation
2214
    :param base_model: A string to indicate the type of jet model to use.
2215
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2216
        For a proper calculation of the magntitude use the sed variant models.
2217
    """
2218
    from redback.model_library import modules_dict  # import model library in function to avoid circular dependency
×
2219
    base_model = kwargs['base_model']
×
2220
    if isfunction(base_model):
×
2221
        function = base_model
×
2222
    elif base_model not in jet_spreading_models:
×
2223
        logger.warning('{} is not implemented as a base model'.format(base_model))
×
2224
        raise ValueError('Please choose a different base model')
×
2225
    elif isinstance(base_model, str):
×
2226
        function = modules_dict['afterglow_models'][base_model]
×
2227
    else:
2228
        raise ValueError("Not a valid base model.")
×
2229
    kwargs['ts'] = kwargs['ts'] * day_to_s
×
2230
    kwargs['spread'] = True
×
2231
    output = function(time, **kwargs)
×
2232
    return output
×
2233

2234
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
2235
def afterglow_models_with_jet_spread(time, **kwargs):
1✔
2236
    """
2237
    A base class for afterglow models with jet spreading. Note, with these models you cannot sample in g0.
2238

2239
    :param time: time in days in observer frame
2240
    :param kwargs: Additional keyword arguments
2241
    :param latres: latitudinal resolution for structured jets, defaults to 2
2242
    :param tres: time resolution of shock evolution, defaults to 100
2243
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2244
        Change to 1 for including inverse compton emission.
2245
    :param l0, ts, q: energy injection parameters, defaults to 0
2246
    :param output_format: Whether to output flux density or AB mag
2247
    :param frequency: frequency in Hz for the flux density calculation
2248
    :param base_model: A string to indicate the type of jet model to use.
2249
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2250
        For a proper calculation of the magntitude use the sed variant models.
2251
    """
2252
    from redback.model_library import modules_dict  # import model library in function to avoid circular dependency
×
2253
    base_model = kwargs['base_model']
×
2254
    if isfunction(base_model):
×
2255
        function = base_model
×
2256
    elif base_model not in jet_spreading_models:
×
2257
        logger.warning('{} is not implemented as a base model'.format(base_model))
×
2258
        raise ValueError('Please choose a different base model')
×
2259
    elif isinstance(base_model, str):
×
2260
        function = modules_dict['afterglow_models'][base_model]
×
2261
    else:
2262
        raise ValueError("Not a valid base model.")
×
2263
    kwargs['spread'] = True
×
2264
    kwargs.pop('g0')
×
2265
    output = function(time, **kwargs)
×
2266
    return output
×
2267

2268
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2020ApJ...896..166R/abstract')
1✔
2269
def afterglow_models_sed(time, **kwargs):
1✔
2270
    """
2271
    A base class for afterglowpy models for bandpass magnitudes/flux/spectra/sncosmo source.
2272

2273
    :param time: time in days in observer frame
2274
    :param kwargs: Additional keyword arguments, must be all parameters required by the base model and the following:
2275
    :param base_model: A string to indicate the type of jet model to use.
2276
    :param spread: whether jet can spread, defaults to False
2277
    :param latres: latitudinal resolution for structured jets, defaults to 2
2278
    :param tres: time resolution of shock evolution, defaults to 100
2279
    :param spectype: whether to have inverse compton, defaults to 0, i.e., no inverse compton.
2280
        Change to 1 for including inverse compton emission.
2281
    :param bands: Required if output_format is 'magnitude' or 'flux'.
2282
    :param output_format: 'magnitude', 'spectra', 'flux', 'sncosmo_source'
2283
    :param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
2284
    :return: set by output format - 'magnitude', 'spectra', 'flux', 'sncosmo_source'
2285
    """
2286
    from redback.model_library import modules_dict  # import model library in function to avoid circular dependency
×
2287
    base_model = kwargs['base_model']
×
2288
    if isfunction(base_model):
×
2289
        function = base_model
×
2290
    elif base_model not in jet_spreading_models:
×
2291
        logger.warning('{} is not implemented as a base model'.format(base_model))
×
2292
        raise ValueError('Please choose a different base model')
×
2293
    elif isinstance(base_model, str):
×
2294
        function = modules_dict['afterglow_models'][base_model]
×
2295
    else:
2296
        raise ValueError("Not a valid base model.")
×
2297
    temp_kwargs = kwargs.copy()
×
2298
    temp_kwargs['spread'] = kwargs.get('spread', False)
×
2299
    lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 200))
×
2300
    frequency = lambda_to_nu(lambda_observer_frame)
×
2301
    time_observer_frame = np.linspace(0, np.max(time), 300)
×
2302
    times_mesh, frequency_mesh = np.meshgrid(time_observer_frame, frequency)
×
2303
    temp_kwargs['frequency'] = frequency_mesh
×
2304
    temp_kwargs['output_format'] = 'flux_density'
×
2305
    output = function(times_mesh, **temp_kwargs).T
×
2306
    fmjy = output * uu.mJy
×
2307
    spectra = fmjy.to(uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
×
2308
                                 equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
2309
    if kwargs['output_format'] == 'spectra':
×
2310
        return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
×
2311
                                                                    lambdas=lambda_observer_frame,
2312
                                                                    spectra=spectra)
2313
    else:
2314
        return get_correct_output_format_from_spectra(time=time, time_eval=time_observer_frame,
×
2315
                                                      spectra=spectra, lambda_array=lambda_observer_frame,
2316
                                                      **kwargs)
2317

2318
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024ApJS..273...17W/abstract')
1✔
2319
def jetsimpy_tophat(time, redshift, thv, loge0, thc, nism, A, p, logepse, logepsb, g0, **kwargs):
1✔
2320
    """
2321
    A tophat jet model from jetsimpy
2322
    :param time: time in days in observer frame
2323
    :param redshift: source redshift
2324
    :param thv: viewing angle in radians
2325
    :param loge0: log10 on axis isotropic equivalent energy
2326
    :param thc: half width of jet core/jet opening angle in radians
2327
    :param nism: number density of ISM in cm^-3 (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2328
    :param A: wind density scale (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2329
    :param p: electron distribution power law index.
2330
    :param logepse: log10 fraction of thermal energy in electrons
2331
    :param logepsb: log10 fraction of thermal energy in magnetic field
2332
    :param g0: initial lorentz factor
2333
    :param kwargs: Additional keyword arguments
2334
    :param output_format: Whether to output flux density or AB mag
2335
    :param frequency: frequency in Hz for the flux density calculation
2336
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2337
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2338
    """
2339
    import jetsimpy #Can not use models unless jetsimpy is downloaded
1✔
2340
    time = time * day_to_s
1✔
2341
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2342
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2343
    P = dict(Eiso = 10 ** loge0, lf = g0, theta_c = thc, n0 = nism, A = A, eps_e = 10 ** logepse, eps_b = 10 ** logepsb, p = p, theta_v = thv, d = dl*3.24078e-25, z = redshift) #make a param dict
1✔
2344
    if kwargs['output_format'] == 'flux_density':
1✔
2345
        frequency = kwargs['frequency']
1✔
2346
        flux_density = jetsimpy.FluxDensity_tophat(time, frequency, P)
1✔
2347
        return flux_density   
1✔
2348
    else:
2349
        frequency = bands_to_frequency(kwargs['bands'])       
1✔
2350
        flux_density = jetsimpy.FluxDensity_tophat(time, frequency, P)
1✔
2351
        return calc_ABmag_from_flux_density(flux_density).value
1✔
2352

2353
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024ApJS..273...17W/abstract')
1✔
2354
def jetsimpy_gaussian(time, redshift, thv, loge0, thc, nism, A, p, logepse, logepsb, g0, **kwargs):
1✔
2355
    """
2356
    A gaussian jet model from jetsimpy
2357
    :param time: time in days in observer frame
2358
    :param redshift: source redshift
2359
    :param thv: viewing angle in radians
2360
    :param loge0: log10 on axis isotropic equivalent energy
2361
    :param thc: half width of jet core/jet opening angle in radians
2362
    :param nism: number density of ISM in cm^-3 (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2363
    :param A: wind density scale (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2364
    :param p: electron distribution power law index.
2365
    :param logepse: log10 fraction of thermal energy in electrons
2366
    :param logepsb: log10 fraction of thermal energy in magnetic field
2367
    :param g0: initial lorentz factor
2368
    :param kwargs: Additional keyword arguments
2369
    :param output_format: Whether to output flux density or AB mag
2370
    :param frequency: frequency in Hz for the flux density calculation
2371
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2372
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2373
    """
2374
    import jetsimpy #Can not use models unless jetsimpy is downloaded
1✔
2375
    time = time * day_to_s
1✔
2376
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2377
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2378
    P = dict(Eiso = 10 ** loge0, lf = g0, theta_c = thc, n0 = nism, A = A, eps_e = 10 ** logepse, eps_b = 10 ** logepsb, p = p, theta_v = thv, d = dl*3.24078e-25, z = redshift) #make a param dict
1✔
2379
    if kwargs['output_format'] == 'flux_density':
1✔
2380
        frequency = kwargs['frequency']
1✔
2381
        flux_density = jetsimpy.FluxDensity_gaussian(time, frequency, P)
1✔
2382
        return flux_density   
1✔
2383
    else:
2384
        frequency = bands_to_frequency(kwargs['bands'])       
×
2385
        flux_density = jetsimpy.FluxDensity_gaussian(time, frequency, P)
×
2386
        return calc_ABmag_from_flux_density(flux_density).value
×
2387

2388
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024ApJS..273...17W/abstract')
1✔
2389
def jetsimpy_powerlaw(time, redshift, thv, loge0, thc, nism, A, p, logepse, logepsb, g0, s, **kwargs):
1✔
2390
    """
2391
    A power-law jet model from jetsimpy
2392
    :param time: time in days in observer frame
2393
    :param redshift: source redshift
2394
    :param thv: viewing angle in radians
2395
    :param loge0: log10 on axis isotropic equivalent energy
2396
    :param thc: half width of jet core/jet opening angle in radians
2397
    :param nism: number density of ISM in cm^-3 (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2398
    :param A: wind density scale (ntot = A * (r / 1e17)^-2 + nism (cm^-3))
2399
    :param p: electron distribution power law index.
2400
    :param logepse: log10 fraction of thermal energy in electrons
2401
    :param logepsb: log10 fraction of thermal energy in magnetic field
2402
    :param g0: initial lorentz factor
2403
    :param s: power-law jet slope
2404
    :param kwargs: Additional keyword arguments
2405
    :param output_format: Whether to output flux density or AB mag
2406
    :param frequency: frequency in Hz for the flux density calculation
2407
    :param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
2408
    :return: flux density or AB mag. Note this is going to give the monochromatic magnitude at the effective frequency for the band.
2409
    """
2410
    import jetsimpy #Can not use models unless jetsimpy is downloaded
1✔
2411
    time = time * day_to_s
1✔
2412
    cosmology = kwargs.get('cosmology', cosmo)
1✔
2413
    dl = cosmology.luminosity_distance(redshift).cgs.value
1✔
2414
    P = dict(Eiso = 10 ** loge0, lf = g0, theta_c = thc, n0 = nism, A = A, eps_e = 10 ** logepse, eps_b = 10 ** logepsb, p = p, theta_v = thv, d = dl*3.24078e-25, z = redshift, s = s) #make a param dict
1✔
2415
    if kwargs['output_format'] == 'flux_density':
1✔
2416
        frequency = kwargs['frequency']
1✔
2417
        flux_density = jetsimpy.FluxDensity_powerlaw(time, frequency, P)
1✔
2418
        return flux_density   
1✔
2419
    else:
2420
        frequency = bands_to_frequency(kwargs['bands'])       
×
2421
        flux_density = jetsimpy.FluxDensity_powerlaw(time, frequency, P)
×
2422
        return calc_ABmag_from_flux_density(flux_density).value
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc