Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b6e6393
Add files via upload
jpkneller Nov 5, 2025
6085250
Rename doc/source/nb/Wright_2017.ipynb to doc/source/nb/pisn/Wright_2…
jpkneller Nov 5, 2025
e98787d
Add files via upload
jpkneller Nov 5, 2025
9cdad25
TypeIa SN models
jpkneller Nov 6, 2025
4b97bcd
Add files via upload
jpkneller Nov 6, 2025
e3d6d47
Add files via upload
jpkneller Nov 6, 2025
96254aa
Rename doc/source/nb/TypeIa.ipynb to doc/source/nb/TypeIa/TypeIa.ipynb
jpkneller Nov 6, 2025
0b21198
Merge pull request #416 from SNEWS2/jpkneller-TypeIa
JostMigenda Jun 10, 2026
66b0724
Update ccsn.py
jpkneller Jun 15, 2026
f504a57
Update base.py
jpkneller Jun 15, 2026
97c6715
Update typeIa_loaders.py
jpkneller Jun 15, 2026
06a505a
Update typeIa.py
jpkneller Jun 15, 2026
ae38d88
Update pisn_loaders.py
jpkneller Jun 15, 2026
08a9f53
Update model_files.yml
jpkneller Jun 15, 2026
e6dd5cc
Merge branch 'main' into jpkneller-pisn-models
jpkneller Jun 15, 2026
d5ae064
Update SNOwGLoBES_models.ipynb
jpkneller Jun 15, 2026
411a35b
Update base.py
jpkneller Jun 15, 2026
c6dcbf7
Update base.py
jpkneller Jun 15, 2026
c3da4d7
Update typeIa.py
jpkneller Jun 15, 2026
aefbb76
Update typeIa_loaders.py
jpkneller Jun 15, 2026
6df4791
Update TypeIa.ipynb
jpkneller Jun 15, 2026
40de9b2
Update base.py
jpkneller Jun 15, 2026
1d27eab
Update typeIa_loaders.py
jpkneller Jun 15, 2026
eb2623f
Update pisn_loaders.py
jpkneller Jun 15, 2026
867c829
Update base.py
jpkneller Jun 15, 2026
72c8421
Update model_files.yml
jpkneller Jun 15, 2026
b95458b
Update model_files.yml
jpkneller Jun 15, 2026
47bd360
Create test_pisn_rates.py
jpkneller Jun 15, 2026
c6500ee
Update test_pisn_rates.py
jpkneller Jun 15, 2026
6eec0fc
Update test_pisn_rates.py
jpkneller Jun 15, 2026
e3f6e81
Update test_pisn_rates.py
jpkneller Jun 15, 2026
40472c1
Update test_pisn_rates.py
jpkneller Jun 15, 2026
3f18a04
Update test_pisn_rates.py
jpkneller Jun 15, 2026
ba5743b
Update test_pisn_rates.py
jpkneller Jun 15, 2026
28fb4fa
Update base.py
jpkneller Jun 17, 2026
61dbfbd
Update base.py
jpkneller Jun 17, 2026
35280e1
Update base.py
jpkneller Jun 17, 2026
46e03da
Update base.py
jpkneller Jun 18, 2026
ad74522
Update base.py
jpkneller Jun 18, 2026
7949db1
Update base.py
jpkneller Jun 18, 2026
a628f16
Update base.py
jpkneller Jun 18, 2026
176c47d
Update base.py
jpkneller Jun 18, 2026
472de1f
Update base.py
jpkneller Jun 18, 2026
aef02fa
Update base.py
jpkneller Jun 19, 2026
ac70072
Update base.py
jpkneller Jun 19, 2026
154efe6
Update base.py
jpkneller Jun 19, 2026
856922b
Update base.py
jpkneller Jun 19, 2026
71ae985
Update base.py
jpkneller Jun 19, 2026
e904771
Update model_files.yml
jpkneller Jun 19, 2026
f871788
Update test_pisn_rates.py
jpkneller Jun 19, 2026
8392556
Update test_pisn_rates.py
jpkneller Jun 19, 2026
a547450
Create test_typeIa_rates.py
jpkneller Jun 19, 2026
cb54312
Merge branch 'main' into jpkneller-pisn-models
jpkneller Jul 9, 2026
d38ee56
Update ccsn_loaders.py
jpkneller Aug 10, 2026
f22d332
Update ccsn.py
jpkneller Aug 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions doc/source/nb/TypeIa/TypeIa.ipynb

