Creating a Supernova Model with an Analytic Spectrum

This notebook demonstrates how to use the Analytic3Species class from snewpy.models to create an analytic supernova model. The neutrino spectrum of this model follows a Gamma distribution (see arXiv:1211.3920) with user-selected parameters.

In this notebook, we first create a model file, then visualize the spectral parameters and finally use SNOwGLoBES to determine the number of events expected in a detector.

[1]:
import os

from astropy.table import Table
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

from snewpy import snowglobes, model_path
from snewpy.neutrino import Flavor
from snewpy.models.ccsn import Analytic3Species

mpl.rc('font', size=14)

SNOwGLoBES_path = None  # change to SNOwGLoBES directory if using a custom detector configuration

model_folder = f"{model_path}/AnalyticFluence/"
os.makedirs(model_folder, exist_ok=True)
print(f"Using folder `{model_folder}`.")
Using folder `/home/docs/.astropy/cache/snewpy/models/AnalyticFluence/`.

Creating a SN model file modelled after the Livermore model

[2]:
# These numbers _almost_ reproduce the Livermore model included in the SNOwGLoBES repository.
# They are obtained by calculating the total L, <E> and <E^2> from the livermore.dat
# fluence file (which is modelled after a 10kpc supernova).
total_energy = (5.478e+52, 5.485e+52, 4 * 5.55e+52)
mean_energy = (11.5081, 15.4678, 21.0690)
rms_or_pinch = "rms"
rms_energy = (12.8788, 17.8360, 24.3913)

# Make an astropy table with two times, 0s and 1s, with constant neutrino properties
table = Table()
table['TIME'] = np.linspace(0,1,2)
table['L_NU_E'] =  np.linspace(1,1,2)*total_energy[0]
table['L_NU_E_BAR'] = np.linspace(1,1,2)*total_energy[1]
table['L_NU_X'] = np.linspace(1,1,2)*total_energy[2]/4. #Note, L_NU_X is set to 1/4 of the total NU_X energy

table['E_NU_E'] = np.linspace(1,1,2)*mean_energy[0]
table['E_NU_E_BAR'] = np.linspace(1,1,2)*mean_energy[1]
table['E_NU_X'] = np.linspace(1,1,2)*mean_energy[2]

if rms_or_pinch == "rms":
    table['RMS_NU_E'] = np.linspace(1,1,2)*rms_energy[0]
    table['RMS_NU_E_BAR'] = np.linspace(1,1,2)*rms_energy[1]
    table['RMS_NU_X'] = np.linspace(1,1,2)*rms_energy[2]
    table['ALPHA_NU_E'] = (2.0 * table['E_NU_E'] ** 2 - table['RMS_NU_E'] ** 2) / (
        table['RMS_NU_E'] ** 2 - table['E_NU_E'] ** 2)
    table['ALPHA_NU_E_BAR'] = (2.0 * table['E_NU_E_BAR'] ** 2 - table['RMS_NU_E_BAR'] ** 2) / (
        table['RMS_NU_E_BAR'] ** 2 - table['E_NU_E_BAR'] ** 2)
    table['ALPHA_NU_X'] = (2.0 * table['E_NU_X'] ** 2 - table['RMS_NU_X'] ** 2) / (
        table['RMS_NU_X'] ** 2 - table['E_NU_X'] ** 2)
elif rms_or_pinch == "pinch":
    table['ALPHA_NU_E'] = np.linspace(1,1,2)*pinch_values[0]
    table['ALPHA_NU_E_BAR'] = np.linspace(1,1,2)*pinch_values[1]
    table['ALPHA_NU_X'] = np.linspace(1,1,2)*pinch_values[2]
    table['RMS_NU_E'] = np.sqrt((2.0 + table['ALPHA_NU_E'])/(1.0 + table['ALPHA_NU_E'])*table['E_NU_E']**2)
    table['RMS_NU_E_BAR'] =  np.sqrt((2.0 + table['ALPHA_NU_E_BAR'])/(1.0 + table['ALPHA_NU_E_BAR'])*table['E_NU_E_BAR']**2)
    table['RMS_NU_X'] = np.sqrt((2.0 + table['ALPHA_NU_X'])/(1.0 + table['ALPHA_NU_X'])*table['E_NU_X']**2 )
