def test_synch_reference_sed(self): """test agnpy synchrotron SED against the one sampled from Figure 7.4 of Dermer Menon 2009""" sampled_synch_sed_table = np.loadtxt( f"{tests_dir}/sampled_seds/synch_figure_7_4_dermer_menon_2009.txt", delimiter=",", comments="#", ) sampled_synch_nu = sampled_synch_sed_table[:, 0] * u.Hz sampled_synch_sed = sampled_synch_sed_table[:, 1] * u.Unit("erg cm-2 s-1") synch = Synchrotron(PWL_BLOB) # recompute the SED at the same ordinates where the figure was sampled agnpy_synch_sed = synch.sed_flux(sampled_synch_nu) # sed comparison plot make_sed_comparison_plot( sampled_synch_nu, sampled_synch_sed, agnpy_synch_sed, "Synchrotron", "synch_comparison_figure_7_4_dermer_menon_2009", ) # requires that the SED points deviate less than 15% from the figure assert u.allclose( agnpy_synch_sed, sampled_synch_sed, atol=0 * u.Unit("erg cm-2 s-1"), rtol=0.15, )
def test_synch_reference_sed(self, gamma_max, nu_range_max): """test agnpy synchrotron SED against the ones in Figure 7.4 of Dermer Menon 2009""" # reference SED nu_ref, sed_ref = extract_columns_sample_file( f"{data_dir}/reference_seds/dermer_menon_2009/figure_7_4/synchrotron_gamma_max_{gamma_max}.txt", "Hz", "erg cm-2 s-1", ) # agnpy # change the gamma_max in the blob pwl_dict_test["parameters"]["gamma_max"] = float(gamma_max) pwl_blob_test.set_spectrum(spectrum_norm_test, pwl_dict_test, "integral") # recompute the SED at the same ordinates of the reference figures synch = Synchrotron(pwl_blob_test) sed_agnpy = synch.sed_flux(nu_ref) # sed comparison plot nu_range = [1e10, nu_range_max] * u.Hz make_comparison_plot( nu_ref, sed_agnpy, sed_ref, "agnpy", "Figure 7.4, Dermer and Menon (2009)", "Synchrotron, " + r"$\gamma_{max} = $" + gamma_max, f"{figures_dir}/synch_comparison_gamma_max_{gamma_max}_figure_7_4_dermer_menon_2009.png", "sed", y_range=[1e-13, 1e-9], comparison_range=nu_range.to_value("Hz"), ) # requires that the SED points deviate less than 25% from the figure assert check_deviation(nu_ref, sed_agnpy, sed_ref, 0.25, nu_range)
def test_synch_sed_param_bpl(self): """check that the parametrised delta-function approximation for the synchrotron radiation gives exactly the same result of the full formula from which it was derived""" nu = np.logspace(8, 23) * u.Hz synch = Synchrotron(BPL_BLOB) sed_delta_approx = synch.sed_flux_delta_approx(nu) # check that the synchrotron parameterisation work y = BPL_BLOB.B.value * BPL_BLOB.delta_D k_eq = (BPL_BLOB.u_e / BPL_BLOB.U_B).to_value("") sed_param = synch_sed_param_bpl( nu.value, y, k_eq, BPL_BLOB.n_e.p1, BPL_BLOB.n_e.p2, BPL_BLOB.n_e.gamma_b, BPL_BLOB.n_e.gamma_min, BPL_BLOB.n_e.gamma_max, BPL_BLOB.d_L.cgs.value, BPL_BLOB.R_b.cgs.value, BPL_BLOB.z, ) assert np.allclose(sed_param, sed_delta_approx.value, atol=0, rtol=1e-2)
def plot_S_model(): # Debug plotting B = 5e-10 print('huhu') nu_range = np.logspace(np.log10(30e6), 9, 6) age_range = np.linspace(0, 200, 5) from agnpy.emission_regions import Blob from agnpy.synchrotron import Synchrotron blob = Blob(z=0.001, B=10**4 * B * u.gauss, spectrum_dict={ "type": "PowerLaw", "parameters": { "p": 2.3, "gamma_min": 2, "gamma_max": 1e7 } }) synch = Synchrotron(blob) sed = synch.sed_flux(nu_range * u.Hz) sed = sed.value / nu_range results = np.zeros((len(age_range), len(nu_range))) for i, age in enumerate(age_range): with mp.Pool() as p: results[i] = p.starmap(S_model, [[nu, B, 0.65, 1000, age] for nu in nu_range]) print(results[i], nu_range) PL = (nu_range**-0.65) PL /= (PL[0] / sed[0]) print((results[0, 0] / sed[0])) results /= (results[0, 0] / sed[0]) import matplotlib.pyplot as plt plt.close() print(nu_range, sed) plt.plot(nu_range, sed, c='k', label=f'AGNPY for 0 Myr; B = {B}T') plt.plot(nu_range, PL, label=f'PL alpha = 0.65', c='k', ls='dotted') for age, res in zip(age_range, results): plt.plot(nu_range, res, label=f'{age}Myr; B = {B}T') plt.xscale('log') plt.yscale('log') plt.xlabel('frequency [Hz]') plt.ylabel('S') # plt.xlim([np.min(nu_range), np.max(nu_range)]) # plt.ylim([np.min(res), 1.05*np.max(res)]) plt.legend() plt.savefig( __file__.replace('lib_aging.py', '') + 'lib_aging_data/synch_vs_nu.png')
def test_ssa_sed(self): """test this version SSA SED against the one generated with version 0.0.6""" sampled_ssa_sed_table = np.loadtxt( f"{tests_dir}/sampled_seds/ssa_sed_agnpy_v0_0_6.txt", delimiter=",", comments="#", ) sampled_ssa_nu = sampled_ssa_sed_table[:, 0] * u.Hz sampled_ssa_sed = sampled_ssa_sed_table[:, 1] * u.Unit("erg cm-2 s-1") ssa = Synchrotron(PWL_BLOB, ssa=True) agnpy_ssa_sed = ssa.sed_flux(sampled_ssa_nu) assert u.allclose( agnpy_ssa_sed, sampled_ssa_sed, atol=0 * u.Unit("erg cm-2 s-1"), )
def test_ssa_reference_sed( self, file_ref, spectrum_type, spectrum_parameters, figure_title, figure_path, ): """test SSA SED generated by a given electron distribution against the ones generated with jetset version 1.1.2, via jetset_ssa_sed.py script""" # reference SED nu_ref, sed_ref = extract_columns_sample_file(file_ref, "Hz", "erg cm-2 s-1") # same parameters used to produce the jetset SED spectrum_norm = 1e2 * u.Unit("cm-3") spectrum_dict = { "type": spectrum_type, "parameters": spectrum_parameters } blob = Blob( R_b=5e15 * u.cm, z=0.1, delta_D=10, Gamma=10, B=0.1 * u.G, spectrum_norm=spectrum_norm, spectrum_dict=spectrum_dict, ) # recompute the SED at the same ordinates where the figure was sampled ssa = Synchrotron(blob, ssa=True) sed_agnpy = ssa.sed_flux(nu_ref) # sed comparison plot, we will check between 10^(11) and 10^(19) Hz nu_range = [1e11, 1e19] * u.Hz make_comparison_plot( nu_ref, sed_agnpy, sed_ref, "agnpy", "jetset 1.1.2", figure_title, figure_path, "sed", comparison_range=nu_range.to_value("Hz"), ) # requires that the SED points deviate less than 5% from the figure assert check_deviation(nu_ref, sed_agnpy, sed_ref, 0.05, nu_range)
def test_synch_delta_sed(self): """check that in a given frequency range the full synchrotron SED coincides with the delta function approximation""" nu = np.logspace(10, 20) * u.Hz synch = Synchrotron(lp_blob_test) sed_full = synch.sed_flux(nu) sed_delta = synch.sed_flux_delta_approx(nu) # range of comparison nu_range = [1e12, 1e17] * u.Hz make_comparison_plot( nu, sed_delta, sed_full, "delta function approximation", "full integration", "Synchrotron", f"{figures_dir}/synch_comparison_delta_aprproximation.png", "sed", [1e-16, 1e-8], nu_range.to_value("Hz"), ) # requires that the delta approximation SED points deviate less than 10% assert check_deviation(nu, sed_delta, sed_full, 0.1, nu_range)
def test_sed_integration_methods(self): """test different integration methods agains each other simpole trapezoidal rule vs trapezoidal rule in log-log space """ nu = np.logspace(8, 22) * u.Hz synch_trapz = Synchrotron(pwl_blob_test, integrator=np.trapz) synch_trapz_loglog = Synchrotron(pwl_blob_test, integrator=trapz_loglog) sed_synch_trapz = synch_trapz.sed_flux(nu) sed_synch_trapz_loglog = synch_trapz_loglog.sed_flux(nu) make_comparison_plot( nu, sed_synch_trapz_loglog, sed_synch_trapz, "trapezoidal log-log integration", "trapezoidal integration", "Synchrotron", f"{figures_dir}/synch_comparison_integration_methods.png", "sed", ) # requires that the SED points deviate less than 1% assert check_deviation(nu, sed_synch_trapz_loglog, sed_synch_trapz, 0.01)
Gamma = 1.01 delta_D = 1.02 z = 0.01 blob1 = Blob(r0, z, delta_D, Gamma, B0 * 10.0, norm, spectrum_dict, xi=xi) u_ph_synch = blob1.u_ph_synch # energy density of synchr photons # u_dens * V_b is the total energy in the blob, # photons spend an average time of 0.75 * R_b/c in the blob # so the total energy flux is: # total energy in blob / (average time * 4 pi dist^2) energy_flux_predicted = (blob.u_ph_synch * blob1.V_b / (0.75 * blob1.R_b / const.c.cgs) * np.power(blob1.d_L, -2) / (4 * np.pi)).to("erg cm-2 s-1") synch1 = Synchrotron(blob1, ssa=False) synch1_sed = synch1.sed_flux(nu) energy_flux_sim = np.trapz(synch1_sed / (nu * const.h.cgs), nu * const.h.cgs) print( f"predicted energy flux: {energy_flux_predicted:.5e}, simulated energy flux: {energy_flux_sim:.5e}" ) # nice agreement ssc1 = SynchrotronSelfCompton(blob1, synch1) ssc1_sed = ssc1.sed_flux(nu) print("UB/Usynch = ", blob1.U_B / u_ph_synch) print( "SED_synch/SED_SSC=", energy_flux_sim / np.trapz(ssc1_sed /
spectrum_dict = {"type": "BrokenPowerLaw", "parameters": parameters} delta_D = 1.01 Gamma = 1.01 B = 1.0 * u.G r_b = 1.0e15 * u.cm # no beaming blob0 = Blob(r_b, 0.01, delta_D, Gamma, B, spectrum_norm, spectrum_dict, xi=0.01) synch0 = Synchrotron(blob0, ssa=True) synch0_sed = synch0.sed_flux(nu) # beaming delta_D = 20 Gamma = 15 blob1 = Blob(r_b, 0.01, delta_D, Gamma, B, spectrum_norm, spectrum_dict, xi=0.01) synch1 = Synchrotron(blob1, ssa=True)
"type": "PowerLaw", "parameters": { "p": 2.8, "gamma_min": 1e2, "gamma_max": 1e7 }, } R_b = 1e16 * u.cm B = 1 * u.G z = Distance(1e27, unit=u.cm).z delta_D = 10 Gamma = 10 blob = Blob(R_b, z, delta_D, Gamma, B, spectrum_norm, spectrum_dict) print("blob definition:") print(blob) synch = Synchrotron(blob) ssc = SynchrotronSelfCompton(blob, synch) nu_syn = np.logspace(8, 23) * u.Hz nu_ssc = np.logspace(15, 30) * u.Hz # commands to profile syn_sed_command = "synch.sed_flux(nu_syn)" syn_sed_SSA_command = "synch.sed_flux(nu_syn, SSA=True)" ssc_sed_command = "ssc.sed_flux(nu_ssc)" n = 100 print("\nprofiling synchrotron sed computation:") profile(syn_sed_command, "syn_sed") time_syn = timing(syn_sed_command, n) time_syn /= n print(f"time: {time_syn:.2e} s")
import numpy as np import astropy.units as u from agnpy.emission_regions import Blob from agnpy.synchrotron import Synchrotron from agnpy.utils.plot import plot_sed import matplotlib.pyplot as plt # define the emission region and the radiative process blob = Blob() synch = Synchrotron(blob) # compute the SED over an array of frequencies nu = np.logspace(8, 23) * u.Hz sed = synch.sed_flux(nu) # plot it plot_sed(nu, sed, label="Synchrotron") plt.show()
"type": "PowerLaw", "parameters": { "p": 2.8, "gamma_min": 1e2, "gamma_max": 1e7 }, } R_b = 1e16 * u.cm B = 1 * u.G z = Distance(1e27, unit=u.cm).z delta_D = 10 Gamma = 10 blob = Blob(R_b, z, delta_D, Gamma, B, spectrum_norm, spectrum_dict) print("blob definition:") print(blob) synch = Synchrotron(blob) synch_ssa = Synchrotron(blob, ssa=True) ssc = SynchrotronSelfCompton(blob, synch) ssc_ssa = SynchrotronSelfCompton(blob, synch_ssa) nu_syn = np.logspace(8, 23) * u.Hz nu_ssc = np.logspace(15, 30) * u.Hz # commands to profile syn_sed_command = "synch.sed_flux(nu_syn)" syn_sed_ssa_command = "synch_ssa.sed_flux(nu_syn)" ssc_sed_command = "ssc.sed_flux(nu_ssc)" ssc_ssa_sed_command = "ssc_ssa.sed_flux(nu_ssc)" n = 100 print("\nprofiling synchrotron sed computation:") profile(syn_sed_command, "syn_sed")
def test_tau_on_synchrotron_compare_with_delta_approximation(self): """Compare the output of the calculation of absorption of gamma rays in synchrotron radiation and compare it with simplified delta approximation. """ # create a test blob r_b = 1.0e16 * u.cm z = 2.01 delta_D = 10.1 Gamma = 10.05 B0 = 1.1 * u.G gmin = 10 gbreak = 1.0e3 gmax = 1.0e5 spectrum_norm = 0.1 * u.Unit("erg cm-3") parameters = { "p1": 1.5, "p2": 2.5, "gamma_b": gbreak, "gamma_min": gmin, "gamma_max": gmax, } spectrum_dict = {"type": "BrokenPowerLaw", "parameters": parameters} blob = Blob(r_b, z, delta_D, Gamma, B0, spectrum_norm, spectrum_dict) nu_tau = np.logspace(22, 34, 100) * u.Hz # for absorption calculations e_tau = nu_tau.to("eV", equivalencies=u.spectral()) # full calculations absorb = Absorption(blob) tau = absorb.tau(nu_tau) # now simplified calculations using Eq. 37 of Finke 2008 mec2 = (m_e * c**2).to("eV") eps1 = e_tau / mec2 eps1p = eps1 * (1 + z) / blob.delta_D eps_bar = 2 * blob.delta_D**2 / (1 + z)**2 / eps1 nu_bar = (eps_bar * mec2).to("Hz", equivalencies=u.spectral()) synch = Synchrotron(blob, ssa=True) synch_sed_ebar = synch.sed_flux(nu_bar) tau_delta = (synch_sed_ebar * sigma_T * Distance(z=z)**2 * eps1p / (2 * m_e * c**3 * blob.R_b * blob.delta_D**4)) tau_delta = tau_delta.to("") # the delta approximation does not work well with sharp cut-offs of synchrotron SED. # We first find the peak of the synchr SED maxidx = np.where(synch_sed_ebar == np.amax(synch_sed_ebar))[0][0] # and only take energies below the peak, (note that energies are ordered # in reversed order), and take only those that are at least 1% of the peak idxs = synch_sed_ebar[maxidx:] > 1.0e-2 * synch_sed_ebar[maxidx] # if there are not many points something went wrong with the test assert sum(idxs) > 10 # the agreement in the middle range is pretty good, but at the edges # of energy range it spoils very fast, so only rough agreement is checked # (0.6 in the log space) assert np.allclose(np.log(tau)[maxidx:][idxs], np.log(tau_delta)[maxidx:][idxs], atol=0.6)