Large diffs are not rendered by default.

247 changes: 247 additions & 0 deletions doc/source/nb/pisn/Wright_2017.ipynb

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions python/snewpy/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def _wrapper(self, *arg, **kwargs):
init(self, *arg, **kwargs)
check(self)
return _wrapper

class SupernovaModel(ABC, LocalFileLoader):
"""Base class defining an interface to a supernova model."""

Expand Down Expand Up @@ -132,10 +132,10 @@ def get_initial_spectra(self, t, E):
A container with the information about the initial neutrino spectra
"""
spectra_dict = self._get_initial_spectra_dict(t, E, flavors=ThreeFlavor)
initial_spectra = flux.Container['1/(MeV*s)'].from_dict(spectra_dict,
time=t,
energy=E,
flavor_scheme=ThreeFlavor)
initial_spectra = flux.Spectrum.from_dict(spectra_dict,
time=t,
energy=E,
flavor_scheme=ThreeFlavor)
return initial_spectra

def get_transformed_spectra(self, t, E, flavor_xform):
Expand All @@ -155,8 +155,8 @@ def get_transformed_spectra(self, t, E, flavor_xform):
flux.Container
A container with the information of the transformed neutrino spectra
"""
initialspectra = self.get_initial_spectra(t, E)
transformed_spectra = flavor_xform.apply_to(initialspectra)
initial_spectra = self.get_initial_spectra(t, E)
transformed_spectra = flavor_xform.apply_to(initial_spectra)
return transformed_spectra

def get_flux (self, t, E, distance, flavor_xform=NoTransformation()):
Expand Down Expand Up @@ -249,7 +249,7 @@ def _get_initial_spectra_dict(self, t, E, flavors=ThreeFlavor):
#Reshape the Energy array to shape [1,len(E)]
E = np.expand_dims(E, axis=0)

initialspectra = {}
initial_spectra = {}

# Estimate L(t), <E_nu(t)> and alpha(t). Express all energies in erg.
E = E.to_value('erg')
Expand Down Expand Up @@ -283,6 +283,6 @@ def _get_initial_spectra_dict(self, t, E, flavors=ThreeFlavor):

#remove unnecessary dimensions, if E or t was scalar:
result = np.squeeze(result)
initialspectra[flavor] = result
initial_spectra[flavor] = result

return initialspectra
return initial_spectra
11 changes: 11 additions & 0 deletions python/snewpy/models/model_files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config:
- &snewpy "https://github.com/SNEWS2/snewpy/raw/v{snewpy_version}/models/{model}/{filename}"
- &ccsn_repository "https://github.com/SNEWS2/snewpy-models-ccsn/raw/v0.3/models/{model}/{filename}"
- &presn_repository "https://github.com/SNEWS2/snewpy-models-presn/raw/v0.2/models/{model}/{filename}"
- &pisn_typeIa_repository "https://github.com/SNEWS2/snewpy-models-pisn_typeIa/raw/v0.1/models/{model}/{filename}"

models:
ccsn:
Expand Down Expand Up @@ -73,3 +74,13 @@ models:

Yoshida_2016:
repository: *presn_repository

pisn:
Wright_2017:
repository: *pisn_typeIa_repository

typeIa:
TypeIa:
repository: *pisn_typeIa_repository


36 changes: 36 additions & 0 deletions python/snewpy/models/pisn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
"""
The submodule ``snewpy.models.pisn`` contains models of pair-instability supernova neutrino fluxes,
"""
import logging
import os