else:
    print("incorrect second moment method: rms or pinch")

filename = "AnalyticFluence_demo.dat"
table.write(model_folder + filename, format='ascii', overwrite=True)

Visualizing the Analytic Model

[3]:
%matplotlib inline
filename = "AnalyticFluence_demo.dat"
model = Analytic3Species(model_folder + filename)
flavors = [Flavor.NU_E,Flavor.NU_E_BAR,Flavor.NU_X]

fig,axes = plt.subplots(1,3,figsize=(16,3))
plt.subplots_adjust(wspace=0.3)
for flavor in flavors:
    axes[0].plot(model.time,model.luminosity[flavor],label=flavor.to_tex())
axes[0].set_ylabel("Luminosity [erg/s]")
axes[0].set_xlabel("time [s]")
axes[0].legend(frameon=False)

for flavor in flavors:
    axes[1].plot(model.time,model.meanE[flavor],label=flavor.to_tex())
axes[1].set_ylabel("Mean Energy [MeV]")
axes[1].set_xlabel("time [s]")
axes[1].legend(frameon=False)

for flavor in flavors:
    axes[2].plot(model.time,model.pinch[flavor],label=flavor.to_tex())
axes[2].set_ylabel("pinch parameter")
axes[2].set_xlabel("time [s]")
axes[2].legend(frameon=False)
[3]:
<matplotlib.legend.Legend at 0x7e88f29a5890>
../_images/nb_AnalyticFluence_5_1.png

Calculating Number of Events

[4]:
#set model type as
modeltype = 'Analytic3Species'
#set model file we just created above
filename = "AnalyticFluence_demo.dat"

#set desired Oscillation scenario, snowglobes default is
#NoTransformation, so that is used here to match the output of
#./supernova.pl livermore water wc100kt30prct
transformation = "NoTransformation"

#set distance in kpc
distance=10

#set desired detector
detector='wc100kt30prct'

#Running the SNEWPY/SNOwGLoBES modules
outfile = "Analytic3Species_demo"

#first generated integrated fluence files for SNOwGLoBES
print("Preparing fluences ...")
tarredoutfile = snowglobes.generate_fluence(model_folder + filename, modeltype, transformation, distance, outfile)

#run the fluence file through SNOwGLoBES
print("Running SNOwGLoBES ...")
snowglobes.simulate(SNOwGLoBES_path, tarredoutfile, detector_input=detector)

print("Collating...")
tables = snowglobes.collate(SNOwGLoBES_path, tarredoutfile, skip_plots=True)

totalcounts = 0
for i in range(1,6):
    print("Number of events of type",tables['Collated_'+outfile+'_'+detector+'_events_smeared_weighted.dat']['header'].split()[i],end=': ')
    counts = sum(tables['Collated_'+outfile+'_'+detector+'_events_smeared_weighted.dat']['data'][i])
    totalcounts += counts
    print(counts)
print("Total number of events: ",totalcounts)
Preparing fluences ...
Running SNOwGLoBES ...
Using snowglobes_data module ...
Collating...
Number of events of type ibd: 26712.124379254576
Number of events of type nue_O16: 41.223616139539715
Number of events of type nuebar_O16: 669.7094367979332
Number of events of type nc: 498.6559983931139
Number of events of type e: 910.6678004611096
Total number of events:  28832.381231046274
[5]:
for smear in ["smeared", "unsmeared"]:
    energy = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][0]*1000.
    nc = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][1]
    escattering = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][2]
    ibd = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][3]
    nueO16 = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][4]
    nuebarO16 = tables['Collated_'+outfile+'_'+detector+'_events_'+smear+'_weighted.dat']['data'][5]
    plt.plot(energy,nc,label="NC")
    plt.plot(energy,escattering,label="escattering")
    plt.plot(energy,ibd,label="ibd")
    plt.plot(energy,nueO16,label="nueO16")
    plt.plot(energy,nuebarO16,label="nuebarO16")
    plt.legend()
    plt.title(smear)
    plt.xlabel("Energy [MeV]")
    plt.ylabel("Count per bin")
    plt.yscale('log')
    plt.show()
../_images/nb_AnalyticFluence_8_0.png
../_images/nb_AnalyticFluence_8_1.png