from astropy import units as u

from snewpy.models import pisn_loaders as loaders
from .base import SupernovaModel

from snewpy.models.registry_model import RegistryModel, Parameter
from snewpy.models.registry_model import all_models

@RegistryModel(
progenitor_mass = [150, 250] * u.Msun,
eos = ['SFHo', 'Helm']
)
class Wright_2017(loaders.Wright_2017):
"""PISN model described in the paper `Neutrino signal from pair-instability supernovae' by Wright et al.,
Phys. Rev. D96 (2017) 103008 <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.96.103008>`_
"""
def _metadata_from_filename(self, filename:str):
metadata = {
'Progenitor mass': float(filename.split('_')[1].strip('Msun')) * u.Msun,
'EOS': filename.split('_')[2].strip('EOS=')
}
return metadata

def __init__(self, progenitor_mass:u.Quantity, eos:str):
filename=f"PISN_{progenitor_mass.to_value('Msun'):.0f}Msun_EOS={eos}_NeutrinoFlux.tar.bz2"
super().__init__(filename, self.metadata)



124 changes: 124 additions & 0 deletions python/snewpy/models/pisn_loaders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
"""
The submodule ``snewpy.models.pisn_loaders`` contains classes to load pair-instability supernova
models from files stored on disk.
"""
import logging
import os
import numpy as np
import pandas as pd
import tarfile

from astropy import units as u
from astropy.table import Table

from scipy import interpolate

from snewpy.models.base import SupernovaModel
from snewpy.flavor import ThreeFlavor
from snewpy.flux import Spectrum
from snewpy import _model_downloader


class Wright_2017(SupernovaModel):
"""PISN model described in the paper `Neutrino signal from pair-instability supernovae' by Wright et al.,
Phys. Rev. D96 (2017) 103008 <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.96.103008>`
"""

def __init__(self, filename, metadata={}):
"""
Parameters
----------
tarfilename: str
Absolute or relative path to tar archive
"""
# Open the requested filename using the model downloader.
datafile = self.request_file(filename)

tf = tarfile.open(datafile)

# Find the "NoOsc" files.
datafiles = sorted([f.name for f in tf if '.dat' in f.name])
nooscfiles = [df for df in datafiles if 'NoOsc' in df]
nooscfiles.sort(key=len)

# Loop through the NoOsc files and pull out the number fluxes.
self.time = []
self.energy = None
self.initial_spectra = {}
self.interpolation = {}

self._flavorkeys = {ThreeFlavor.NU_E: 'NuE',
ThreeFlavor.NU_E_BAR: 'aNuE',
ThreeFlavor.NU_MU: 'NuMu',
ThreeFlavor.NU_MU_BAR: 'aNuMu',
ThreeFlavor.NU_TAU: 'NuTau',
ThreeFlavor.NU_TAU_BAR: 'aNuTau'}

for nooscfile in nooscfiles:
with tf.extractfile(nooscfile) as f:
logging.debug('Reading {}'.format(nooscfile))
meta = f.readline()
metatext = meta.decode('utf-8')
t = float(metatext.split('TBinMid=')[-1].split('sec')[0])
dt = float(metatext.split('tBinWidth=')[-1].split('s')[0])
dE = float(metatext.split('eBinWidth=')[-1].split('MeV')[0])

data = Table.read(f, format='ascii.commented_header', header_start=-1)
data.meta['t'] = t
data.meta['dt'] = dt
data.meta['dE'] = dE

self.time.append(t)

if self.energy is None:
self.energy = (data['E(GeV)'].data*1000).tolist()

for flavor in ThreeFlavor:
key = self._flavorkeys[flavor]
# convert from flux back to initial spectra: number per /s/erg
spectrum = data[key].data.tolist() * (4*np.pi*(10*u.kpc)**2) /dt/dE
if flavor in self.initial_spectra:
self.initial_spectra[flavor].append(spectrum)
else:
self.initial_spectra[flavor] = [spectrum]

for flavor in ThreeFlavor:
self.interpolation[flavor] = interpolate.RegularGridInterpolator((self.time, self.energy), self.initial_spectra[flavor], method='cubic')

self.time *= u.s
self.energy *= u.MeV

self.filename = os.path.basename(filename)

def _get_initial_spectra_dict(self, t, E, flavors=ThreeFlavor):
"""Get neutrino spectra/luminosity curves after oscillation.

Parameters
----------
t : astropy.Quantity
Time to evaluate initial spectra.
E : astropy.Quantity or ndarray of astropy.Quantity
Energies to evaluate the initial spectra.
flavors: iterable of snewpy.neutrino.Flavor
Return spectra for these flavors only (default: all)

Returns
-------
spectra : dict
Dictionary of model spectra, keyed by neutrino flavor.
"""
#convert input arguments to 1D arrays
t = u.Quantity(t, ndmin=1)
E = u.Quantity(E, ndmin=1)

initial_spectra = {}
for flavor in ThreeFlavor:
initial_spectra[flavor] = self.interpolation[flavor]((t, E)) / (u.MeV * u.s)

@JostMigenda JostMigenda Jun 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both t and E are multi-element arrays (e.g. if we do model.get_flux(times, energies, d) as demonstrated in FlavorTransformation.ipynb), this will fail.

The fix could be something like:

        t = u.Quantity(t, ndmin=1).to(u.s).value
        E = u.Quantity(E, ndmin=1).to(u.MeV).value
        tE_grid = np.stack(np.meshgrid(t, E, indexing='ij'), axis=-1)

        initial_spectra = {}
        for flavor in ThreeFlavor:
            initial_spectra[flavor] = self.interpolation[flavor](tE_grid) / (u.MeV * u.s)

(Note that we also need to remove the units in between, otherwise np.stack will complain that we’re mixing units.)

I’ve played around with a toy example to figure out the correct order of indices (self.interpolation[flavor](tE_grid)[i, j] == self.interpolation[flavor]((t[i], E[j]))), but since this is so easy to mix up, I would appreciate if someone could double-check that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same applies to the TypeIa class.


return initial_spectra





38 changes: 38 additions & 0 deletions python/snewpy/models/typeIa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
The submodule ``snewpy.models.typeIa`` contains models of neutrino fluxes from Type Ia supernovae
"""
import logging
import os

from astropy import units as u

from snewpy.models import typeIa_loaders as loaders
from .base import SupernovaModel

from snewpy.models.registry_model import RegistryModel, Parameter
from snewpy.models.registry_model import all_models

@RegistryModel(
mechanism = ['GCD', 'DDT'],
)
class TypeIa(loaders.TypeIa):
"""The DDT model is described in 'Neutrinos from type Ia supernovae: The deflagration-to-detonation transition scenario', by Warren P. Wright et al.,
[Phys. Rev. D94 (2016) 025026](https://journals.aps.org/prd/abstract/10.1103/PhysRevD.94.025026), [arXiv:1605.01408](https://arxiv.org/abs/1605.01408).
There are 30 snapshots in time and the format of each data file is the SNOwGLoBES format.

The GCD model is described in 'Neutrinos from type Ia supernovae: The gravitationally confined detonation scenario' by Warren P. Wright et al.,
[Phys. Rev. D95 (2017) 043006](https://journals.aps.org/prd/abstract/10.1103/PhysRevD.95.043006), [arXiv:1609.07403](https://arxiv.org/abs/1609.07403).
There are 64 snapshots in time and the format of each data file is the SNOwGLoBES format.
"""
def _metadata_from_filename(self, filename:str):
metadata = {
'mechanism': filename.split('_')[0]
}
return metadata

def __init__(self, mechanism:str):
filename = f"{mechanism}_NeutrinoFlux.tar.bz2"
super().__init__(filename)


Loading